Dernière mise à jour le 29/09/16

Les interruptions PIC24/PIC32


La lecture qui va suivre permet de nous intéresser sur les interruptions issu d’un microcontrôleur et de prendre plusieurs exemples afin de voir et comprendre le fonctionnement des ces fameuses interruptions. Les interruptions d’un PIC de la famille 12F/16F/18F… est différents d’un PIC de la famille 24 ou 32 puisque ce dernier dispose de 5 timers qui peuvent être réglés soit sur 16 bits ou soit 32 bits. Pour comprendre en détails j’ai réalisés plusieurs exemples ci-dessous, ou je vais utiliser dans un 1er temps le PIC24FJ16GA002 de chez Microchip qui sera une simulation via Proteus, puis dans un 2ème temps je vais utiliser sur une platine d’essais sans soudure le PIC32MX534F064H de chez MikroElectronika vendu sous le nom de MINI-32.

Bon maintenant assez discuté et rentrons désormais dans le vif du sujet !!

Appel de la routine iv IVT_ADDR_TXINTERRUPT
Pour lancer une interruption il faut avant tout appeler la routine nommée« iv IVT_ADDR_TXINTERRUPT » (X à remplacer par le timer désiré).
Comme vous pouvez le constater, il faut utiliser une structure qui est bien différentes des PIC12F/16F/18F. La structure du programme sera de cette façon:


procedure TimerX; // procedure nommée Timer1
iv IVT_ADDR_TXINTERRUPT; // Appel de l’interruption du TimerX
// (X sera remplacé par le timer désiré)
begin
// Programme de l’interruption
end;
begin
while true do
begin
// programme principal
end;
end.

J’ai donné un nom pour la procédure « TimerX » mais rien ne vous empêche de l’appeler autrement. Sous ce nom s’enchaine immédiatement la routine iv IVT_ADDR_TXINTERRUPT qui fait appel à l’interruption du timerX. Ensuite entre « begin » le début du programme et « end » la fin du programme il faudra bien évidemment écrire une ligne de code qui s’exécutera uniquement lorsque l’interruption sera appelée.En ce qui concerne le programme principal, celui-ci est exécuté lorsque l’interruption a terminée mais nous verrons plus loin comme une interruption commence et se termine.

Remarque :
Pour faire appel à l’interruption d’un autre Timers (Timer2/Timer3/Timer4/Timer5)
grâce à la routine :
iv IVT_ADDR_TXINTERRUPT, il suffit de remplacer « X » par le numéro du timer désiré comme dis précédemment
Voilà en ce qui concerne l’appel de l’interruption pas compliqué n’est-ce pas ?

Configurations TimerX


Nous venons de voir comment appeler une procédure d’interruption ainsi que la structure du programme mais ce n’est pas pour autant que le PIC va fonctionner … Il faut pour que ce microcontrôleur fonctionne, le programmer et l’initialiser lors de sa première mise sous tension.

TXCON
Le bit TXCON permet de réaliser beaucoup de chose (à vous de consulter la datasheet). (X sera remplacé par le timer désiré). Le Quartz oscille à une fréquence de 10Mhz et que cette oscillation est présente sur les broches OSCI et OSCO, cette oscillation n’est plus la même une fois arrivée à «l’intérieur» du PIC. En effet l’oscillation en interne est divisée par 2 (Fosc/2) soit deux fois moins vite. Dans les exemples qui vont suivres le prescaler sera réglé à 1:64, il en résulte que la durée pour chaque coups d’horloge est d’environ de 840 ms ce qui proche de la seconde et largement suffisant pour observer le clignotements des leds.

IEC0.TXIE ou IEC1.TXIE
Ce bit quant à lui permet d’activer les interruptions du timerX (cas contraire rien ne fonctionnera)

IFS0.TXIF ou IFS1.TXIF
Tout d’abord le bit « IFS0.TXIF ou IFS1.TXIF » (X sera remplacé par le timer désiré) permet lorsque le timer à débordé c’est-à-dire une fois que le timer est arrivé au bout de son comptage (soit sur 16 bits soit sur 32 bits), une interruption sera déclenchée et, celle-ci permettra de faire passer le drapeau « Flag » à l’état logique « 1 ». Une fois ce drapeau à l’état logique « 1 » le programme principal est stoppé laissant place à l’interruption. Afin que le programme principal s’exécute correctement dès la mise sous tension du microcontroleur, il faut pour cela initialiser le bit IFS0.TXIF à l’état logique « 0 ».

