Bagaimana interrupt handler diimplementasikan dalam CMSIS Cortex M0?

9

Saya memiliki kit LPC1114. Beberapa hari terakhir saya telah menggali implementasi CMSIS dari Cortex M0 untuk menemukan bagaimana hal-hal dilakukan di dalamnya. Sejauh ini saya mengerti bagaimana masing-masing register dipetakan dan bagaimana saya bisa mengaksesnya. Tapi saya masih tidak tahu bagaimana interupsi diterapkan di dalamnya. Yang saya tahu tentang interupsi dalam CMSIS adalah ada beberapa nama penangan interupsi yang disebutkan dalam file startup. Dan saya dapat menulis handler saya sendiri hanya dengan menulis fungsi C dengan nama yang sama yang disebutkan dalam file startup. Yang membingungkan saya adalah bahwa dalam panduan pengguna, diceritakan bahwa semua GPIO dapat digunakan sebagai sumber interupsi eksternal. Tetapi hanya ada 4 interupsi PIO yang disebutkan dalam file startup. Jadi katakan padaku:

  1. Bagaimana saya bisa menerapkan penangan interupsi eksternal untuk GPIO lain?
  2. Di mana tabel interupsi dipetakan dalam CMSIS?
  3. Apa perbedaan utama antara NVIC dan implementasi interupsi dalam AVR / PIC? (kecuali NVIC dapat dipetakan di mana saja dalam flash)
0xakhil
sumber

Jawaban:

14

Informasi berikut ini merupakan tambahan untuk jawaban Igor yang sangat baik.

Dari perspektif pemrograman C, penangan interupsi didefinisikan dalam file cr_startup_xxx.c (misalnya file cr_startup_lpc13.c untuk LPC1343). Semua penangan interupsi yang mungkin didefinisikan di sana sebagai alias LEMAH. Jika Anda tidak mendefinisikan XXX_Handler () Anda sendiri untuk sumber interupsi, maka fungsi pengendali interupsi default yang ditentukan dalam file ini akan digunakan. Linker akan memilah fungsi yang akan dimasukkan dalam biner akhir bersama dengan tabel vektor interupsi dari cr_startup_xxx.c

Contoh interupsi GPIO dari port ditunjukkan pada file demo di gpio.c. Ada satu input interupsi ke NVIC per port GPIO. Setiap bit individu di port dapat diaktifkan / dinonaktifkan untuk menghasilkan interupsi pada port itu. Jika Anda memerlukan interupsi pada port PIO1_4, dan PIO1_5 misalnya, maka Anda akan mengaktifkan bit interupsi PIO1_4 dan PIO1_5 individual di GPIO0IE. Ketika fungsi interrupt handler PIOINT0_Handler () Anda, Anda yang menentukan PIO1_4 atau PIO1_5 (atau keduanya) interupsi mana yang tertunda dengan membaca register GPIO0RIS dan menangani interupsi dengan tepat.

Austin Phillips
sumber
10

