Kondisi Ras Tidur Mikrokontroler

11

Diberi mikrokontroler yang menjalankan kode berikut:

volatile bool has_flag = false;

void interrupt(void) //called when an interrupt is received
{
    clear_interrupt_flag(); //clear interrupt flag
    has_flag = true; //signal that we have an interrupt to process
}

int main()
{
    while(1)
    {
        if(has_flag) //if we had an interrupt
        {
            has_flag = false; //clear the interrupt flag
            process(); //process the interrupt
        }
        else
            sleep(); //place the micro to sleep
    }
}

Misalkan if(has_flag)kondisi bernilai false dan kita akan menjalankan instruksi tidur. Tepat sebelum kami menjalankan instruksi tidur, kami menerima interupsi. Setelah kami meninggalkan interupsi, kami menjalankan instruksi tidur.

Urutan eksekusi ini tidak diinginkan karena:

  • Mikrokontroler pergi tidur alih-alih bangun dan memanggil process().
  • Mikrokontroler mungkin tidak pernah bangun jika tidak ada interupsi setelahnya diterima.
  • Panggilan ke process()ditunda hingga interupsi berikutnya.

Bagaimana kode dapat ditulis untuk mencegah kondisi lomba ini terjadi?

Edit

Beberapa mikrokontroler, seperti di ATMega, memiliki bit aktifkan Tidur yang mencegah kondisi ini terjadi (terima kasih Kvegaoro karena telah menunjukkan ini). JRoberts menawarkan contoh implementasi yang mencontohkan perilaku ini.

Mikro lainnya, seperti pada PIC18s, tidak memiliki bit ini, dan masalahnya masih terjadi. Namun, micros ini dirancang sedemikian rupa sehingga interupsi masih dapat membangun inti terlepas dari apakah bit global interrupt diaktifkan diatur (terima kasih supercat untuk menunjukkan ini). Untuk arsitektur seperti itu, solusinya adalah menonaktifkan interupsi global tepat sebelum tidur. Jika interupsi menyala tepat sebelum menjalankan instruksi tidur, penangan interupsi tidak akan dieksekusi, inti akan bangun, dan begitu interupsi global diaktifkan kembali, penangan interupsi akan dieksekusi. Dalam pseudo-code, implementasinya akan terlihat seperti ini:

int main()
{
    while(1)
    {
        //clear global interrupt enable bit.
        //if the flag tested below is not set, then we enter
        //sleep with the global interrupt bit cleared, which is
        //the intended behavior.
        disable_global_interrupts();

        if(has_flag) //if we had an interrupt
        {
            has_flag = false; //clear the interrupt flag
            enable_global_interrupts();  //set global interrupt enable bit.

            process(); //process the interrupt
        }
        else
            sleep(); //place the micro to sleep
    }
}
TRISAbits
sumber
Apakah ini pertanyaan praktis atau teoretis?
AndrejaKo
secara teori Anda menggunakan timer yang membangunkan Anda setiap sekali (masukkan nilai yang dapat diterima) ms dan kemudian kembali tidur jika tidak perlu dilakukan.
Pemain Grady
1
Saya akan lakukan interrupt_flagsebagai int, dan menambahkannya setiap kali ada interupsi. Kemudian ganti if(has_flag)ke while (interrupts_count)dan kemudian tidur. Meskipun demikian, interupsi dapat terjadi setelah Anda keluar dari loop sementara. Jika ini merupakan masalah, maka apakah pemrosesan dalam interrupt itu sendiri?
angelatlarge
1
baik itu tergantung pada mikro apa yang Anda jalankan .. Jika itu adalah ATmega328 Anda mungkin dapat menonaktifkan mode tidur pada interupsi, jadi jika kondisi ras yang Anda jelaskan terjadi maka fungsi tidur akan ditimpa, loop kembali dan Anda akan memproses interupsi dengan latensi kecil. Tetapi juga menggunakan timer untuk bangun pada interval yang sama atau kurang dengan latensi maksimum Anda akan menjadi solusi yang hebat juga
Kvegaoro
1
@TRISAbits: Pada PIC 18x, pendekatan yang saya jelaskan dalam jawaban saya berfungsi dengan baik (ini adalah desain normal saya ketika menggunakan bagian itu).
supercat

Jawaban:

9

Biasanya ada beberapa jenis dukungan perangkat keras untuk kasus ini. Misalnya, seiinstruksi AVR untuk mengaktifkan interupsi akan mengaktifkan hingga instruksi berikut selesai. Dengan itu orang dapat melakukannya:

forever,
   interrupts off;
   if has_flag,
      interrupts on;
      process interrupt;
   else,
      interrupts-on-and-sleep;    # won't be interrupted
   end
end

Interupsi yang terlewatkan dalam contoh akan dalam kasus ini ditunda sampai prosesor menyelesaikan urutan tidurnya.