TMRX
Ce bit permet de régler et d’affiner une valeur comprise entre 0 et 65535 pour un comptage sur 16 bits ou bien de 0 à 4294967295 pour un comptage sur 32 bits (X sera remplacé par le timer désiré). Le drapeau qui déclenche l’interruption, qu’il soit à l’état logique “1” ou l’état logique “0” n’aura aucune influence sur le comptage du ou des timers configurés.

Exemple Timer1


1er-exemple
Fonctionnement
Nous avons évoqué précédemment que les coups d’horloge étaient d’environ 840ms. Chaque fois que l’horloge arrivera à un temps de 840ms le drapeau via le bit IFS0.T1IF passera à l’état logique «1 » ce qui va interrompre la programme principal qui celui-ci ne fait que de scruter en permanence la procédure « Reset_LATA_timer1 » cette procédure ne fait que de désactiver la led. La subtilité d’une interruption fait que pendant 840ms le programme principal est scruté et à la fin des 840ms celui-ci est stoppé laissant place à l’interruption jusqu’à ce que le bit IFS0.T1IF soit remis à l’état logique “0” pour permettre au programme principal d’être de nouveau scruté. Je vous laisse réfléchir sur le code ci-dessous.


program Exemple_1_Timer1;
{ Declarations section }

procedure init;
begin
TRISA:=$0000;
LATA:=$0000;
AD1PCFG := $FFFF; // permet d’activer toutes les entrées en numérique
IFS0.T1IF := 0; // mise à zéro du bit T1IF
IEC0.T1IE := 1; // activation des interruptions du Timer1
T1CON :=%1000000000100000; // Timer1 ON, prescaler 1:64
TMR1:=$7FFF; // on commence à compterà parti de zéro
IPC0:=$7000;
IPC1:=$7000;
end;

procedure Timer1;
iv IVT_ADDR_T1INTERRUPT;
begin
if IFS0.T1IF = 1 then
begin
LATA.0:=1;
delay_ms(419);
IFS0.T1IF := 0; // remise à zéro du drapeau
end;
end;

procedure Reset_LATA_timer1;
begin
LATA.0:=0;
end;

// Programme principal
begin
init;
while true do
begin
Reset_LATA_timer1;
end;
end.

Exemple Timer1/Timer2


exemple_2

Passons maintenant à l’exemple suivant ou nous allons utiliser deux timers, (Timer1/Timer2). Avec l’aide des ces 2 timers le but est de faire clignoter alternativement les leds (D1 et D2). Au départ dans la procédure init du programme, Le Timer1 (TMR1) a été initialisé à $7FFF (Hex) soit 32767 (Dec). En ce qui concerne le Timer2 (TMR2) lui a été initialisé à $0000 (Hex) soit 0 (Dec). Le timer1 à donc une longueur d’avance sur le timer2 ?

TMR1/TMR2 comment ça fonctionne ?
Voilà un point important à élaborer qui consiste à comprendre le fonctionnement d’un timer. La configuration d’un timer via un PIC de la famille 24 ou 32 peut se faire soit sur 16 bits ou soit 32 bits, il en résulte que le timer à soit 65536 possibilité en 16 bits ou bien 4294967295 pour un 32 bits, hum !! Celà dit beaucoup de comptage pour le timer.
Prenons 16 bits afin de rester dans le contexte de l’exemple 2. Sur 16 bits le timer mathématiquement compte de 0 à 65536 (2^16=65536) soit 65536 possibilité puisque le zéro est compris. Imaginer le jeu de l’oie ou la case départ commence par zéro, au moment où vous allez avancer le pion cette case départ ne sera pas compter puisque vous allez commencer à compter à partir de la 1ere case… Et bien vous savez quoi ?!! Il en est de même pour le timer où il se verra de compter à partir de « 1 » soit 65535 possibilités (soit une case de moins). Lorsque le timer aura compté jusqu’à 65535 le drapeau « Flag » passera à l’état logique « 1 ».

