Comment les gestionnaires d'interruption sont-ils implémentés dans CMSIS de Cortex M0?

9

J'ai un kit LPC1114. Ces derniers jours, j'ai fouillé l'implémentation CMSIS de Cortex M0 pour trouver comment les choses s'y déroulent. Jusqu'à présent, j'ai compris comment chaque registre est mappé et comment je peux y accéder. Mais je ne sais toujours pas comment les interruptions y sont implémentées. Tout ce que je sais des interruptions dans CMSIS, c'est qu'il y a des noms de gestionnaires d'interruption mentionnés dans le fichier de démarrage. Et je peux écrire mes propres gestionnaires en écrivant simplement une fonction C avec les mêmes noms mentionnés dans le fichier de démarrage. Ce qui m'embrouille, c'est que dans le guide de l'utilisateur, on dit que tous les GPIO peuvent être utilisés comme sources d'interruption externes. Mais il n'y a que 4 interruptions PIO mentionnées dans le fichier de démarrage. Alors dites-moi:

  1. Comment puis-je implémenter des gestionnaires d'interruption externes pour d'autres GPIO?
  2. Où est la table d'interruption mappée dans le CMSIS?
  3. Quelles sont les principales différences entre NVIC et l'implémentation d'interruption dans les AVR / PIC? (sauf que NVIC peut être mappé n'importe où dans le flash)
0xakhil
la source

Réponses:

14

Les informations suivantes s'ajoutent à l'excellente réponse d'Igor.

Du point de vue de la programmation C, les gestionnaires d'interruption sont définis dans le fichier cr_startup_xxx.c (par exemple, le fichier cr_startup_lpc13.c pour LPC1343). Tous les gestionnaires d'interruptions possibles y sont définis comme un alias FAIBLE. Si vous ne définissez pas votre propre XXX_Handler () pour une source d'interruption, la fonction de gestionnaire d'interruption par défaut définie dans ce fichier sera utilisée. L'éditeur de liens triera la fonction à inclure dans le binaire final avec la table des vecteurs d'interruption de cr_startup_xxx.c

Des exemples d'interruptions GPIO à partir des ports sont présentés dans les fichiers de démonstration de gpio.c. Il y a une entrée d'interruption sur le NVIC par port GPIO. Chaque bit individuel du port peut être activé / désactivé pour générer une interruption sur ce port. Si vous avez besoin d'interruptions sur les ports PIO1_4 et PIO1_5 par exemple, vous devez activer les bits d'interruption individuels PIO1_4 et PIO1_5 dans GPIO0IE. Lorsque votre fonction de gestionnaire d'interruption PIOINT0_Handler () se déclenche, c'est à vous de déterminer laquelle des interruptions PIO1_4 ou PIO1_5 (ou les deux) sont en attente en lisant le registre GPIO0RIS et en gérant l'interruption de manière appropriée.

Austin Phillips
la source
10

(Veuillez noter que les points 1 et 2 sont des détails de mise en œuvre et non des limitations architecturales.)

  1. Dans les puces NXP plus grandes (telles que LPC17xx), il y a quelques broches d'interruption dédiées (EINTn) qui ont leur propre gestionnaire d'interruption. Les autres GPIO doivent utiliser une interruption commune (EINT3). Vous pouvez ensuite interroger le registre d'état d'interruption pour voir quelles broches ont déclenché l'interruption.
  2. Je ne connais pas très bien le LPC11xx mais il semble qu'il y ait une interruption par port GPIO. Vous devrez à nouveau vérifier le registre d'état pour déterminer les broches spécifiques. Il existe également jusqu'à 12 broches pouvant servir de sources de réveil. Je ne sais pas si vous pouvez les détourner en tant qu'interruptions générales (c'est-à-dire qu'elles ne seront probablement déclenchées qu'en mode veille).
  3. La table de gestion par défaut est placée à l'adresse 0 (qui est en flash). La première entrée est la valeur de réinitialisation pour le registre SP, la seconde est le vecteur de réinitialisation et les autres sont d'autres exceptions et vecteurs d'interruption. Quelques-uns des premiers (tels que NMI et HardFault) sont corrigés par ARM, les autres sont spécifiques à la puce. Si vous devez modifier les vecteurs au moment de l'exécution, vous pouvez le remapper en RAM (vous devez d'abord copier la table). Dans LPC11xx, le remappage est fixé au début de SRAM (0x10000000), d'autres puces peuvent être plus flexibles.
  4. Le NVIC est optimisé pour une gestion efficace des interruptions:
    • niveau de priorité programmable de 0 à 3 pour chaque interruption. Une interruption de priorité plus élevée prévaut celles de priorité inférieure (imbrication). L'exécution de la priorité inférieure reprend lorsque l'interruption de priorité supérieure est terminée.
    • empilement automatique de l'état du processeur lors de l'entrée d'interruption; cela permet d'écrire des gestionnaires d'interruption directement en C et élimine le besoin de wrappers d'assemblage.
    • chaînage: au lieu de sauter et de pousser à nouveau l'état, la prochaine interruption en attente est traitée immédiatement
    • arrivée tardive: si une interruption de priorité supérieure arrive lors de l'empilement de l'état du processeur, elle est exécutée immédiatement au lieu de celle précédemment en attente.

Puisque vous êtes familier avec les PIC, jetez un œil à cette note d'application: Migration des microcontrôleurs PIC vers Cortex-M3

Il s'agit de M3, mais la plupart des points s'appliquent également à M0.

Igor Skochinsky
la source
8

Les réponses d'Austin et d'Igor sont suffisamment détaillées. Cependant, je veux y répondre d'une autre manière, peut-être vous le trouverez utile.

Le LPC11xx (Cortex-M0) a 4 niveaux pour les broches GPIO, toutes les broches de GPIO0.0 à GPIO0.n partagent le même numéro d'interruption et toutes les broches de GPIO3.0 à GPIO3.m partagent le même numéro d'interruption.

Il y a six étapes pour initialiser une interruption GPIO dans LPC11xx

  1. Configurez la fonction de broche en modifiant les registres de bloc de connexion de broche.
  2. Configurez la direction des broches en modifiant le registre de direction des données GPIO (la valeur par défaut est entrée).
  3. Configurez l'interruption pour chaque broche individuelle, vous devez vous rendre dans le registre de masque d'interruption GPIO GPIOnIE et définir la logique du bit (qui correspond à la broche) 1.
  4. Configurez l'interruption pour le front montant ou le front descendant ou les deux en modifiant les registres de détection d'interruption GPIO GPIOnIBE et GPIOnIS.
  5. Activez la source d'interruption PIO_0 / PIO_1 / PIO_2 / PIO_3 dans le contrôle d'interruption vectorisée imbriquée à l'aide des fonctions CMSIS.
  6. Définissez la priorité d'interruption à l'aide des fonctions CMSIS.

Implémentations de code. Vous avez besoin de deux fonctions: l'une initialise 6 étapes ci-dessus et la seconde est le gestionnaire d'interruption, qui doit être du même nom que le gestionnaire défini dans le startup_LPC11xx.sfichier des codes de démarrage . Les noms vont de PIOINT0_IRQHandlerà PIOINT3_IRQHandler. Si vous utilisez un nom différent, vous devez changer les noms dans le fichier de démarrage.

/*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
la source