Penanganan interrupt pada mikrokontroler dan contoh FSM


9

Pertanyaan awal

Saya punya pertanyaan umum tentang penanganan interupsi dalam mikrokontroler. Saya menggunakan MSP430, tapi saya pikir pertanyaannya dapat diperluas ke UC lainnya. Saya ingin tahu apakah praktik yang baik untuk mengaktifkan / menonaktifkan interupsi sering sepanjang kode. Maksud saya, jika saya memiliki bagian kode yang tidak akan sensitif terhadap interupsi (atau lebih buruk lagi, tidak boleh mendengarkan interupsi, karena alasan apa pun), apakah lebih baik untuk:

  • Nonaktifkan interupsi sebelum dan kemudian aktifkan kembali setelah bagian kritis.
  • Letakkan bendera di dalam ISR masing-masing dan (alih-alih menonaktifkan interupsi), atur bendera ke false sebelum bagian kritis dan atur ulang menjadi true, setelahnya. Untuk mencegah kode ISR dieksekusi.
  • Tak satu pun dari keduanya, jadi saran dipersilahkan!

Pembaruan: menyela dan grafik negara

Saya akan memberikan situasi tertentu. Mari kita asumsikan kita ingin mengimplementasikan bagan negara, yang terdiri dari 4 blok:

  1. Transisi / Efek.
  2. Kondisi keluar.
  3. Aktivitas masuk.
  4. Lakukan aktivitas.

Inilah yang diajarkan seorang profesor kepada kita di universitas. Mungkin, bukan cara terbaik untuk melakukannya adalah dengan mengikuti skema ini:

while(true) {

  /* Transitions/Effects */
  //----------------------------------------------------------------------
  next_state = current_state;

  switch (current_state)
  {
    case STATE_A:
      if(EVENT1) {next_state = STATE_C}
      if(d == THRESHOLD) {next_state = STATE_D; a++}
      break;
    case STATE_B:
      // transitions and effects
      break;
    (...)
  }

  /* Exit activity -> only performed if I leave the state for a new one */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(current_state)
    {
      case STATE_A:
        // Exit activity of STATE_A
        break;
      case STATE_B:
        // Exit activity of STATE_B
        break;
        (...)
    }
  }

  /* Entry activity -> only performed the 1st time I enter a state */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(next_state)
    {
      case STATE_A:
        // Entry activity of STATE_A
        break;
      case STATE_B:
        // Entry activity of STATE_B
        break;
      (...)
    }
  }

  current_state = next_state;

  /* Do activity */
  //----------------------------------------------------------------------
  switch (current_state)
  {
    case STATE_A:
      // Do activity of STATE_A
      break;
    case STATE_B:
      // Do activity of STATE_B
      break;
    (...)
  }
}

Mari kita juga berasumsi bahwa dari, katakanlah STATE_A, saya ingin peka terhadap gangguan yang datang dari serangkaian tombol (dengan sistem debouce, dll.). Ketika seseorang menekan salah satu tombol ini, interupsi dibuat dan bendera yang terkait dengan port input disalin ke dalam variabel buttonPressed. Jika debounce diatur ke 200 ms dalam beberapa cara (pengawas waktu, pengatur waktu, penghitung, ...) kami yakin bahwabuttonPressed tidak dapat diperbarui dengan nilai baru sebelum 200 ms. Inilah yang saya tanyakan kepada Anda (dan saya sendiri :) tentu saja)

Apakah saya perlu mengaktifkan interupsi dalam aktivitas DO STATE_Adan menonaktifkan sebelum pergi?

/* Do activity */
//-------------------------------------
switch (current_state)
{
  case STATE_A:
    // Do activity of STATE_A
    Enable_ButtonsInterrupt(); // and clear flags before it
    // Do fancy stuff and ...
    // ... wait until a button is pressed (e.g. LPM3 of the MSP430)
    // Here I have my buttonPressed flag ready!
    Disable_ButtonsInterrupt();
    break;
  case STATE_B:
    // Do activity of STATE_B
    break;
  (...)
}

