jeudi 12 décembre 2013

PIC24, les interruptions

Dans cet article je traite des interruptions sur les PIC24. A titre de démo je vais utiliser le cube de LEDs 4x4x4 présenté dans un article précédent. J'ai modifié le firmware de sorte que l'affichage du cube est maintenant géré par une une interruption. J'ai aussi ajouter une interface RS-232 en utilisant le périphérique UART1. La réception des caractères est aussi géré par une interruption.

Les interruptions sur PIC24/dsPIC30/dsPIC33 sont multi-niveaux avec priorité programmable. Multi-niveaux (nested en anglais) signifie qu'une interruption en cours peut-être interrompue par une autre de priorité supérieure. Lorsque l'interruption de niveau supérieur se termine l'exécution continu dans le code d'interruption de niveau inférieur qui a été interrompu. chaque table d'interruption (la principale et l'alternative) contient 126 vecteurs. C'est vecteurs sont des adresses qui correspondent au point d'entré de la routine qui sert l'interruption (ISR entry point). Les 8 premiers vecteurs servent a gérer les exceptions. En fait actuellement sur les PIC24 seulement 4 sont utilisés les autres sont réservés. Les 118 autres vecteurs sont pour les périphériques. Encore là plusieurs de ces vecteurs sont marqués comme reserved et donc inutilisés. La table des vecteurs est disponible dans le document suivant: répertoire_installation/microchip/xc16/v1.20/docs/IntVectors/PIC24F_Interrupt_Vectors.html.

Le système d'interruption des PIC24 fonctionne de façon très similaire à celui des PIC32MX on n'est donc pas dépaysé si on connais déjà ce dernier. Il y a cependant des différences, entre autre il n'y a pas de sous-priorité. Les niveaux de priorité sont de 0-15 pour le CPU mais les niveaux programmables par l'utilisateur sont de 0-7 car les niveaux 8-15 sont réservés aux exceptions (traps). La priorité est dans l'ordre numérique c'est à dire que la plus basse est 0 et la plus haute est 15. Ça peut sembler évident mais sur certains MCU c'est l'inverse.

Si 2 interruptions de priorité égales sont déclenchées en même temps c'est l'ordre dans la table des vecteurs qui détermine laquelle sera servie en premier, plus petit IRQ# en premier.

Le CPU ne réponds qu'aux interruptions de niveau supérieur au niveau pour lequel il est programmé. C'est à dire qu'il y a 3 bits dans le registre d'état (SR), IPL0, IPL1 et IPL2 qui contrôlent le niveau de réponse. le registre core control register (CORCON) contient un 4ième bit IPL4. Si IPL4 est réglé à 1 le CPU ne répond plus qu'aux exceptions. A la mise sous tension ou après un RESET matériel ou logiciel le niveau de réponse est à zéro.

Configuration des interruptions

Il y a plusieurs registres spéciaux associés à la configuration des interruptions:

  • INTCON1 Contrôle global des interruptions. Contient le bit NSTDI qui sert à activer/désactiver les interruptions multi-niveaux. Contient aussi les bits qui indiquent la source d'une exception.
  • INTCON2 Contient le bit ALTIVT. Lorsque ce bit est à 1 la table de vecteurs alternative est utilisée (débogage). Contient aussi le bit DISI qui indique que les interruptions sont temporairement désactivés pour un nombres de cycles déterminé par le contenu du registre DISICNT (utile dans les sections de code critiques). Contient aussi les bits INTnEP qui détermine la polarité de réponse des interruptions externes (transition positive/négative).
  • SRL<7:5> IPL2:IPL0 contrôle le niveau de réponse du CPU
  • CORCON<3> IPL3 contrôle le niveau de réponse du CPU
  • IECn ces SFR servent à activer/désactiver l'interruption sur un périphérique particulier. Chaque périphérique a 1 bit qui lui est assigné dans un de ces registres.
  • IPCn ces SFR servent à déterminer le niveau de priorité de l'interruption. Chaque périphérique a 3 bits qui lui sont assigné dans un de ces registres.
  • IFSn ces SFR servent d'indicateurs booléens pour les interruptions (interrupt flag). Chaque interruption a 1 bit qui lui est réservé dans 1 de ces registres.

