Dernière mise à jour :
29/07/2009
Présentation
Les interruptions dans le domaine des microcontrôleurs ont pour moi
bien longtemps constitué un grand
mystère. Je comprenais que quelque chose se passait quand un
évenement "externe" survenait, mais je ne comprenais pas comment
exploiter cette information "subite". Il m'a fallu lire et
décortiquer pas mal d'exemples pour en comprendre le principe
général de fonctionnement. J'ai ainsi découvert
qu'il existait des interruptions qui annonçaient un changement
d'état sur une ligne d'entrée (un front montant par
exemple), et des interruptions qui annonçaient le
débordement d'un timer (réinitialisation d'un compteur
après qu'il ait atteint sa limite de comptage maximale). Dans le
premier cas, l'évenement est externe au PIC, il s'agit par
exemple d'un état logique qui change sur une broche
d'entrée / sortie configurée en entrée, et dans le
second cas l'évènement est interne, il se produit
à un instant donné dans le PIC lui-même.
Points communs...
Dans MikroPascal, il existe une routine spécifique pour la
gestion des interruptions, qui s'appelle "Interrupt" et qui est unique.
Elle peut être utilisée quelque soit le type
d'interruption, c'est à dire quelque soit la cause
(l'évènement) qui a crée l'interruption.
program electronique_pic_tuto_base_interrupt_001a;
procedure Interrupt;
begin
// action à exécuter lorsque l'interruption survient
end;
// main program
begin
Init;
while true do
begin
// code à exécuter "hors interruptions"
end;
end.
Comme plusieurs causes peuvent conduire à l'exécution de
cette unique routine Interrupt, il convient, dans cette dernière
même, de faire une "recherche sur la cause". Cela se fait
simplement en analysant la valeur de "drapeaux" conçus pour cet
usage et qui par leur valeur, indiquent si tel ou tel évenement
est la cause de l'interruption. Prenons un exemple concret, basé
sur le PIC 16F628A qui comporte plusieurs sources d'interruption. Ce
microcontrôleur peut déclancher une interruption :
- quand une des broches du port B - configurées en entrées - change
d'état logique;
- quand les timers internes TMR0 et TMR1 "débordent" (c'est
à dire quand ils ont atteint leur compte maximal et qu'ils
repassent à zéro);
- quand la sortie du comparateur interne change d'état;
- quand l'USART (module de réception de données série type RS232 ou
MIDI) reçoit des données;
- et dans d'autres cas...
Pour que ces interruptions puissent déclancher la routine
Interrupt, il faut les activer et savoir où aller lire leur
cause. En d'autres termes, indiquer au microcontrôleur qu'on
souhaite être informé quand elles surviennent. Pour cela,
il existe plusieurs registres comportant plusieurs bits affectés
à cette tâche : certains bits sont spécialement
destinés à l'activation des interruptions, et d'autres
bits sont destinés à stocker l'information qui dit que
telle interruption a été provoquée. Par exemple,
le timer TMR0 est capable de déclancher une interruption, si le
bit qui permet de l'activer est correctement configuré. Ce bit
est le bit N° 5 du registre INTCON, appelé aussi T0IE (
Timer
0 Interrupt
Enable).
S'il est configuré à 1, l'interruption du timer 0 est
activée, et s'il est configuré à 0, l'interruption
du timer 0 est désactivée. En plus de ce bit d'activation
T0IE, un autre bit est disponible, qui stocke le statut de
l'interruption, il s'agit du bit N° 2 du même registre
INTCON, appelé T0IF (
Timer
0 Interrupt
Flag).
Si la valeur de T0IF est à 0, cela signifie que le timer 0 n'a
pas déclanché d'interruption. Et si la valeur de T0IF est
à 1, cela signifie que le timer 0 a déclanché une
interruption. Quand la routine Interrupt survient, il convient dans un
premier temps de vérifier le statut des interruptions
potentielles, c'est ce que montre l'exemple suivant.
procedure Interrupt;
begin
if INTCON.T0IF = 1 then
begin
// action à exécuter lorsque l'interruption du timer 0 survient
INTCON.T0IF := 0; // remise à zéro du statut de l'interruption
end;
end;
Remarques
- le bit du registre qui contient le statut de l'interruption (T0IF
pour le timer 0) doit être remis à zéro par
logiciel, de préférence dans la routine Interrupt
même. Si cela n'est pas fait, aucune autre interruption de
même type ne pourra avoir lieu par la suite.
- si un seul vecteur d'interruption est activé, il peut sembler
inutile de chercher la cause du déclenchement de la routine
Interrupt, puisque quand elle est déclanchée on en
connait forcement la cause. Mais c'est un bon réflexe de
vérifier l'origine de l'interruption, ne serait-ce que pour le
cas où une autre est activée par inadvertance (je sais,
ça ne peut pas arriver).
Si maintenant deux types d'interruptions sont
déclanchées, par exemple celle du timer 0 et celle du
changement d'état des entrées du port B, voici comment il
faut procéder au niveau de la routine Interrupt.
procedure Interrupt;
begin
if INTCON.T0IF = 1 then
begin
// action N° 1, à exécuter lorsque l'interruption du timer 0 survient
INTCON.T0IF := 0; // remise à zéro du statut de l'interruption
end;
if INTCON.RBIF = 1 then
begin
// action N° 2, à exécuter lorsque l'interruption des entrées du port B survient
INTCON.RBIF := 0; // remise à zéro du statut de l'interruption
end;
end;
Et ne pas
oublier, dans
l'initialisation principale du logiciel, d'activer les interruptions
que l'on souhaite surveiller, car dans le cas contraire on risque
d'attendre longtemps l'appel de la routine Interrupt !
program electronique_pic_tuto_base_interrupt_001b;
procedure Init;
begin
CMCON := %00000111; // comparators OFF
TRISIO.0 := 0; // GPIO configuré en sortie
ANSEL.ANS0 := 0; // GPIO configuré en sortie numérique
INTCON.GIE := 1; // activation générale (globale) des interruptions
INTCON.T0IE := 1; // activation interruption timer 0
INTCON.RBIE := 1; // activation interruption entrées port B
end;
procedure Interrupt;
begin
if INTCON.T0IF = 1 then
begin
// action N° 1, à exécuter lorsque l'interruption du timer 0 survient
INTCON.T0IF := 0; // remise à zéro du statut de l'interruption
end;
if INTCON.RBIF = 1 then
begin
// action N° 2, à exécuter lorsque l'interruption des entrées du port B survient
INTCON.RBIF := 0; // remise à zéro du statut de l'interruption
end;
end;
// main program
begin
Init;
while true do
begin
// code à exécuter "hors interruptions"
end;
end.
-