<< The Fribotte Homepage >>
Un club de passionnés en robotique participant à la coupe de France E=M6.
[Accueil] [Qui sommes-nous ?] [Robots] [Coupe e=m6] [BD Technique] [Forum] [Reportages] [Liens] [WiKiFri]

Fribotte

 

La Programmation des pics
par Bigonoff
Premiere partie - pic 16f84 - Révision 5

Téléchargement des fichiers originels ici
Publié le 27/02/2002

12. Les interruptions

Index coursIndex du cours
Chapitre précédent11. Réalisation d’un programme embarqué (Suite)
Chapitre suivant12. Les interruptions (Suite 1)

 

12. Les interruptions

Voilà un chapitre qui fera certainement peur à beaucoup de futurs programmeurs. Pourtant, le mécanisme des interruptions est très aisé à comprendre, et également à mettre en œuvre, pour autant que l’on travaille de manière propre et structurée.
12.1 Qu’est-ce qu’une interruption ?

Imaginez une conversation normale. Chaque interlocuteur prend la parole quand vient son tour de parler. Survient alors un événement extérieur dont le traitement est urgent. Par exemple, un piano tombe du 3ème étage de l’immeuble au pied duquel vous discutez. Vous imaginez bien que votre interlocuteur ne va pas attendre la fin de votre phrase pour vous signaler le danger. Il va donc vous INTERROMPRE durant le cours normal de votre conversation., afin de pouvoir TRAITER IMMEDIATEMENT l’EVENEMENT extérieur. Les interlocuteurs reprendront leur conversation où elle en était arrivée, sitôt le danger écarté.

Et bien, pour les programmes, c’est exactement le même principe. Votre programme se déroule normalement. Survient un événement spécifique. Le programme principal est interrompu (donc, subit une INTERRUPTION), et va traiter l’événement, avant de reprendre le programme principal à l’endroit où il avait été interrompu.

L’interruption est donc une RUPTURE DE SEQUENCE ASYNCHRONE, c’est à dire non synchronisée avec le déroulement normal du programme.

Vous voyez ici l’opposition avec les ruptures de séquences synchrones, provoquées par le programme lui-même (goto, call, btfss…).

12.2 Mécanisme général d’une interruption

Nous pouvons dire, sans nous tromper de beaucoup, qu’une routine d’interruption est un sous-programme particulier, déclenché par l’apparition d’un événement spécifique. Cela a l’air un peu ardu, mais vous allez voir que c’est très simple.

Voici donc comment cela fonctionne :

  • Le programme se déroule normalement
  • L’événement survient
  • Le programme achève l’instruction en cours de traitement
  • Le programme saute à l’adresse de traitement de l’interruption
  • Le programme traite l’interruption
  • Le programme saute à l’instruction qui suit la dernière exécutée dans le programme principal.

Il va bien sûr de soi que n’importe quel événement ne peut pas déclencher une interruption. Il faut que 2 conditions principales soient remplies :

  • L’événement en question doit figurer dans la liste des événements susceptibles de provoquer une interruption pour le processeur sur lequel on travaille
  • L’utilisateur doit avoir autoriser l’interruption, c’est à dire doit avoir signalé que l’événement en question devait générer une interruption.

Organigramme général de l’exécution d’une interruption :

Que pouvons-nous dire en voyant cet ordinogramme ? Et bien, nous pouvons déjà nous dire que le programme principal ne sait pas quand il est interrompu, il est donc crucial de lui remettre ses registres dans l’état où ils étaient avant l’interruption.

En effet, supposons que l’instruction xxx ait positionné un flag (par exemple, le bit d’indicateur Z). Si par malheur, la routine d’interruption a modifié ce bit, le programme ne pourra pas se poursuivre normalement.

Nous voyons également que l’instruction xxx termine son exécution avant de se brancher sur la routine d’interruption. Une instruction commencée n’est donc jamais interrompue.

12.3 Mécanisme d’interruption sur les PICs