Dengan cara yang saya yakin bahwa waktu berikutnya saya mengeksekusi blok 1 (transisi / efek) pada iterasi berikutnya saya yakin bahwa kondisi yang diperiksa sepanjang transisi tidak datang dari interupsi berikutnya yang telah menimpa nilai sebelumnya dari buttonPressedyang saya perlu (walaupun tidak mungkin hal ini terjadi karena 250 ms harus berlalu).


3
Sulit untuk membuat rekomendasi tanpa mengetahui lebih banyak tentang situasi Anda. Tetapi kadang-kadang perlu untuk menonaktifkan interupsi dalam sistem embedded. Lebih baik membiarkan interupsi dinonaktifkan hanya untuk periode waktu yang singkat sehingga interupsi tidak terlewatkan. Dimungkinkan untuk menonaktifkan hanya interupsi tertentu daripada semua interupsi. Saya tidak ingat pernah menggunakan teknik flag-inside-ISR seperti yang Anda jelaskan, jadi saya ragu apakah itu solusi terbaik Anda.
kkrambo

Jawaban:


17

Taktik pertama adalah merancang keseluruhan firmware sehingga tidak masalah jika terjadi gangguan setiap saat. Harus mematikan interupsi sehingga kode latar depan dapat mengeksekusi urutan atom harus dilakukan hemat. Sering ada cara arsitektur di sekitarnya.

Namun, mesin itu ada untuk melayani Anda, bukan sebaliknya. Aturan umum praktis hanya untuk menjaga programmer buruk dari menulis kode yang benar-benar buruk. Jauh lebih baik untuk memahami dengan tepat bagaimana mesin bekerja dan kemudian merancang cara yang baik untuk memanfaatkan kemampuan itu untuk melakukan tugas yang diinginkan.

Ingatlah bahwa kecuali Anda benar-benar ketat pada siklus atau lokasi memori (pasti dapat terjadi), jika tidak, Anda ingin mengoptimalkan kejelasan dan pemeliharaan kode. Misalnya, jika Anda memiliki mesin 16 bit yang memperbarui penghitung 32 bit dalam interupsi centang jam, Anda perlu memastikan bahwa ketika kode latar depan membaca penghitung bahwa kedua bagiannya konsisten. Salah satu caranya adalah mematikan interupsi, membaca dua kata, dan kemudian menghidupkan kembali interupsi. Jika interupsi latensi tidak kritis, maka itu dapat diterima.

Dalam kasus di mana Anda harus memiliki latensi interupsi rendah, Anda dapat, misalnya, membaca kata tinggi, membaca kata rendah, membaca kata tinggi lagi dan ulangi jika itu berubah. Ini sedikit memperlambat kode latar depan, tetapi tidak menambahkan latensi interupsi sama sekali. Ada berbagai trik kecil. Lain mungkin untuk menetapkan bendera dalam rutin interupsi yang menunjukkan penghitung harus bertambah, kemudian lakukan itu di loop acara utama dalam kode latar depan. Itu berfungsi dengan baik jika tingkat interupsi counter cukup lambat sehingga loop acara akan melakukan kenaikan sebelum bendera diatur lagi.

Atau, alih-alih bendera gunakan penghitung satu kata. Kode foreground menyimpan variabel terpisah yang berisi penghitung terakhir yang dimutakhirkan sistem. Itu mengurangi tanda penghitung yang tidak ditandatangani tanpa nilai yang disimpan untuk menentukan berapa banyak kutu pada saat itu harus ditangani. Ini memungkinkan kode latar depan ketinggalan hingga 2 N -1 peristiwa pada suatu waktu, di mana N adalah jumlah bit dalam kata asli yang ALU dapat tangani secara atom.

Setiap metode memiliki kelebihan dan kekurangan masing-masing. Tidak ada jawaban yang benar. Sekali lagi, pahami bagaimana mesin bekerja, maka Anda tidak perlu aturan praktis.


7

Jika Anda memerlukan bagian kritis, Anda harus memastikan, bahwa operasi yang menjaga bagian kritis Anda adalah atomik dan tidak dapat diganggu.

