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:
- Transisi / Efek.
- Kondisi keluar.
- Aktivitas masuk.
- 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_A
dan 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 buttonPressed
yang saya perlu (walaupun tidak mungkin hal ini terjadi karena 250 ms harus berlalu).
sumber
Jawaban:
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.
sumber
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.
sumber
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.
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 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.
sumber
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.
sumber
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.
sumber