Bien entendu, les PICs répondent au fonctionnement général ci-dessus, mais elles ont également leurs particularités. Voyons maintenant le principe des interruptions sur les PICs

  • Tout d’abord, l’adresse de début de toute interruption est fixe. Il s’agit toujours de l’adresse 0x04. Toute interruption provoquera le saut du programme vers cette adresse.
  • Toutes les sources d’interruption arrivant à cette adresse, si le programmeur utilise plusieurs sources d’interruptions, il lui faudra déterminer lui-même laquelle il est en train de traiter.
  • Les PICs en se connectant à cette adresse, ne sauvent rien automatiquement, hormis le contenu du PC, qui servira à connaître l’adresse du retour de l’interruption. C’est donc à l’utilisateur de se charger des sauvegardes.
  • Le contenu du PC est sauvé sur la pile interne (8 niveaux). Donc, si vous utilisez des interruption, vous ne disposez plus que de 7 niveaux d’imbrication pour vos sous-programmes. Moins si vous utilisez des sous-programmes dans vos interruption.
  • Le temps de réaction d’une interruption est calculé de la manière suivante : le cycle courant de l'instruction est terminé, le flag d'interruption est lu au début du cycle suivant. Celui-ci est achevé, puis le processeur s'arrête un cycle pour charger l'adresse 0x04 dans PC. Le processeur se connecte alors à l'adresse 0x04 où il lui faudra un cycle supplémentaire pour charger l'instruction à exécuter. Le temps mort total sera donc compris entre 3 et 4 cycles.
  • Une interruption ne peut pas être interrompue par une autre interruption. Les interruptions sont donc invalidées automatiquement lors du saut à l’adresse 0x04 par l’effacement du bit GIE (que nous allons voir).
  • Les interruptions sont remises en service automatiquement lors du retour de l’interruption. L’instruction RETFIE agit donc exactement comme l’instruction RETURN, mais elle repositionne en même temps le bit GIE.

Les interruptions sur les PICs :

 

12.4 Les sources d’interruptions de la 16F84

La 16F84 est très pauvre à ce niveau, puisqu’elle ne dispose que de 4 sources d’interruptions possibles (contre 13 pour la 16F876 par exemple). Les événements susceptibles de déclencher une interruption sont les suivants :

  • TMR0 : Débordement du timer0 (tmr0). Une fois que le contenu du tmr0 passe de 0xff à 0x00, une interruption peut être générée. Nous utiliserons ces propriétés dans le chapitre sur le timer 0.
  • EEPROM : cette interruption peut être générée lorsque l’écriture dans une case EEPROM interne est terminée. Nous verrons ce cas dans le chapitre sur l’écriture en zone eeprom.
  • RB0/INT : Une interruption peut être générée lorsque, la pin RB0, encore appelée INTerrupt pin, étant configurée en entrée, le niveau qui est appliqué est modifié. Nous allons étudier ce cas ici.
  • PORTB : De la même manière, une interruption peut être générée lors du changement d’un niveau sur une des pins RB4 à RB7. Il n’est pas possible de limiter l’interruption à une seule de ces pins. L’interruption sera effective pour les 4 pins ou pour aucune.
12.5 Les dispositifs mis en œuvre

Comment interdire ou autoriser les interruptions, comment détecter quel est l’évènenement déclencheur, et comment les gérer ? Nous allons aborder ceci d’abord de manière symbolique, pour vous aider à bien visualiser la procédure.

Imaginons un hôtel. Le groom de service représente notre programme. L’hôtel comporte 4 chambres, et chaque chambre est équipée d’un bouton-poussoir. Chaque bouton-poussoir servant à appeler le groom, est relié à une lampe à l’accueil de l’hôtel.

Chaque lampe a la possibilité de faire résonner une sonnette si l’interrupteur général de la sonnette est positionné, et si l’interrupteur particulier reliant chaque lampe à la sonnette est mis.

Voilà donc le schéma obtenu (attention, c’est un schéma symbolique, pas électrique).

Quand vous comprendrez bien ce petit schéma symboliques, vous aurez compris les interruptions. Vous voyez tout de suite qu’il y a deux méthodes pour que le groom soit prévenu.