Dengan demikian menonaktifkan interupsi, yang paling sering ditangani oleh instruksi prosesor tunggal (dan disebut menggunakan fungsi intrinsik kompiler), adalah salah satu taruhan teraman yang dapat Anda ambil.

Tergantung pada sistem Anda, mungkin ada beberapa masalah dengan itu, seperti interupsi mungkin terlewatkan. Beberapa mikrokontroler mengatur bendera terlepas dari keadaan mengaktifkan interupsi global, dan setelah meninggalkan bagian kritis, interupsi dijalankan dan hanya ditunda. Tetapi jika Anda memiliki interupsi yang terjadi pada tingkat tinggi, Anda dapat melewatkan interupsi kedua kalinya jika Anda memblokir interupsi terlalu lama.

Jika bagian kritis Anda hanya membutuhkan satu interupsi untuk tidak dieksekusi, tetapi yang lain harus dieksekusi, pendekatan lain tampaknya dapat berjalan.

Saya menemukan diri saya memprogram rutinitas layanan interupsi sesingkat mungkin. Jadi mereka hanya mengatur flag yang kemudian diperiksa selama rutinitas program normal. Tetapi jika Anda melakukan itu, waspadalah terhadap kondisi lomba sambil menunggu bendera itu ditetapkan.

Ada banyak pilihan dan tentu saja tidak ada satu jawaban yang benar untuk ini, ini adalah topik yang membutuhkan desain yang cermat dan layak dipikirkan lebih banyak daripada hal lainnya.


5

Jika Anda telah menentukan bahwa bagian kode harus dijalankan tanpa gangguan maka, kecuali dalam keadaan yang tidak biasa, Anda harus menonaktifkan interupsi untuk durasi minimum yang mungkin untuk menyelesaikan tugas, dan mengaktifkannya kembali setelahnya.

Letakkan bendera di dalam ISR masing-masing dan (alih-alih menonaktifkan interupsi), atur bendera ke false sebelum bagian kritis dan atur ulang menjadi true, setelahnya. Untuk mencegah kode ISR dieksekusi.

Ini masih akan memungkinkan interupsi terjadi, lompatan kode, cek, lalu pengembalian. Jika kode Anda dapat menangani banyak gangguan ini, maka Anda mungkin harus mendesain ISR untuk mengatur flag daripada melakukan pemeriksaan - itu akan lebih pendek - dan menangani flag dalam rutin kode normal Anda. Ini terdengar seperti seseorang memasukkan terlalu banyak kode ke interupsi, dan mereka menggunakan interupsi untuk melakukan tindakan yang lebih lama yang seharusnya terjadi dalam kode biasa.

Jika Anda berurusan dengan kode yang interupsinya panjang, sebuah flag seperti yang Anda sarankan mungkin menyelesaikan masalah, tetapi masih akan lebih baik untuk menonaktifkan interupsi jika Anda tidak dapat memfaktorkan ulang kode untuk menghilangkan kode berlebih dalam interupsi .

Masalah utama dengan melakukannya dengan cara flag adalah bahwa Anda tidak menjalankan interupsi sama sekali - yang mungkin berakibat nantinya. Sebagian besar mikrokontroler akan melacak bendera interupsi meskipun interupsi dinonaktifkan secara global, dan kemudian akan menjalankan interupsi ketika Anda mengaktifkan kembali interupsi:

  • Jika tidak ada interupsi yang terjadi selama bagian kritis, tidak ada yang dieksekusi setelah.
  • Jika satu interupsi terjadi selama bagian kritis, satu dieksekusi setelah.
  • Jika beberapa interupsi terjadi selama bagian kritis, hanya satu yang dieksekusi setelah.

Jika sistem Anda rumit dan harus melacak interupsi lebih lengkap, Anda harus merancang sistem yang lebih rumit untuk melacak interupsi dan beroperasi sesuai itu.