JRobert
sumber
Jawaban bagus! Algoritma yang Anda berikan benar-benar berfungsi dengan sangat baik pada AVR. Terima kasih untuk sarannya.
TRISAbits
3

Pada banyak mikrokontroler, selain dapat mengaktifkan atau menonaktifkan penyebab interupsi tertentu (biasanya dalam modul pengendali interupsi), ada flag utama dalam inti CPU yang menentukan apakah permintaan interupsi akan diterima. Banyak mikrokontroler akan keluar dari mode tidur jika permintaan interupsi mencapai inti, baik inti mau menerimanya atau tidak.

Pada desain seperti itu, pendekatan sederhana untuk mencapai perilaku tidur yang dapat diandalkan adalah dengan memeriksa lingkaran utama menghapus bendera dan kemudian memeriksa apakah tahu alasan mengapa prosesor harus bangun. Setiap interupsi yang terjadi selama waktu itu yang dapat mempengaruhi salah satu dari alasan-alasan itu harus menetapkan bendera. Jika loop utama tidak menemukan alasan untuk tetap terjaga, dan jika flag tidak disetel, loop utama harus menonaktifkan interupsi dan memeriksa lagi flag [mungkin setelah beberapa instruksi NOP jika mungkin interupsi yang menjadi tertunda selama instruksi penonaktifan-interupsi dapat diproses setelah pengambilan operan yang terkait dengan instruksi berikut telah dilakukan]. Jika bendera masih belum diatur, maka tidurlah.

Dalam skenario ini, interupsi yang terjadi sebelum loop utama menonaktifkan interupsi akan mengatur bendera sebelum tes akhir. Suatu interupsi yang menjadi tertunda terlalu terlambat untuk diservis sebelum instruksi tidur akan mencegah prosesor dari tidur. Kedua situasi baik-baik saja.

Sleep-on-exit terkadang merupakan model yang baik untuk digunakan, tetapi tidak semua aplikasi benar-benar "pas". Misalnya, perangkat dengan LCD hemat energi mungkin paling mudah diprogram dengan kode yang terlihat seperti:

void select_view_user(int default_user)
{
  int current_user;
  int ch;
  current_user = default_user;
  do
  {
    lcd_printf(0, "User %d");
    lcd_printf(1, ...whatever... );
    get_key();
    if (key_hit(KEY_UP)) {current_user = (current_user + 1) % MAX_USERS};
    if (key_hit(KEY_DOWN)) {current_user = (current_user + MAX_USERS-1) % MAX_USERS};
    if (key_hit(KEY_ENTER)) view_user(current_user);
  } while(!key_hit(KEY_EXIT | KEY_TIMEOUT));
}

Jika tidak ada tombol yang ditekan, dan tidak ada hal lain yang terjadi, tidak ada alasan sistem tidak tidur selama eksekusi get_keymetode. Meskipun dimungkinkan untuk memiliki kunci memicu interupsi, dan mengelola semua interaksi antarmuka pengguna melalui mesin negara, kode seperti di atas sering kali merupakan cara paling logis untuk menangani aliran antarmuka pengguna yang sangat-modal khas mikrokontroler kecil.

supercat
sumber
Terima kasih supercat untuk jawaban yang bagus. Menonaktifkan interupsi dan kemudian tidur adalah solusi fantastis asalkan inti akan bangun dari sumber interupsi terlepas dari apakah bit interupsi global diatur / dihapus. Saya melihat skema hardware mengganggu PIC18, dan solusi ini akan bekerja.
TRISAbits
1

Programkan mikro untuk bangun saat interupsi.

Rincian spesifik akan bervariasi tergantung pada mikro yang Anda gunakan.

Kemudian, ubah rutinitas utama ():

int main()
{
    while(1)
    {
        sleep();
        process(); //process the interrupt
    }
}
jwygralak67
sumber
1
Arsitektur bangun-interupsi diasumsikan dalam pertanyaan. Saya tidak berpikir jawaban Anda memecahkan pertanyaan / masalah.
angelatlarge
@angelatlarge Point diterima. Saya telah menambahkan contoh kode yang menurut saya membantu.
jwygralak67
@ jwygralak67: Terima kasih atas sarannya, tetapi kode yang Anda berikan hanya memindahkan masalah ke dalam proses () rutin, yang sekarang harus menguji apakah interupsi terjadi sebelum mengeksekusi tubuh proses ().
TRISAbits
2
Jika interupsi tidak terjadi, mengapa kita bangun?
JRobert
1
@JRobert: Kita bisa bangun dari gangguan sebelumnya, menyelesaikan proses () rutin, dan ketika kita menyelesaikan tes if (has_flag) dan tepat sebelum tidur, kita mendapat gangguan lagi, yang menyebabkan masalah yang saya jelaskan di pertanyaan.
TRISAbits