Soit via la lampe, soit via la sonnette.

  • Pour la première méthode, le groom doit venir voir les lampes à intervalles réguliers pour vérifier si personne n’a appelé. Il doit donc venir SCRUTER le tableau de signalisation. C’est la méthode de SCRUTATION.
  • Avec la sonnette, le groom est INTERROMPU dans son travail par celle-ci. Il n’a pas besoin de venir scruter inutilement, mais il sera dérangé dans son travail par la sonnette. C’est la méthode des INTERRUPTIONS Notez déjà les points suivants :
  • Le locataire de la chambre ne peut décider quelle méthode utilisée, c’est le groom qui décide de valider ou non les sonnettes.
  • Le groom peut inhiber toutes les sonnettes en une seule fois, ou décider quelle chambre va pouvoir actionner la sonnette.
  • Les interrupteurs de validation n’agissent pas sur les lampes.
  • Si le groom est interrompu dans son travail par la sonnette, il doit de toute façon aller regarder les lampes pour voir qui a sonné. Sauf s’il sait qu’il n’a autorisé qu’une seule chambre à actionner la sonnette.
  • Ce qui n’apparaît pas sur ce schéma simplifié, mais qu’il faut savoir, c’est que, une fois la lampe allumée, elle le reste. C’est le groom qui doit l’éteindre manuellement.

Appliquons donc tout ceci en pratique sur la 16F84. Vous avez déjà compris que lampes et interrupteurs étaient, dans la 16F84, des bits de registres particuliers. Voici maintenant le registre principal de gestion des interruptions pour la 16F84.

12.6 Le registre INTCON (INTerrupt CONtrol)

Ce registre se situe à l’adresse 0x0B, dans les 2 banques. Il est donc toujours accessible. Il est détaillé figure 4-5 page 16. C’est un registre de bits, donc, chaque bit a une fonction particulière. Voici le détail de ces bits :

b7 : GIE

Global Interrupt Enable bit. Il permet de valider ou d’invalider toutes les interruptions d’une seule fois. Ce bit correspond donc à notre interrupteur de validation générale.

b6 : EEIE

Eeprom write complete Interrupt Enable bit. Ce bit permet de valider l’interruption de fin d’écriture en eeprom (nous étudierons plus tard le mécanisme d’écriture eeprom).

b5 : T0IE

Tmr0 Interrupt Enable bit : Valide l’interruption générée par le débordement du timer0.

b4 : INTE

INTerrupt pin Enable bit : Valide l’interruption dans le cas d’une modification de niveau de la pin RB0.

ATTENTION : rappelez-vous le bit 6 du registre OPTION, qui détermine quel est le sens de transition qui provoque l’interruption. On pourra donc choisir si c’est une transition 0->1 ou 1->0 qui provoque l’interruption, mais pas les deux ensemble.

b3 : RBIE

RB port change Interrupt Enable bit : Valide les interruptions si on a changement de niveau sur une des entrées RB4 à RB7.

b2 : T0IF

Tmr0 Interrupt Flag bit. C’est un Flag, donc il signale. Ici c’est le débordement du timer0

b1 : INTF

INTerrupt pin Flag bit : signale une transition sur la pin RB0 dans le sens déterminé par INTEDG du registre OPTION (b6)

b0 : RBIF

Port Interrupt Flag bit : signale qu’une des entrées RB4 à RB7 a été modifiée.

Remarque

Rappelez-vous que les flags ne se remettent pas à 0 tout seuls. C’est votre programme qui doit s’en charger, sous peine de rester indéfiniment bloqué dans une routine d’interruption. Nous dirons que ces flags sont des FLAGS REMANENTS

Remarquez déjà que tous les bits dont le nom se termine par E (Enable) sont en fait des commutateurs de validation (ce sont les interrupteurs de notre schéma symbolique). Les bits donc le nom se termine par F sont des Flags (indicateurs). Ils sont représentés par des lampes sur notre schéma symbolique. Sur celui-ci nous avons 5 interrupteurs et 4 ampoules. Or nous n’avons que 8 bits dans INTCON0. Que nous manque-t-il ?

Si vous regardez attentivement, il nous manque le bit EEIF. Mais, rassurez-vous, ce bit existe bien, il est tout simplement dans le registre EECON1, que nous verrons dans la leçon sur les accès EEPROM. Nous allons donc transformer notre schéma symbolique pour qu’il corresponde à la réalité d’une PIC16F84.