Namun, jika Anda selalu merancang interupsi Anda untuk melakukan pekerjaan minimum yang diperlukan untuk mencapai fungsinya, dan menunda semua yang lain untuk pemrosesan reguler, maka interupsi jarang akan berdampak negatif pada kode Anda yang lain. Memiliki pengambilan interupsi atau rilis data jika diperlukan, atau set / reset output, dll sesuai kebutuhan, kemudian minta jalur kode utama memperhatikan flag, buffer, dan variabel yang mempengaruhi sehingga interupsi proses panjang dapat dilakukan dalam loop utama, daripada interupsi.

Ini akan menghilangkan semua kecuali situasi yang sangat, sangat sedikit di mana Anda mungkin memerlukan bagian kode yang tidak terputus.


Saya telah memperbarui pos untuk lebih menjelaskan situasi tempat saya bekerja :)
Umberto D.

1
Dalam kode contoh Anda, saya akan mempertimbangkan menonaktifkan interupsi tombol spesifik ketika tidak diperlukan, dan mengaktifkannya saat diperlukan. Melakukan hal ini sering kali bukanlah masalah desainnya. Biarkan interupsi global aktif sehingga Anda dapat menambahkan interupsi lain ke kode nanti jika perlu. Atau, atur ulang flag saat Anda masuk ke negara A dan abaikan saja. Jika tombol ditekan dan bendera ditetapkan, siapa yang peduli? Abaikan saja sampai Anda kembali ke keadaan A.
Adam Davis

Iya! Itu mungkin solusi karena dalam desain aktual saya sering masuk LPM3 (MSP430) dengan interupsi global diaktifkan dan saya keluar dari LPM3, melanjutkan eksekusi, segera setelah interupsi terdeteksi. Jadi solusinya adalah yang Anda sajikan yang menurut saya dilaporkan di bagian kedua kode: aktifkan interupsi segera setelah saya mulai melakukan aktivitas lakukan keadaan yang memerlukannya dan nonaktifkan sebelum pergi ke blok transisi. Mungkin solusi lain yang mungkin adalah menonaktifkan interupsi sesaat sebelum meninggalkan "Do activity block" dan aktifkan kembali kapan (kapan?) Sesudahnya?
Umberto D.

1

Meletakkan bendera di dalam ISR seperti yang Anda gambarkan mungkin tidak akan berhasil karena pada dasarnya Anda mengabaikan acara yang memicu interupsi. Menonaktifkan interupsi secara global biasanya merupakan pilihan yang lebih baik. Seperti yang orang lain katakan, Anda seharusnya tidak perlu melakukan ini terlalu sering. Ingatlah bahwa membaca atau menulis apa pun yang dilakukan melalui satu instruksi seharusnya tidak perlu dilindungi karena instruksi tersebut dijalankan atau tidak.

Banyak hal tergantung pada sumber daya apa yang ingin Anda bagikan. Jika Anda memasukkan data dari ISR ​​ke program utama (atau sebaliknya), Anda bisa mengimplementasikan sesuatu seperti buffer FIFO. Satu-satunya operasi atom akan memperbarui pointer baca dan tulis, yang meminimalkan jumlah waktu yang Anda habiskan dengan interupsi dinonaktifkan.


0

Ada perbedaan halus yang perlu Anda perhitungkan. Anda mungkin memilih untuk "menunda" penanganan interupsi atau "abaikan" dan interupsi.

Seringkali kita mengatakan dalam kode bahwa kita menonaktifkan interupsi. Apa yang mungkin akan terjadi, karena fungsi perangkat keras, adalah bahwa sekali kita mengaktifkan interupsi akan memicu. Ini dengan cara menunda interupsi. Agar sistem dapat bekerja dengan andal, kita perlu mengetahui waktu maksimum kita dapat menunda penanganan gangguan ini. Dan kemudian memastikan bahwa semua kasus di mana interupsi dinonaktifkan akan selesai dalam waktu yang lebih singkat.

Terkadang kita ingin mengabaikan interupsi. Cara terbaik mungkin untuk memblokir interupsi di tingkat perangkat keras. Seringkali ada pengendali interupsi atau serupa di mana kita dapat mengatakan input mana yang harus menghasilkan interupsi.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.