Si on veut que le CPU réponde à toutes les interruptions on laisse les IPLx à zéro (valeur par défaut). Mais cette valeur peut-être modifier en cours d'exécution selon les besoins.

Pour configurer une interruption sur périphérique il faut:

  1. Déterminer à quel IPCn est associé le périphérique et programmer le niveau de priorité désiré. Niveau 4 par défaut.
  2. Déterminer à quel IFSn est associé le périphérique et remettre à zéro son bit IFS.
  3. Déterminer à quel IECn est associé le périphérique et mettre à 1 sont bit IE pour rendre active l'interruption.
Exemple: TIMER1, niveau d'interruption 7 (le plus élevé).

Création de la routine de service d'interruption

Les routines d'interruption (ISR) ne doivent avoir aucun arguments et ne retournent aucune valeur pour des raisons évident elles sont donc de la forme void isr(void). Pour qu'une fonction soit reconnue comme ISR ou gestionnaire d'exception il faut lui attribué l'attibut interrupt. Exemple:
L'attribut no_auto_psv réduit le préambule de l'interruption donc son délais de réponse (latency) en évitant la sauvegarde du registre PSV. Pour les MCU avec 16Ko de mémoire flash ou moins ce registre est toujours à zéro donc inutile de le sauvegarder. Même chose si l'application n'utilise pas la fonction program space visibility2.
Les indicateurs booléens d'interruptions ne sont pas remis à zéro automatiquement il faut le faire avant de quitter la routine ISR sinon l'interruption va se déclencher en boucle.

Il existe 2 macros pour simplifier la déclaration des routines d'interruptions:
_ISR
_ISRFAST
Dans l'exemple ci-haut j'aurais plus déclarer:
_ISRFAST _T1Interrupt(void);

Désactivation globale des interruptions

Contrairement aux autres MCU PIC, les PIC24 n'ont pas de bit GIE dans INTCON ou ailleurs. Pour désactiver les interruptions de niveau 0-6 temporairement ont peut utiliser l'instruction DISI en assembleur. Pour les désactiver de façon permanente on peut modifier le niveau de réponse du CPU avec la macro SET_CPU_IPL(ipl). En mettant ipl=7 toutes les interruptions sont désactivées.

Programme démo

Ajout d'une interface RS-232 au cube.

Ce démo utilise le cube de LED 4x4x4 avec la même animation que dans l'article de présentation du cube sauf que l'affichage du cube est dirigé par une interruption sur le TIMER1 à 200µsec d'intervalle. l'ensemble du cube est donc mise à jour environ 78 fois par seconde. De plus la variable anim_delay est incrémentée en boucle dans la même ISR et est utilisée pour contrôler la vitesse de défilement de l'animation. Avec la constante ANIM_DLY servant de modulo. Pour la valeur 1000 on a un intervalle 0,2 seconde entre chaque configuration. Le programme comprends en plus 2 autres interruptions, l'une pour la transmission des caractères sur UART1 et une autre pour la réception des caractères. Transmission et réception utilisent une file circulaire, rxbuff pour la réception et txbuff pour la transmission. A partir d'un ordinateur en utilisant un émulateur de terminal on peut arrêter l'animation en envoyant le caractère 's' et la redémarrer avec le caractère 'r'. Le MCU répond à chaque commande en envoyant le message " OK\n\r".


notes

1) Le family reference manual des PIC24F contients 66 sections disponibles ici.

2) la fonction program space visibility permet de rendre accessible en lecture une section de la mémoire flash en mémoire RAM. Cette fonction est utile lorsqu'il y a des tables de données en mémoire flash. Le registre PSVPAG détermine l'adresse de départ du segment flash à rendre visible dans la RAM. Cette adresse doit-être un multiple de 16Ko. Cette section sera visible dans la RAM à partir de l'adresse 0x8000. Lorsqu'on programme en 'C' le compilateur s'occupe de ces détails. Par exemple dans le démo ci-haut la chaîne de caractère " OK\n\r" est conservée en mémoire flash et le compilateur fait ce qu'il faut pour que UartPrint() puisse l'accéder via le program space visibility.

Aucun commentaire:

Enregistrer un commentaire