12.14 Passage au simulateur dune routine dinterruption |
Petit rappel, il faut commencer par sélectionner la mise en service du
simulateur : Allez dans le menu " project " et sélectionnez
" edit project ". Dans la fenêtre qui souvre, sélectionnez
" change " à côté du " development mode ". et
cliquez sur la case " MPLAB-SIM 16F84 ". Répondez oui à toutes les
questions qui pourraient vous être posées.
Dans le menu " windows ", sélectionnons maintenant
laffichage des registre spéciaux (special function registers). Plaçons cette
fenêtre sur le côté.
Lançons la compilation avec <F10>. Ensuite pressez <F6> pour faire le
reset du programme, puis <F7>. Répétez <F7> en suivant lévolution du
programme. Noubliez pas quil va effectuer 68 boucles pour effacer la RAM,
soyez patient. Profitez-en pour observer le comportement de FSR pour ladressage
indirect.
Une fois arrivé dans le programme principal, le programme boucle indéfiniment. En
effet, un événement extérieur (bouton-poussoir) est nécessaire pour provoquer le
passage dans la routine dinterruption. Je vais maintenant vous expliquer comment
simuler un événement extérieur.
Allez dans le menu " debug ", choisissez " Simulator
stimulus ->Asynchronous stimulus ". Une petite fenêtre souvre avec 12
événements configurables. Cette fenêtre se place toujours en avant-plan. Rangez-la à
gauche de votre éditeur, sans cacher les registres spéciaux. Prenez garde pour
linstant de ne pas cliquer sur un bouton tant que je ne vous le signalerai pas.
Placez-vous sur le bouton <Stim 1 (P) > et cliquez avec le bouton de droite.
Vous avez un menu déroulant qui apparaît. Sélectionnez " assign
pin ". Choisissez ensuite la pin que vous voulez stimuler. Dans notre cas, RB0.
Votre bouton est maintenant transformé en <RB0 (P)>.
Si vous regardez encore avec le bouton de droite, vous voyez que vous avez accès au
mode de fonctionnement du bouton. Vous avez le choix entre Pulse (génère une impulsion),
Low (place un niveau 0), High (place un niveau 1), ou Toggle (inversion du niveau à
chaque pression). Nous choisirons la moins pratique pour cet exemple, mais la plus
explicite.
Choisissez donc " Low ". Votre bouton est maintenant < RB0
(L) >. Créez un second bouton à côté du premier en sélectionnant RB0 et High.
Vous disposez maintenant de 2 boutons. Un place RB0 au niveau 1 (donc bouton-poussoir
relâché), lautre le place sur 0, donc bouton-poussoir pressé.
Notez, mais ne le faite pas maintenant, que si vous pressez deux fois sur un bouton
avec le bouton de gauche, vous obtenez le message suivant :
Ceci signifie que vous ne pouvez évidemment exécuter quune stimulation par
cycle. Vous êtes donc obligé après chaque pression sur un bouton de stimulation, de
revenir sur la fenêtre de léditeur et davancer dune instruction avec
<F7> avant de pouvoir exercer un nouveau stimuli.
Examinons le registre PORTB dans la fenêtre daffichage des registres spéciaux.
Vous voyez que tous les bits sont des 0. En effet, MPLAB ne peut pas connaître
lélectronique que vous avez connecté sur ses pins. Cest donc à vous de lui
indiquer le niveau que vous êtes sensé avoir sur les dites pins. Pour ceux qui
nauraient pas compris le fonctionnement de la résistance de rappel, voici le
schéma équivalant :
Nous voyons donc que, quand le bouton-poussoir nest pas pressé, nous avons un
niveau 1 sur RB0 provoqué par la résistance de rappel que nous avons mise en service.
Pressons donc le bouton <RB0 (H)> une seule fois, et allons dans léditeur.
Pressons <F7> pour avancer dun pas et valider la modification de niveau.
Examinez PORTB : RB0 est maintenant passé à 1. Notre bouton-poussoir nest pas
enfoncé. Pressez quelques fois <F7> pour vérifier que rien dautre ne
sest passé.
Nous allons maintenant simuler la pression du bouton-poussoir. Pressez le bouton
<RB0 (L)> pour envoyer 0 sur RB0. Revenez dans léditeur et pressez une seule
fois sur <F7>. Linstruction qui suit lévénement est alors
linstruction située à ladresse 0x04, car le passage de 1 à 0 sur RB0 a
provoqué notre interruption.
Avancez lentement par pressions de <F7> dans la routine dinterruption.
Examinez leffet des différentes instructions vues. Une fois la ligne :
|
xorwf |
PORTA , f |
; inverser RA2 |
exécutée, vous constatez que la LED sest allumée (RA2 est passé à 1
sur le registre PORTA). Avancez lentement jusquà ce que la ligne :
|
retfie |
|
; return from interrupt |
soit sélectionnée et ne pressez plus <F7>. A cet endroit, nous trouvons
le retour de la routine dinterruption vers le programme principal. Pressez une
nouvelle fois <F7>.
Que se passe-t-il ? Au lieu de revenir au programme principal, nous recommençons
une nouvelle interruption.
Pour provoquer une interruption, il faut que le bit Enable ET le bit Flag dune
des sources dinterruptions soient à 1. Or, il ny a quun seul bit
Enable à 1, et cest INTE.
Examinons donc INTF (cest le bit 1 de INTCON). Ce bit est toujours à 1, donc
nouvelle interruption. Nous avons donc commis une erreur classique. Nous avons oublié
deffacer le flag à la fin du traitement de notre interruption. Remarquez que cet
effacement est intégré dans la partie " switch " de la routine
dinterruption du fichier m16f84.asm. Nous avons effacé cette ligne par mégarde en
supprimant les différents tests.
12.15 Première correction : reset du flag |
Il nous faut donc ajouter la ligne suivante dans notre sous-routine intrb0
|
bcf |
INTCON , INTF |
; effacer flag INT/RB0 |
Nous obtenons donc :
|
movlw |
B'00000100' |
; bit positionné = bit à inverser |
|
BANK0 |
|
; car on ne sait pas sur quelle banque |
|
|
|
; on est dans une interruption (le |
|
|
|
; programme principal peut avoir changé |
|
|
|
; de banque. Ce n'est pas le cas ici, |
|
|
|
; mais c'est une sage précaution |
|
xorwf |
PORTA , f |
; inverser RA2 |
|
bcf |
INTCON , INTF |
; effacer flag INT/RB0 |
|
return |
|
; fin d'interruption RB0/INT |
Recompilons notre programme avec <F10>, puis <F6>, et enfin, recommencez
toute la procédure que nous venons de voir.
Vérifiez que la routine dinterruption se termine maintenant en rendant la main
au programme principal. La LED 1 est maintenant allumée (RA2 = 1). Pressez <RB0(H)>
pour simuler le relâchement du bouton-poussoir. Pressez quelques fois <F7> et
pressez <RB0 (L)> pour simuler une seconde pression de bouton-poussoir. Suivez la
routine dinterruption et constatez que cette fois la LED séteint.
On obtient donc au simulateur le fonctionnement suivant :
- Une pression sur le B.P. (bouton-poussoir) allume la LED
- Une autre pression éteint la LED
- Et ainsi de suite.
Nous avons donc obtenu le résultat souhaité.
Placez votre PIC dans le programmateur, et envoyez lui le fichier
" Myinter.hex " [ NDLR : Fichiers
exemples ici ]. Placez votre PIC sur la platine dessais (modifiée)
et pressez le B.P. à plusieurs reprises. La LED ne fonctionne pas du tout comme prévu,
Elle réagit, mais de manière aléatoire. Que se passe-t-il ?
12.16 Se mettre à léchelle de temps de la PIC |
Et bien, cest tout simple. Les PICs sont des composants très rapides. Ils ne
travaillent pas à la même échelle de temps que nous. Essayons de nous transformer en
PIC. Nous voyons alors un énorme Bouton poussoir.
Lorsque ce B.P. est pressé, cest alors une énorme barre qui vient
court-circuiter 2 contacts. Cette barre est élastique. Que voit la PIC ? Elle
voit une énorme barre flexible qui tombe dune énorme hauteur sur 2 contacts
métalliques. Une fois la barre en contact, elle REBONDIT plusieurs fois. A chaque
pression sur le B.P., la PIC voit donc une série de fermeture et douverture du
B.P., au lieu dune seule pour nous.
La PIC est donc plus rapide que notre B.P. Noubliez donc jamais que les PICs ne
travaillent pas à une échelle de temps humaine. Vous devez en tenir compte.
12.17 Le problème de lanti-rebond |
Comment remédier à ce problème ? Et bien, tout simplement en attendant un temps
supérieur au temps de rebondissement avant dautoriser une nouvelle interruption sur
RB0. Nous allons utiliser nos connaissances actuelles pour résoudre ce problème. Il
devient utile de dessiner un ordinogramme de ce que nous voulons faire :
Explications
Le programme principal effectue normalement ses initialisations, puis teste si une
demande de tempo a été introduite en positionnant le flag " tempo ".
Si le flag nest pas mis, il boucle sans fin.
Si le B.P. est pressé, une interruption est générée, RA2 est inversé, la routine
dinterruption positionne le flag tempo à 1 et interdit toute nouvelle interruption
de RB0. Toute autre action sur RB0 sera donc sans effet (donc les rebonds ne sont pas pris
en compte).
La routine dinterruption prend fin. Retour au programme principal, qui continue
alors à tester le flag tempo. Celui-ci vient dêtre positionné par la routine
dinterruption. Le programme principale appelle alors une routine de tempo (que nous
avons déjà vue dans la leçon principale).
Après écoulement du temps nécessaire à la fin des rebonds, le flag tempo est
annulé (pour ne pas boucler sans fin), et les interruptions sont à nouveau autorisées,
afin de permettre de prendre en compte une nouvelle pression sur le B.P.
Vous voyez donc ici quil peut être utile dans un programme dinterrompre et
de relancer les interruptions à certains moment spécifiques.
12.18 Finalisation du programme |
Tout dabord, il nous faut une routine de temporisation. Ouvrez le fichier
" Led_cli.asm " et effectuez un copier/coller de la routine de
temporisation. [ NDLR : Fichiers exemples ici
]
;********************************************************************* |
; SOUS-ROUTINE DE TEMPORISATION |
;********************************************************************* |
;--------------------------------------------------------------------------------------------------------
|
; Cette sous-routine introduit un retard de 500.000 µs. |
; Elle ne reçoit aucun paramètre et n'en retourne aucun |
;--------------------------------------------------------------------------------------------------------
|
tempo |
|
|
|
|
movlw |
2 |
; pour 2 boucles |
|
movwf |
cmpt3 |
; initialiser compteur3 |
boucle3 |
|
|
|
|
clrf |
cmpt2 |
; effacer compteur2 |
boucle2 |
|
|
|
|
clrf |
cmpt1 |
; effacer compteur1 |
boucle1 |
|
|
|
|
nop |
|
; perdre 1 cycle |
|
decfsz |
cmpt1 , f |
; décrémenter compteur1 |
|
goto |
boucle1 |
; si pas 0, boucler |
|
decfsz |
cmpt2 , f |
; si 0, décrémenter compteur 2 |
|
goto |
boucle2 |
; si cmpt2 pas 0, recommencer boucle1 |
|
decfsz |
cmpt3 , f |
; si 0, décrémenter compteur 3 |
|
goto |
boucle3 |
; si cmpt3 pas 0, recommencer boucle2 |
|
return |
|
; retour de la sous-routine |
Nous allons modifier légèrement cette sous-routine. Nous pouvons enlever la boucle
extérieure, car, 500ms cest beaucoup plus que le temps de rebond du B.P. Enlevons
également linstruction nop.
Nous obtenons :
;********************************************************************* |
; SOUS-ROUTINE DE TEMPORISATION |
;********************************************************************* |
;--------------------------------------------------------------------------------------------------------
|
; Cette sous-routine introduit un retard |
; Elle ne reçoit aucun paramètre et n'en retourne aucun |
;--------------------------------------------------------------------------------------------------------
|
tempo |
|
|
|
|
clrf |
cmpt2 |
; effacer compteur2 |
boucle2 |
|
|
|
|
clrf |
cmpt1 |
; effacer compteur1 |
boucle1 |
|
|
|
|
decfsz |
cmpt1 , f |
; décrémenter compteur1 |
|
goto |
boucle1 |
; si pas 0, boucler |
|
decfsz |
cmpt2 , f |
; si 0, décrémenter compteur 2 |
|
goto |
boucle2 |
; si cmpt2 pas 0, recommencer boucle1 |
|
return |
|
; retour de la sous-routine |
Nous ne nous sommes donc pas servi de la variable cmpt3. Nous pouvons donc la supprimer
de notre zone des variables. Tant que nous y sommes, nous allons avoir besoin dun
flag, cest à dire dun bit. Créons-le dans cette zone.
;********************************************************************* |
; DECLARATIONS DE VARIABLES |
;********************************************************************* |
|
|
|
|
|
CBLOCK |
0x00C |
; début de la zone variables |
|
|
w_temp : 1 |
; Sauvegarde de W dans interruption |
|
|
status_temp : 1 |
; Sauvegarde de STATUS dans interrupt |
|
|
cmpt1 : 1 |
; compteur de boucles 1 dans tempo |
|
|
cmpt2 : 1 |
; compteur de boucles 2 dans tempo |
|
|
flags : 1 |
; un octet pour 8 flags |
|
|
|
; réservons b0 pour le flag tempo |
|
|
|
; b1 : libre |
|
|
|
; b2 : libre |
|
|
|
; b3 : libre |
|
|
|
; b4 : libre |
|
|
|
; b5 : libre |
|
|
|
; b6 : libre |
|
|
|
; b7 : libre |
|
|
|
|
|
ENDC |
|
; Fin de la zone |
|
|
|
|
#DEFINE |
tempoF |
flags , 0 |
; Définition du flag tempo |
Modifions notre programme principal en suivant notre ordinogramme. Nous obtenons :
;********************************************************************* |
; PROGRAMME PRINCIPAL |
;********************************************************************* |
start |
|
|
|
|
btfss |
tempoF |
; tester si tempo flag mis |
|
goto |
start |
; non, attendre qu'il soit mis |
|
call |
tempo |
; oui, exécuter tempo |
|
bcf |
tempoF |
; effacer flag tempo |
|
bsf |
INTCON , INTE |
; remettre interrupts INT en service |
|
goto |
start |
; boucler |
|
|
|
|
|
END |
|
; directive fin de programme |
Il ne reste plus quà modifier notre routine dinterruption en fonction de
notre ordinogramme. Nous obtenons :
;**********************************************************************
|
; INTERRUPTION RB0/INT |
;**********************************************************************
|
;--------------------------------------------------------------------------------------------------------- |
; inverse le niveau de RA2 à chaque passage |
; interdit toute nouvelle interruption |
; valide le flag tempo |
;--------------------------------------------------------------------------------------------------------- |
intrb0 |
|
|
|
|
movlw |
B'00000100' |
; bit positionné = bit à inverser |
|
BANK0 |
|
; car on ne sait pas sur quelle banque |
|
|
|
; on est dans une interruption (le |
|
|
|
; programme principal peut avoir changé |
|
|
|
; de banque. Ce n'est pas le cas ici, |
|
|
|
; mais c'est une sage précaution |
|
xorwf |
PORTA , f |
; inverser RA2 |
|
bcf |
INTCON , INTF |
; effacer flag INT/RB0 |
|
bcf |
INTCON , INTE |
; interdire autre inter. RB0 |
|
bsf |
tempoF |
; positionner flag tempo |
|
return |
|
; fin d'interruption RB0/INT |
Compilons notre programme et chargeons le nouveau fichier .hex dans notre PIC.
Lançons lalimentation. Cela ne fonctionne toujours pas, pourquoi ? Et bien
réfléchissons à ce qui se passe.
Une fois la routine dinterruption terminée, les interruptions sont invalidées.
Linterrupteur rebondi sans causer dappel dinterruption. MAIS SON FLAG ET
POSITIONNE à cause des rebonds. Souvenez-vous que les bits Enable nagissent pas sur
les flags. Donc, dès que le programme principal remet linterruption en service, une
interruption est générée directement, INTF ayant été positionné avant.
Vous devez donc penser à tout lorsque vous utilisez les interruptions. Si un programme
fonctionne au simulateur et pas sur le circuit réel, commencez par soupçonnez des
problèmes de timing ?
Il nous suffit donc dajouter une le reset du flag INTF avant de remettre les
interruptions INT en service.
|
bcf |
INTCON , INTF |
; effacer flag INT |
Recompilez votre programme, et rechargez-le dans votre PIC. Alimentez votre
montage.
Cette fois cela fonctionne parfaitement. Voici un montage très pratique à utiliser.
Si vous remplacez la LED par un petit relais ou un triac optocouplé, vous voilà en
possession dun télérupteur. Placez un bouton-poussoir dans chaque endroit
doù vous désirez allumer la lampe, et vous pouvez allumer ou éteindre celle-ci
depuis plusieurs endroits, sans utiliser dinterrupteurs spéciaux avec un tas de
fils à tirer.
A titre de bonus, voici le schéma dun télérupteur fonctionnant avec votre
programme.
Remarque sur notre programme
Nous avons utilisé une temporisation de valeur quelconque. Cette temporisation inhibe
toute action sur le B.P. (cest son but) durant +- 250mS. Donc, vous pourrez presser
au grand maximum 4 fois sur le B.P. par seconde.
Mais quelle est la durée réelle des rebonds ? Et bien, elle dépend de
linterrupteur utilisé, technologie et taille. Pour connaître le temps de rebond de
votre propre interrupteur, diminuez progressivement la durée de la temporisation. Une
fois que votre télérupteur ne fonctionne plus à chaque pression, vous avez atteint la
limite. Calculez alors la durée de votre temporisation, vous aurez la durée
approximative des rebonds.
12.19 Remarques importantes |
Souvenez-vous, quune fois que vous utilisez les interruptions dans un programme,
vous ne pouvez jamais savoir le temps qui va séparer 2 instructions successives.
En effet, entre les instructions en question peut avoir été traité une ou plusieurs
routine(s) dinterruption.
En conséquence, VOUS NE POUVEZ PAS UTILISER de calcul de temps en utilisant le calcul
du nombre dinstructions dans toute partie de code dans lequel une interruption
risque de survenir.
Notre sous-routine de temporisation de " Led-cli ", par exemple,
donnerai un délai qui risquerait dêtre allongé. Dans ce programme, cependant, les
interruptions ne sont plus en service au moment de lexécution de cette
temporisation. Voyez lordinogramme.
De plus, pour toute séquence dont le déroulement en temps est critique et ne peut
être interrompu, vous devez inhiber les interruptions. A votre charge de les remettre en
service en temps utile.
Une fois que vous utilisez les interruptions, votre programme devra affronter des
événements asynchrones avec son déroulement. Donc, vous ne pourrez JAMAIS tester toutes
les éventualités possibles. VOUS NE POUVEZ DONC JAMAIS ETRE SUR PAR SIMULATION QUE VOTRE
PROGRAMME EST COMPLETEMENT DEBUGGE.
Ceci vous explique pourquoi des gestions de processus critiques en temps réel
utilisent plusieurs ordinateurs (navette spatiale). Ces ordinateurs étant
désynchronisés, un bug de cette nature qui apparaît sur un deux a peu de chance
de se produire simultanément sur un second. Pour savoir lequel a posé problème, il faut
donc un troisième ordinateur.
Pensez toujours à ceci si vous êtes amenés un jour à réaliser un programme dont
dépend la sécurité de personnes ou de biens.
Au terme de ce chapitre, vous pouvez appréhender les mécanismes dinterruption
et leur mise en uvre. Nous utiliserons encore cette possibilité dans la leçon sur
le timer.
Jespère avoir démystifié ce concept, trop souvent imaginé comme
" la méthode des pros ". En réalité, une fois de plus, pas de
magie. Ce nest quune exploitation, certes parfois complexe, de processus
simples et faciles à appréhender.
Gardez seulement à lesprit que vous quittez le monde du synchrone pour entrer
dans le monde de lasynchrone, beaucoup plus difficile à simuler totalement de
façon efficace. Lutilisation des interruptions impose de ce fait la parfaite
compréhension des mécanismes mis en place, et vous oblige à envisager toutes les
possibilités au niveau de la survenance dun événement extérieur. Ceci est
surtout vrai pour les programmes avec plusieurs sources dinterruptions.
|