Come vengono implementati i gestori di interrupt nel CMSIS di Cortex M0?


9

Ho un kit LPC1114. Negli ultimi giorni ho scoperto l'implementazione CMSIS di Cortex M0 per scoprire come vengono fatte le cose. Finora ho capito come sono mappati tutti i registri e come posso accedervi. Ma ancora non so come siano implementati gli interrupt. Tutto quello che so sugli interrupt in CMSIS è che ci sono alcuni nomi di gestori di interrupt menzionati nel file di avvio. E posso scrivere i miei gestori semplicemente scrivendo una funzione C con gli stessi nomi menzionati nel file di avvio. Ciò che mi confonde è che nella guida per l'utente viene detto che tutti i GPIO possono essere utilizzati come fonti di interrupt esterne. Ma ci sono solo 4 interruzioni PIO menzionate nel file di avvio. Allora dimmi:

  1. Come posso implementare gestori di interrupt esterni per altri GPIO?
  2. Dove viene mappata la tabella degli interrupt nel CMSIS?
  3. Quali sono le principali differenze tra NVIC e l'implementazione degli interrupt negli AVR / PIC? (tranne NVIC può essere mappato ovunque nel flash)

Risposte:


14

Le seguenti informazioni si aggiungono alla risposta eccellente di Igor.

Dal punto di vista della programmazione C, i gestori di interrupt sono definiti nel file cr_startup_xxx.c (ad es. File cr_startup_lpc13.c per LPC1343). Tutti i possibili gestori di interrupt sono definiti lì come alias WEAK. Se non si definisce il proprio XXX_Handler () per una sorgente di interrupt, verrà utilizzata la funzione del gestore di interrupt predefinita definita in questo file. Il linker ordinerà quale funzione includere nel file binario finale insieme alla tabella di vettore di interrupt da cr_startup_xxx.c

Esempi di interruzioni GPIO dalle porte sono mostrati nei file demo in gpio.c. C'è un input di interrupt sull'NVIC per porta GPIO. Ogni singolo bit nella porta può essere abilitato / disabilitato per generare un interrupt su quella porta. Se si richiedono interruzioni sulle porte PIO1_4 e PIO1_5 ad esempio, si abiliteranno i singoli bit di interruzione PIO1_4 e PIO1_5 in GPIO0IE. Quando viene attivata la funzione del gestore di interrupt PIOINT0_Handler (), spetta a voi determinare quali degli interrupt PIO1_4 o PIO1_5 (o entrambi) sono in sospeso leggendo il registro GPIO0RIS e gestendo l'interrupt in modo appropriato.


10

(Si noti che i punti 1 e 2 sono dettagli di implementazione e non limitazioni di architettura.)

  1. Nei chip NXP più grandi (come LPC17xx) ci sono un paio di pin di interrupt dedicati (EINTn) che hanno il loro gestore di interrupt. Il resto dei GPIO deve usare un interrupt comune (EINT3). È quindi possibile eseguire il polling del registro di stato dell'interrupt per vedere quali pin hanno attivato l'interrupt.
  2. Non ho molta familiarità con LPC11xx ma sembra che abbia un interrupt per porta GPIO. Dovrai nuovamente controllare il registro di stato per capire i pin specifici. Ci sono anche fino a 12 pin che possono fungere da fonti di attivazione. Non sono sicuro che puoi dirottarli come interruzioni generali (cioè probabilmente verranno attivati ​​solo quando sono in stato di sospensione).
  3. La tabella dei gestori predefinita viene posizionata all'indirizzo 0 (che è in flash). La prima voce è il valore di ripristino per il registro SP, la seconda è il vettore di ripristino e il resto sono altre eccezioni e vettori di interruzione. Un paio dei primi (come NMI e HardFault) sono riparati da ARM, il resto è specifico per chip. Se è necessario modificare i vettori in fase di esecuzione, è possibile rimappare in RAM (è necessario prima copiare la tabella). In LPC11xx il remapping è fissato all'inizio di SRAM (0x10000000), altri chip possono essere più flessibili.
  4. NVIC è ottimizzato per una gestione efficiente degli interrupt:
    • livello di priorità programmabile di 0-3 per ciascun interrupt. Un interrupt con priorità più alta previene quelli con priorità più bassa (annidamento). L'esecuzione della priorità inferiore riprende al termine dell'interrupt con priorità superiore.
    • impilamento automatico dello stato del processore all'entrata di interrupt; ciò consente di scrivere i gestori di interrupt direttamente in C e di eliminare la necessità di wrapper di assemblaggio.
    • concatenamento della coda: invece di scoppiare e spingere di nuovo lo stato, il successivo interrupt in sospeso viene gestito immediatamente
    • late-arrival: se un interrupt con priorità più alta arriva mentre impila lo stato del processore, viene eseguito immediatamente anziché quello in sospeso in precedenza.

Dato che hai familiarità con i PIC, dai un'occhiata a questa app Nota: migrazione da microcontrollori PIC a Cortex-M3

Riguarda M3, ma la maggior parte dei punti si applica anche a M0.


8

Le risposte di Austin e Igor sono abbastanza dettagliate. Tuttavia, voglio rispondere in un altro modo, forse lo trovi utile.

LPC11xx (Cortex-M0) ha 4 livelli per i pin GPIO, tutti i pin da GPIO0.0 a GPIO0.n condividono lo stesso numero di interrupt e tutti i pin da GPIO3.0 a GPIO3.m condividono lo stesso numero di interrupt.

Esistono sei passaggi per inizializzare l'interruzione GPIO in LPC11xx

  1. Impostare la funzione pin modificando i registri del blocco connessione pin.
  2. Impostare la direzione del pin modificando il registro della direzione dei dati GPIO (viene inserito il valore predefinito).
  3. Impostare l'interrupt per ogni singolo pin, è necessario accedere al registro della maschera di interrupt GPIO GPIOnIE e impostare la logica di bit (corrispondente al pin) 1.
  4. Impostare l'interrupt per fronte di salita o di discesa o entrambi modificando i registri di rilevamento di interruzione GPIO GPIOnIBE e GPIOnIS.
  5. Abilitare la fonte di interruzione PIO_0 / PIO_1 / PIO_2 / PIO_3 nel controllo di interruzione vettoriale annidato utilizzando le funzioni CMSIS.
  6. Impostare la priorità di interruzione utilizzando le funzioni CMSIS.

Implementazioni del codice. Sono necessarie due funzioni: una inizializza 6 passaggi precedenti e la seconda è il gestore di interrupt, che deve avere lo stesso nome del gestore definito nei codici di avvio, startup_LPC11xx.sfile. I nomi sono da PIOINT0_IRQHandlera PIOINT3_IRQHandler. Se si utilizza un nome diverso, è necessario modificare i nomi nel file di avvio.

/*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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.