(Harap dicatat bahwa poin 1 dan 2 adalah detail implementasi dan bukan batasan arsitektur.)

  1. Dalam chip NXP yang lebih besar (seperti LPC17xx) ada beberapa pin interrupt khusus (EINTn) yang memiliki handler interrupt sendiri. Sisa GPIO harus menggunakan satu interupsi umum (EINT3). Anda kemudian dapat polling register status interupsi untuk melihat pin mana yang memicu interupsi.
  2. Saya tidak terlalu mengenal LPC11xx tetapi tampaknya memiliki satu interupsi per port GPIO. Anda lagi harus memeriksa register status untuk mengetahui pin tertentu. Ada juga hingga 12 pin yang dapat bertindak sebagai sumber wakeup. Saya tidak yakin apakah Anda dapat membajaknya sebagai gangguan umum (yaitu, mereka mungkin hanya akan dipicu ketika dalam kondisi tidur).
  3. Tabel pengendali default ditempatkan di alamat 0 (yang ada dalam blitz). Entri pertama adalah nilai reset untuk register SP, yang kedua adalah reset vektor, dan sisanya adalah pengecualian lain dan vektor interupsi. Beberapa yang pertama (seperti NMI dan HardFault) diperbaiki oleh ARM, sisanya khusus chip. Jika Anda perlu mengubah vektor saat runtime, Anda dapat memetakannya kembali ke RAM (pertama-tama Anda perlu menyalin tabel). Dalam LPC11xx remapping diperbaiki ke awal SRAM (0x10000000), chip lainnya bisa lebih fleksibel.
  4. NVIC dioptimalkan untuk penanganan interupsi yang efisien:
    • tingkat prioritas yang dapat diprogram dari 0-3 untuk setiap interupsi. Interupsi berprioritas lebih tinggi mendahului yang berprioritas rendah (bersarang). Eksekusi yang lebih rendah prioritas dilanjutkan ketika interupsi prioritas yang lebih tinggi selesai.
    • susun otomatis dari status prosesor pada entri interupsi; ini memungkinkan penulisan penangan interupsi secara langsung dalam C dan menghilangkan kebutuhan pembungkus rakitan.
    • tail-chaining: alih-alih muncul dan mendorong negara lagi, interupsi tertunda berikutnya ditangani segera
    • keterlambatan tiba: jika interupsi dengan prioritas lebih tinggi tiba saat menumpuk status prosesor, itu dieksekusi segera alih-alih yang sebelumnya tertunda.

Karena Anda terbiasa dengan PIC, lihat Catatan Aplikasi ini: Bermigrasi dari PIC Microcontrollers ke Cortex-M3

Ini tentang M3, tetapi sebagian besar poin berlaku untuk M0 juga.

Igor Skochinsky
sumber
8

Jawaban Austin dan Igor cukup rinci. Namun, saya ingin menjawabnya dengan cara lain, mungkin Anda merasa terbantu.

LPC11xx (Cortex-M0) memiliki 4 level untuk pin GPIO, semua pin dari GPIO0.0 hingga GPIO0.n berbagi nomor interupsi yang sama, dan semua pin dari GPIO3.0 hingga GPIO3.m berbagi nomor interupsi yang sama.

Ada enam langkah untuk menginisialisasi interupsi GPIO di LPC11xx

  1. Atur fungsi pin dengan memodifikasi Pin Connection Block Register.
  2. Atur arah pin dengan memodifikasi register arah data GPIO (nilai default adalah input).
  3. Atur interupsi untuk setiap pin individual, Anda harus pergi ke register masker interupsi GPIO GPIOnIE dan mengatur bit (yang sesuai dengan pin) logika 1.
  4. Atur interupsi untuk naik turun atau turun atau keduanya dengan memodifikasi register interupsi indra GPIO GPIOnIBE dan GPIOnIS.
  5. Aktifkan sumber interupsi baik PIO_0 / PIO_1 / PIO_2 / PIO_3 di Kontrol Interrupt Ber-vektor dengan menggunakan fungsi CMSIS.
  6. Tetapkan prioritas interupsi dengan menggunakan fungsi CMSIS.

Implementasi kode. Anda memerlukan dua fungsi: satu menginisialisasi 6 langkah di atas, dan yang kedua adalah penangan interrupt, yang diperlukan untuk nama yang sama dengan penangan yang ditentukan dalam kode start-up, startup_LPC11xx.sfile. Nama-nama itu dari PIOINT0_IRQHandlerhingga PIOINT3_IRQHandler. Jika Anda menggunakan nama yang berbeda, Anda harus mengubah nama dalam file start-up.

/*Init the GPIO pin for interrupt control */
void GPIO_Init(){
    LPC_IOCON-> =..              //Pin configuration register
    LPC_GPIO1->FIODIR = ...      //GPIO Data direction register
    LPC_GPIO1->FIOMASK = ..      //GPIO Data mask register - choose  the right pin
    LPC_GPIO1->GPIOnIE = ..      //Set up falling or rising edge 
    NVIC_EnableIRQ(PIO_1);       //Call API to enable interrupt in NVIC
    NVIC_SetPriority(PriorityN); //Set priority if needed
}


/*Must have the same name as listed in start-up file startup_LPC11xx.s */
void PIOINT1_IRQHandler(void){
   //Do something here
}
Phuong Pham
sumber