Enfonçons un peu plus loin le clou!! et essayons de comprendre comment ce relais drapeau passe à l’état logique « 1 ». Imaginé une formule 1 qui aurait effectuée ces 65535 tours de pistes et arrive à tout vitesse sur la ligne d’arrivée ou à cet instant le drapeau se lève… Et bien encore une fois c’est le même principe pour le timer qui « enclenchera » ce relais drapeau lorsqu’il aura compté jusqu’à 65535 afin de stopper le programme principal.





Comment comparer 65535 avec un temps en ms ?
Comment calculer le temps en milliseconde à partir d’une valeur décimale ?
Nous avons évoqué dans le paragraphe « Configurations TimerX » que tout va dépendre du Quartz utilisé. Pour un Quartz de 10Mhz la cadence du PIC en interne sera deux fois moins rapide soit 5Mhz. Ensuite va venir des coefficients comme le prescaler (1:64) ainsi que le nombre de bits utilisés 16 bits soit 65535.

La temporisation obtenue en interne du Pic sera :
Tpic = (2/Fosc)*prescaler*nbrs de bits
Tpic = (2/10000000)*64*65535 = 0,838 sec soit 838ms

Pour un prescaler réglé à 1:64 et une configuration du PIC sur 16Bits avec un Quartz de 10Mhz la durée du comptage de 1 à 65535 est d’environ 838ms

Fonctionnement des 2 timers
Afin que les leds clignotent simultanément, et que nous puissions observer ces deux clignotements, il suffit de partager le temps en deux. Pendant qu’une led sera allumée l’autre sera éteinte et vice versa. La durée étant de 838ms ce qui donne pour deux clignotements 419ms.

Lorsque le timer1 aura compté jusqu’à 65535 la routine du timer1 s’exécutera suite au passage du drapeau à l’état logique « 1 ». Cela entraine l’allumage de la led et dans la foulé une temporisation de 418ms (petite parenthèse ce n’est pas une erreur oui!! il s’agit bien de 418ms soit 1ms de moins histoire de laisser un peu de temps pour que a procédure “Reset_LATA_…..” s’exécute bien ) afin de retarder le timer1 avant d’éteindre la led. Le fait de rajouter une temporisation n’aura aucun impact sur le comptage des timers. Cette temporisation permettra de voir le clignotement des leds une après l’autre.

Le programme en MikroPascal ci-dessous :


program Exemple_1_Timer1_Timer2;

{ Declarations section }

procedure init;
begin
TRISA:=$0000;
LATA:=$0000;
AD1PCFG := $FFFF; // permet d’activer toutes les entrées en numérique
IFS0.T1IF := 0; // mise à zéro du bit T1IF à l’initialisation
IFS0.T2IF := 0; // mise à zéro du bit T2IF à l’initialisation
IEC0.T1IE := 1; // activation des interruptions du Timer1
IEC0.T2IE := 1; // activation des interruptions du Timer2
T1CON :=%1000000000100000; // Timer1 activé, prescaler 1:64
T2CON :=%1000000000100000; // Timer2 activé, prescaler 1:64
TMR1:=$7FFF; // on commence à compter à parti de 32767
TMR2:=$0000; // on commence à compter à parti de 0
IPC0:=$7000;
IPC1:=$7000;
end;

procedure Timer1;
iv IVT_ADDR_T1INTERRUPT;
begin
if IFS0.T1IF = 1 then
begin
LATA.0:=1;
delay_ms(418); //(0,838 / 2 = 0,419) réglé à 418ms soit 1ms de moins
IFS0.T1IF := 0; // remise à zéro du drapeau du timer1
end;
end;

procedure Timer2;
iv IVT_ADDR_T2INTERRUPT;
begin
if IFS0.T2IF = 1 then
begin
LATA.1:=1;
delay_ms(418); //(0,838 / 2 = 0,419) réglé à 418ms soit 1ms de moins
IFS0.T2IF := 0; // remise à zéro du drapeau du Timer2
end;
end;

procedure Reset_LATA_timer1;
begin
LATA.0:=0; // on éteint la led D1
end;

procedure Reset_LATA_timer2;
begin
LATA.1:=0; // on éteint la led D2
end;

// Programme principal
begin
init;
while true do
begin
Reset_LATA_timer1;
Reset_LATA_timer2;
end;
end.