Après ces explications détaillées, trop diront les habitués des processeurs, vous devez maintenant avoir compris le fonctionnement des interruptions sur la 16F84. Analysons maintenant quelques points de la routine d’interruption en elle-même. Pour rester concrets, nous allons mélanger théorie et pratique.

 

12.7 Sauvegarde et restauration de l’environnement

Si vous regardez de nouveau l’organigramme de la routine d’interruption, vous constatez que vous devez procéder à la sauvegarde et à la restauration de l’environnement de votre programme. En quoi cela consiste-t-il ?

Et bien, comme nous l’avons déjà dit, votre programme interrompu ne sait pas qu’il l’a été, donc vous devez remettre les registres qui permettent à votre programme principal de fonctionner dans l’état où ils étaient au moment de l’interruption.

Petit exemple :

Supposons votre programme principal interrompu entre les 2 instructions suivantes :

  Movf mavariable , w ; charger mavariable et positionner Z
  Btfss STATUS , Z ; tester bit Z et sauter si vaut 1

Il est plus que probable que votre routine d’interruption va utiliser au moins une instructions qui modifie le bit Z. Vous devrez donc restaurer le registre STATUS dans l’état qu’il était avant de sortir de la routine d’interruption. Sinon le test ne se fera pas sur la valeur de mavariable, mais sur une valeur de Z modifiée par la routine d’interruption. De plus, il est à peut prêt certain que w va être modifié également, donc à sauvegarder.

12.7.1 Les registres à sauvegarder

Et bien, le PC est sauvegardé automatiquement, le programme revient donc à la bonne adresse tout seul. STATUS est, comme nous venons de voir, à restaurer par la routine d’interruption.

Si, dans la routine d’interruption et dans le programme principal, nous utilisons l’adressage indirect, nous devrons également restaurer FSR.

W sera également modifié par la routine d’interruption. S’il y a d’autres registres à sauvegarder, ce sera en fonction du fonctionnement de votre programme. Les seuls registres à sauver obligatoirement sont donc STATUS et W.

12.7.2 La méthode de sauvegarde

En voilà une bête question, allez-vous vous dire. En effet, avec un simple :

  movf STATUS , w ; charge STATUS dans W
  movwf emplacement_de_sauvegarde ; sauvegarde STATUS

Les registres semblent sauvegardés.

Et bien, analysons en détail ce qui ce passe dans ce cas. Si on analyse le fonctionnement de movf, nous constatons que le bit Z est affecté par cette opération. Donc, dans W nous n’avons plus STATUS tel qu’il était, mais déjà un status modifié. Nous ne pouvons donc pas utiliser cette procédure. Mais alors, comment faire ?

Il suffit de réfléchir : Le problème est d’amener STATUS dans le registre W SANS AFFECTER aucun bit de STATUS. Il suffit de chercher une instruction qui pourrait convenir. Pourquoi pas l’instruction swap ? Pour rappel, cette instruction inverse les 4 bits de poids faibles avec les 4 bits de poids forts de l’octet désigné .

Effectuons donc la sauvegarde de la manière suivante :

  swapf STATUS , w ; swap status avec résultat dans w sans rien modifier
  movwf status_temp ; sauver status swappé dans variable de sauvegarde

Pour restaurer, il faudra donc " reswapper " status_temp avant de le remettre dans STATUS.

Donc, nous pourrons utiliser la procédure suivante :

  swapf status_temp , w ; swap ancien status, résultat dans w
  movwf STATUS ; restaurer status

En effet, movwf ne modifie aucun bit du registre STATUS, swapf remet dans l’ordre status_temp et place le résultat dans w. constatons cependant que cette procédure à l’inconvénient de modifier à chaque fois le registre W. Nous devrons donc sauver W AVANT de sauver STATUS (sans modifier STATUS pas encore sauvé), et nous devrons restaurer W APRES avoir restauré STATUS (et de nouveau sans modifier STATUS déjà restauré)

Pour sauver W, pas de problème, l’instruction suivante :

  movwf w_temp ; sauver registre W

Sauve W sans modifier STATUS.

