Dans ce chapitre, nous allons parler temporisations et comptages. La 16F84 ne comporte
quun seul timer sur 8 bits, contrairement à dautres PICs de la famille (comme
la 16F876). Si on examine attentivement le fonctionnement du timer0, nous verrons
quil sagit en fait dun compteur.
13.1 Les différents modes de fonctionnement |
Nous avons vu que le timer0 est en fait un compteur. Mais que compte-t-il ? Et
bien, vous avez deux possibilités.
- En premier lieu, vous pouvez compter les impulsions reçues sur la pin RA4/TOKI. Nous
dirons dans ce cas que nous sommes en mode compteur
- Vous pouvez aussi décider de compter les cycles dhorloge de la PIC elle-même.
Dans ce cas, comme lhorloge est fixe, nous compterons donc en réalité du temps.
Donc, nous serons en mode " timer ".
La sélection dun ou lautre de ces deux modes de fonctionnement
seffectue par le bit 5 du registre OPTION : T0CS pour Tmr0 Clock Source select
bit.
- T0CS = 1 : Fonctionnement en mode compteur
- T0CS = 0 : Fonctionnement en mode timer
Dans le cas où vous décidez de travailler en mode compteur, vous devez aussi
préciser lors de quelle transisition de niveau le comptage est effectué. Ceci est
précisé grâce au bit 4 du registre OPTION : T0SE pour Timer0 Source Edge select
bit.
- T0SE = 0 : comptage si lentrée RA4/TOKI passe de 0 à 1
- T0SE = 1 : comptage si lentrée RA4/TOKI passe de 1 à 0
Ce registre, qui se localise à ladresse 0x01 en banque0, contient tout
simplement la valeur actuelle du timer0. Vous pouvez écrire ou lire tmr0. Si par exemple
vous avez configuré tmr0 en compteur, la lecture du registre tmr0 vous donnera le nombre
dévénements survenus sur la pin RA4/TOKI.
13.3 Les méthodes dutilisation du timer0 |
Comment utiliser le timer0, et quelles sont les possibilités offertes à ce niveau,
voilà de quoi nous allons parler ici.
13.3.1 Le mode de lecture simple
La première méthode qui vient à lesprit est la suivante : Nous lisons le
registre tmr0 pour voir ce quil contient. La valeur lue est le reflet du nombre
dévénements survenus, en prenant garde au fait que le tmr0 ne peut compter que
jusque 255. En cas de dépassement, le tmr0 recommence à 0. Cest donc à vous de
gérer cette possibilité.
Petit exemple :
|
clrf |
tmr0 |
; début du comptage |
|
; ici un certain nombre dinstructions |
|
movf |
tmr0 , w |
; charger valeur de comptage |
|
movwf |
mavariable |
; sauver pour traitement ultérieur |
13.3.2 Le mode de scrutation du flag
Nous devons savoir à ce niveau, que tout débordement du timer0 (passage de 0xFF à
0x00) entraîne le positionnement du flag T0IF du registre INTCON. Vous pouvez donc
utiliser ce flag pour déterminer si vous avez eu débordement du timer0, ou, en
dautres termes, si le temps programmé est écoulé. Cette méthode à
linconvénient de vous faire perdre du temps inutilement
Petit exemple :
|
clrf |
tmr0 |
; début du comptage |
|
bcf |
INTCON , T0IF |
; effacement du flag |
loop |
|
|
|
|
btfss |
INTCON , T0IF |
; tester si compteur a débordé |
|
goto |
loop |
; non, attendre débordement |
|
; suite du programme |
; oui, poursuivre : 256 événements écoulés |
Mais vous pourriez vous dire que vous ne désirez pas forcément attendre 256
incrémentations de tmr0. Supposons que vous désiriez attendre 100 incrémentations. Il
suffit dans ce cas de placer dans tmr0 une valeur telle que 100 incrémentations plus
tard, tmr0 déborde.
exemple :
|
movlw |
256-100 |
; charger 256 100 |
|
movwf |
tmr0 |
; initialiser tmr0 |
|
bcf |
INTCON , T0IF |
; effacement du flag |
loop |
|
|
|
|
btfss |
INTCON , T0IF |
; tester si compteur a débordé |
|
goto |
loop |
; non, attendre débordement |
|
; suite du programme |
; oui, poursuivre : 256 événements écoulés |
13.3.3 Le mode dinterruption
Cest évidemment le mode principal dutilisation du timer0. En effet,
lorsque T0IE est positionné dans le registre INTCON, chaque fois que le flag T0IF passe
à 1, une interruption est généree. La procédure à utiliser est celle vue dans la
leçon sur les interruptions.
13.3.4 Les méthodes combinées
Supposons que vous vouliez, par exemple, mesurer un temps entre 2 impulsions sur le
broche RB0. Supposons également que ce temps soit tel que plusieurs débordements du tmr0
puissent avoir lieu. Une méthode simple de mesure du temps serait la suivante :
- A la première impulsion sur RB0, on lance le timer 0 en mode interruptions.
- A chaque interruption de tmr0, on incrémente une variable
- A la seconde interruption de RB0, on lit tmr0 et on arrête les interruptions
- Le temps total sera donc (256*variable)+tmr0
On a donc utilisé les interruptions pour les multiples de 256, et la lecture directe
de tmr0 pour les " unités ".
Supposons que nous travaillons avec un quartz de 4MHz. Nous avons donc dans ce cas
(4000000/4) = 1.000.000 de cycles par seconde. Chaque cycle dhorloge dure donc
1/1000000ème de seconde, soit 1µs.
Si nous décidons dutiliser le timer0 dans sa fonction timer et en mode
interruptions. Nous aurons donc une interruption toutes les 256µs, soit à peut près
toutes les quarts de millième de seconde.
Si nous désirons réaliser une LED clignotante à une fréquence de +- 1Hz, nous
aurons besoin dune temporisation de 500ms, soit 2000 fois plus. Ce nest donc
pas pratique. Nous disposons pour améliorer ceci dun PREDIVISEUR .
Quest-ce donc ? Et bien, tout simplement un diviseur dévénements
situé AVANT lentrée de comptage du timer0. Nous pourrons donc décider
davoir incrémentation de tmr0 tous les 2 événements par exemple, ou encore tous
les 64 événements.
Regardez tableau de la page 16. Vous voyez en bas le tableau des bits PS0 à PS2 du
registre OPTION qui déterminent la valeur du prédiviseur.
Ces valeurs varient, pour le timer0, entre 2 et 256. Le bit PSA, quand à lui,
détermine si le prédiviseur est affecté au timer0 ou au watchdog. Voici un tableau
exprimant toutes les possibilités de ces bits
PSA |
PS2 |
PS1 |
PS0 |
/tmr0 |
/WD |
Temps tmr0 |
Temps typique Watchdog (minimal) |
0 |
0 |
0 |
0 |
2 |
1 |
512 µs |
18 ms (7 ms) |
0 |
0 |
0 |
1 |
4 |
1 |
1024 µs |
18 ms (7 ms) |
0 |
0 |
1 |
0 |
8 |
1 |
2048 µs |
18 ms (7 ms) |
0 |
0 |
1 |
1 |
16 |
1 |
4096 µs |
18 ms (7 ms) |
0 |
1 |
0 |
0 |
32 |
1 |
8192 µs |
18 ms (7 ms) |
0 |
1 |
0 |
1 |
64 |
1 |
16384 µs |
18 ms (7 ms) |
0 |
1 |
1 |
0 |
128 |
1 |
32768 µs |
18 ms (7 ms) |
0 |
1 |
1 |
1 |
256 |
1 |
65536 µs |
18 ms (7 ms) |
1 |
0 |
0 |
0 |
1 |
1 |
256 µs |
18 ms (7 ms) |
1 |
0 |
0 |
1 |
1 |
2 |
256 µs |
36 ms (14 ms) |
1 |
0 |
1 |
0 |
1 |
4 |
256 µs |
72 ms (28 ms) |
1 |
0 |
1 |
1 |
1 |
8 |
256 µs |
144 ms (56 ms) |
1 |
1 |
0 |
0 |
1 |
16 |
256 µs |
288 ms (112 ms) |
1 |
1 |
0 |
1 |
1 |
32 |
256 µs |
576 ms (224 ms) |
1 |
1 |
1 |
0 |
1 |
64 |
256 µs |
1,152 Sec (448 ms) |
1 |
1 |
1 |
1 |
1 |
128 |
256 µs |
2,304 Sec (996 ms) |
- PSA à PS0 sont les bits de configuration du prédiviseur
- /tmr0 indique la valeur du prédiviseur résultante sur le timer0
- /WD indique la valeur du prédiviseur résultante sur le Watchdog
- temps tmr0 indique le temps max entre 2 interruptions tmr0 avec quartz de 4MHz
- Temps watchdog indique le temps typique disponible entre 2 reset watchdog (indépendant
du quartz utilisé). La valeur entre parenthèses indique le temps minimal, qui est celui
à utiliser pour faire face à toutes les circonstances.
Remarques importantes :
- Il ny a quun prédiviseur, qui peut être affecté au choix au timer du
watchdog (que nous verrons plus tard) ou au timer0. Il ne peut être affecté aux deux en
même temps.
- Il nexiste pas de prédiviseur = 1 pour le timer0. Si vous ne voulez pas utiliser
le prédiviseur, vous devez donc impérativement le sélectionner sur le watchdog avec une
valeur de 1 (ligne jaune du tableau).
- La valeur contenue dans le prédiviseur nest pas accessible. Par exemple, si vous
décidez dutiliser un prédiviseur de 64, et quil y a un moment donné 30
événements déjà survenus, vous navez aucun moyen de le savoir. Le prédiviseur
limite donc la précision en cas de lecture directe.
- Lécriture dans le registre tmr0 efface le contenu du prédiviseur. Les
événements survenus au niveau du prédiviseur sont donc perdus.
13.5 Application pratique du timer0 |
Nous allons mettre en uvre notre tmr0 dans une première application pratique.
Reprenons donc notre premier exercice, à savoir, faire clignoter une LED à la fréquence
approximative de 1Hz.
13.5.1 Préparations
Faites un copier/coller de votre nouveau fichier m16f84.asm et renommez cette copie
" Led_tmr.asm " [ NDLR : Fichiers
exemples ici ]. Relancez MPLAB et créez un nouveau projet intitulé
" Led_tmr.pjt ". Ajoutez-y votre nud
" Led_tmr.asm ".
Créez votre en-tête (je continue dinsister)
;*************************************************************************** |
; |
; Fait clignoter une LED à une fréquence approximative de 1Hz |
; |
;*************************************************************************** |
; |
; NOM: LED CLIGNOTANTE AVEC TIMER0 |
; Date: 17/02/2001 |
; Version: 1.0 |
; Circuit: Platine d'essais |
; Auteur: Bigonoff |
; |
;*************************************************************************** |
; |
; Fichier requis: P16F84.inc |
; |
;*************************************************************************** |
; |
; Notes: Utilisation didactique du tmr0 en mode interruption |
; |
;*************************************************************************** |
Définissez ensuite les _CONFIG en plaçant le watch-dog hors service.
Calculons ensuite le nombre de débordement de tmr0 nécessaires. Nous avons besoin
dune temporisation de 500 ms, soit 500.000µs. Le timer0 génère, sans
prédiviseur, une interruption toutes les 256µs . Nous allons donc utiliser le
prédivideur. Si nous prenons la plus grande valeur disponible, soit 256, nous aurons donc
une interruption toutes les (256*256) = 65536µs.
Nous devrons donc passer (500.000/65536) = 7,63 fois dans notre routine
dinterruption. Comme nous ne pouvons pas passer un nombre décimal de fois, nous
choisirons 7 ou 8 fois, suivant que nous acceptons une erreur dans un sens ou dans
lautre. Notez que si vous passez 7 fois, vous aurez compté trop peu de temps, il
sera toujours possible dallonger ce temps. Dans le cas contraire, vous aurez trop
attendu , donc plus de correction possible.
Il est évident que lacceptation dune erreur est fonction de
lapplication. Si vous désirez faire clignoter une guirlande de Noël, lerreur
de timing sera dérisoire. Si par contre vous désirez construire un chronomètre, une
telle erreur sera inacceptable. Commençons donc par ignorer lerreur.
Nous allons décider dutiliser une prédivision de 256 avec 7 passages dans la
routine dinterruption. Le temps obtenu sera donc en réalité de (256*256*7) =
458752 µs au lieu de nos 500.000µs théoriques.
En reprenant notre tableau page16 sur le contenu du registre OPTION, nous devrons donc
initialiser celui-ci avec : B10000111, soit 0x87. En effet, résistances
de rappel hors-service (on nen na pas besoin), source timer0 en interne et
prédiviseur sur timer0 avec valeur 256. Nous obtenons donc :
OPTIONVAL |
EQU |
H'0087' |
; Valeur registre option |
|
|
|
; Résistance pull-up OFF |
|
|
|
; Préscaler timer à 256 |
Ensuite nous devons déterminer la valeur à placer dans le registre INTCON pour
obtenir les interruptions sur le timer0. Ce sera B10100000, soit
0xA0
INTERMASK |
EQU |
H'00A0' |
; Interruptions sur tmr0 |
Ensuite, nos définitions :
;********************************************************************* |
; DEFINE |
;********************************************************************* |
#DEFINE |
LED |
PORTA,2 |
; LED |
Ne touchons pas à notre routine dinterruption principale, car nous avons
suffisamment de place pour conserver nos tests. Ecrivons donc notre routine
dinterruption timer. Nous voyons tout dabord que nous allons devoir compter
les passages dans tmr0, nous allons donc avoir besoin dune variable. Déclarons-la
dans la zone 0X0C.
|
|
cmpt : 1 |
; compteur de passage |
13.5.2 Linitialisation
Comme il est plus facile de détecter une valeur égale à 0 quà 7, nous
décrémenterons donc notre variable de 7 à 0. Nous inverserons la LED une fois la valeur
0 atteinte. Nous devons donc intialiser notre variable à 7 pour le premier passage.
Nous effectuerons ceci dans la routine dintialisation, avant le goto start.
Profitons-en également pour placer notre port LED en sortie.
Nous obtenons donc :
;********************************************************************* |
; INITIALISATIONS |
;********************************************************************* |
|
|
|
|
init |
|
|
|
|
clrf |
PORTA |
; sorties portA à 0 |
|
clrf |
PORTB |
; sorties portB à 0 |
|
BANK1 |
|
; passer banque1 |
|
clrf |
EEADR |
; permet de diminuer la consommation |
|
movlw |
OPTIONVAL |
; charger masque |
|
movwf |
OPTION_REG |
; initialiser registre option |
|
|
|
|
|
|
; Effacer RAM |
|
|
|
;---------------- |
|
|
movlw |
0x0c |
; initialisation pointeur |
|
movwf |
FSR |
; pointeur d'adressage indirect |
init1 |
|
|
|
|
clrf |
INDF |
; effacer ram |
|
incf |
FSR,f |
; pointer sur suivant |
|
btfss |
FSR,6 |
; tester si fin zone atteinte (>=0x40) |
|
goto |
init1 |
; non, boucler |
|
btfss |
FSR,4 |
; tester si fin zone atteinte (>=0x50) |
|
goto |
init1 |
; non, boucler |
|
|
|
|
|
|
; initialiser PORTS |
|
|
; --------------- |
|
bcf |
LED |
; passer LED en sortie |
|
|
|
|
|
BANK0 |
|
; passer banque0 |
|
movlw |
INTERMASK |
; masque interruption |
|
movwf |
INTCON |
; charger interrupt control |
|
|
|
|
|
|
; initialisations variables |
|
|
; ------------------------- |
|
movlw |
7 |
; charger 7 |
|
movwf |
cmpt |
; initialiser compteur de passages |
|
|
|
|
|
goto |
start |
; sauter programme principal |
13.5.3 La routine dinterruption
Réalisons donc maintenant notre routine dinterruption :
Tout dabord, on décrémente notre compteur de passage, sil nest pas
nul, on na rien à faire cette fois.
|
decfsz |
cmpt , f |
; décrémenter compteur de passages |
|
return |
|
; pas 0, on ne fait rien |
Ensuite, si le résultat est nul , nous devons inverser la LED et recharger 7 dans le
compteur de passages. Voici le résultat final :
;********************************************************************** |
; INTERRUPTION TIMER 0 |
;********************************************************************** |
inttimer |
|
|
|
|
decfsz |
cmpt , f |
; décrémenter compteur de passages |
|
return |
|
; pas 0, on ne fait rien |
|
BANK0 |
|
; par précaution |
|
movlw |
b'00000100' |
; sélectionner bit à inverser |
|
xorwf |
PORTA , f |
; inverser LED |
|
movlw |
7 |
; pour 7 nouveaux passages |
|
movwf |
cmpt |
; dans compteur de passages |
|
return |
|
; fin d'interruption timer |
Il ne nous reste plus quà effacer la ligne
|
clrwdt |
|
; effacer watch dog |
du programme principal, puisque le watchdog nest pas en service.
Compilez votre programme. Nous allons maintenant le passer au simulateur.
Noubliez pas de mettre le simulateur en service, et ouvrez la fenêtre des
registres spéciaux. Avancez ensuite votre programme en pas à pas jusquà ce
quil arrive dans le programme principal.
Remarques
- Le dernier registre en dans la fenêtre des registres spéciaux
" T0pre " est en rouge car cest un registre qui nexiste
pas physiquement dans la PIC. Cest MPLAB qui compte les prédivisions pour les
besoins de la simultation.
- Chaque fois que " T0pre " atteint la valeur de prédivision, tmr0
est incrémenté de 1. Ce nest que lorsquil débordera que nous aurons une
interruption.
- Dans le programme principal, " T0pre " est incrémenté de 2 unités
à chaque pression sur <F7>. Cest normal, car ce programme ne comporte
quun saut (goto), et chaque saut prend 2 cycles.
|