Par contre, pour restaurer W, nous devons charger w_temp SANS MODIFIER STATUS, il nous faut donc de nouveau utiliser swap. Seulement, nous avions sauvé w non " swappé ". Pour le restaurer, nous ne devons pas le " swapper ", ce qui peut aussi se traduire par le " swapper " 2 fois. Nous pouvons donc écrire :

  swapf w_temp,f ; Swapper w_temp sans modifier Z
  swapf w_temp,w ;Swapper une deuxième fois w_temp, résultat dans w, STATUS
      ; non affecté

La première instruction " swappe " l’ancienne valeur de w et place le résultat dans l’emplacement de sauvegarde. La seconde instruction " swappe " une seconde fois cette valeur, et place le résultat dans W. Nous avons donc rechargé W sans modifier STATUS.

Voici donc la structure de base d’une routine d’interruption :

;**********************************************************************
; ROUTINE INTERRUPTION
;**********************************************************************
       
    ;sauvegarder registres
    ;--------------------------
  org 0x004 ; adresse d'interruption
  movwf w_temp ; sauver registre W
  swapf STATUS , w ; swap status avec résultat dans w
  movwf status_temp ; sauver status swappé
     
    ; switch vers différentes interrupts
    ; ----------------------------------------
; ici, on teste éventuellement de quelle interruption il s’agit
       
    ; traitement des interruptions
    ; ----------------------------------
; ici, on peut traiter interruption puis effacer son flag
       
    ;restaurer registres
    ;----------------------
  swapf status_temp,w ; swap ancien status, résultat dans w
  movwf STATUS ; restaurer status
  swapf w_temp,f ; Inversion L et H de l'ancien W
      ; sans modifier Z
  swapf w_temp,w ; Ré-inversion de L et H dans W
      ; W restauré sans modifier status
  retfie   ; return from interrupt

Et voilà votre squelette de routine d’interruption : vraiment pas compliqué, n’est-ce pas ? Il n’y a plus qu’à compléter, car le fichier m16F84.asm contient déjà les routines de sauvegarde et restauration (que nous venons d’expliquer) et les tests de type d’interruption (que nous allons détailler).

Comme je suis partisan de la programmation structurée, la routine de switch branche en réalité sur des sous-programmes séparés. Rien n’empêche en effet d’utiliser des sous-programmes dans une routine d’interruption.

12.7.3 Particulatité de l’instruction " RETFIE "

A ce niveau de l’exposé, une remarque pertinente serait la suivante :

Pourquoi existe-t-il une instruction RETFIE, alors qu’on pourrait utiliser RETURN ?

Et bien, vous ne pouvez pas interrompre une interruption par une autre. Si c’était le cas, les sauvegardes des registres W et STATUS seraient " écrasé " par une seconde opération (mais c’est possible sur d’autres processeurs). Donc, dès que le programme est branché sur l’interruption, le bit GIE est mis à 0 automatiquement.

Pour qu’une nouvelle interruption puisse avoir lieu une fois celle en cours terminée, il faut remettre GIE à 1. Ceci est exécuté automatiquement par RETFIE.

Vous allez alors me dire : et si je fais ceci ?

  bsf INTCON , GIE ; remettre GIE à 1
  RETURN   ; et sortir de la routine d’interruption

Et bien, c’est exactement ce que fait RETFIE, mais à un détail près, et ce détail est de la plus grande importance : RETFIE est une seule et même instruction, donc ne peut pas être interrompue par une interruption.

Dans le cas où les 2 instructions précédentes seraient utilisées, une fois GIE mis à un, si un des flags d’interruption est toujours à 1 (autre interruption, où la même qui se reproduit une autre fois), le programme se reconnecterait sur la routine d’interruption avant d’avoir exécuté le RETURN, donc avant d’avoir restauré le PC.

Celui-ci continuerait à occuper un emplacement sur la pile. En sachant que la pile est limitée à 8 emplacements, il y aurait de nombreuses chances de plantage du programme par débordement de la pile.

 

Index cours
Index du cours
Chapitre précédent
11. Réalisation d’un programme embarqué (Suite)
Chapitre suivant
12. Les interruptions (Suite 1)

 


Complétez cette page, posez vos questions et remarques ici : WiKiFri

Page http://fribotte.free.fr/bdtech/cours/pic16f84/PART1_cours12a.html modifiée le 14/10/2002.
Copyright fribotte@free.fr, libre de droit pour toute utilisation non commerciale.
Reproduction autorisée par simple mail