Le microcontrôleur
- partie 2
Les registres
Après avoir vu les bases du Microcontrôleur (voir la page sur "ça fait
quoi ?" et "comment ça se branche?") , nous allons aborder des
choses un peu plus pointues concernant le fonctionnement interne. ATTENTION, CA PIQUE !
J'ai fortement insisté là-dessus dans la partie 1 : Ce sont les
registres qui font l'essentiel du boulot. Oui mais j'imagine déjà les
questions qui se posent...presque un sommaire :
- A quoi ressemblent ces registres ?
- Comment accéder et modifier ces registres?
- ces registres contrôlent quoi exactement?
- Vous voulez des exemples?
1/ A
quoi ressemblent ces registres ?
Physiquement, ce sont des bascules D....des mémoires en fait ! On peut facilement retenir qu'un registre est un tiroir dans lequel
on stocke une valeur. Ce tiroir a une influence sur le fonctionnement
d'un élément du µC.
Chaque registre contient une valeur codée sur un octet (8 bits). Elle
est donc comprise entre 0 et 255.
Exemple :
dans le tiroir "PORTA", on peut trouver la valeur 00110011 (en
binaire) = 0x33 (en hexadécimal) =
51 (en décimal)
Tout le problème c'est de savoir sur quoi agit chaque tiroir (registre
!!!) Il faut donc que le "meuble à tiroirs" soit bien rangé !
Heureusement, chacun d'eux possède un nom précis. Ces noms ne sont pas
choisis au hasard. Ce sont, pour la plupart, des abréviations
concernant l'élément contrôlé.
Exemple : TCNT1= "Timer CouNTer 1" ==> "compteur du
chronomètre 1"
Ça permet de retrouver facilement les noms des variables quand on veut
consulter ou modifier sa valeur à l'aide du programme.
Remarquez que les différents TIMERS possèdent des noms de registres
presque identiques (au numéro de timer près). Même chose pour les ports
d'entrées-sorties (PORTA, PORTB, PORTC...). Ca permet de faciliter
encore la recherche des registres lors de l'écriture du programme. J'ai
construit l'image ci-dessus de façon à observer les similitudes entre
différents blocs de registres.
Autre détail physique :
Les registres ne sont ni de l'EEprom ni de la mémoire
flash. DONC,en cas de coupure de courant, ils ont effacés. A chaque
démarrage, les tiroirs sont vides !
2/ Comment
accéder et modifier ces registres?
Le contrôleur central (coeur) du µC peut consulter ( =lire ) ou
modifier
( =écrire ) le contenu de chaque tiroir. C'est votre programme qui va
modifier ou lire ces registres. Vos lignes de code doivent donc décrire
explicitement le nom du registre à modifier.
Exemple : si je veux afficher
les états "11001100" sur le port A, il suffit d'écrire la valeur dans le registre "PORTA":
PORTA
= 11001100; 
Remarque : Pour la plupart des registres, chaque bit porte un nom : C'est le cas
pour les registres des ports d'entrées-sorties. Il est donc possible de régler individuellement la valeur de chaque
patte du port. Il suffit d'écrire la valeur voulue dans le bit
concerné. Par exemple :
PORTA_0
= 0; //patte 0 du port A à l'état "bas" = "0" = "masse"
Autre exemple : Au début du programme, il faut régler chaque patte du
port en sortie ou en entrée. C'est le registre DDRA qui gère ce
point. Ainsi, si on veut que les pattes 0 et 1 soient en entrée et les autres en sortie, on écrit :
DDRA
= 11111100;
Ici aussi, il est possible de s'adresser à chaque bit du registre, individuellement :
DDRA7 = 1; //patte 7 du port A en sortie
DDRA6 = 1; //patte 6 du port A en sortie
DDRA1 = 0; //patte 1 du port A en entrée
DDRA0 = 0; //patte 0 du port A en entrée
etc...
Maintenant, puisque la patte "1" est "en entrée", je peux consulter
( =lire) l'état de l'élément qui y est raccordé. Par exemple, si le bouton de
programmation est raccordé à la patte 1 du portA on peut écrire :
BoutonProg
= PINA1;
Au moment où je l'interroge, la variable "BoutonProg" mémorise immédiatement la valeur présente sur la
patte 1 du port A.
Evidemment, on a la même logique pour les autres ports
d'entrées-sorties....et pour tous les registres en général.
3/
Ces registres contrôlent quoi exactement?
TOUT !
Il serait ambitieux de vouloir décrire les registres de façon
exhaustive. Il existe des ouvrages complets là-dessus. Je conseille
notamment de faire l'acquisition du livre de Christian Tavernier : "Microcontrôleurs AVR : des
ATtiny aux ATmega", paru chez Dunod. C'est une mine de
renseignements ! Si vous préferez les PIC, il a également écrit le le même ouvrage pour les PIC
Heureusement, la documentation technique gratuite (datasheet) de chaque
composant décrit l'usage de chaque registre de façon extrêmement
précise.Voici
par exemple les notices de 3 µC courants :
Attiny861 : La page 223 donne la liste
de tous les registres du ATtiny861 (source du constructeur : atmel.com)
ATmega162 : Voir les pages 304-305 pour la liste
exhaustive des registres de ce composant.
ATmega8535
: La liste des registres est à la page 299 !
Vous trouverez dans les paragraphes suivants un descriptif des
principaux registres des µC ATMEL : J'ai pris en exemple les registres
de l'ATtiny861. Certains sont un peu spécifiques mais le principe de
base reste vrai pour tous les µC.
3.1 /Pour les ports
d'entrées-sorties
Pas de surprise puisque je viens de décrire les différents registres ci-dessus.
- Affectation d'une valeur sur chaque PORT :
PORTA |
PORTA_7 |
PORTA_6 |
PORTA_5 |
PORTA_4 |
PORTA_3 |
PORTA_2 |
PORTA_1 |
PORTA_0 |
Ces mots clés permettent d'attribuer une valeur sur le port ou la patte concernée.
Evidemment, il y a des mêmes similaires pour les autres ports : PORTB, PORTC, PORTD...suivant la taille de votre µC |
- Attribution de l'état "Entrée" ou "Sortie"
DDRA "Data Direction Register A"
|
DDRA7
|
DDRA6
|
DDRA5
|
DDRA4
|
DDRA3
|
DDRA2
|
DDRA1
|
DDRA0
|
Au début du programme, il faut impérativement attribuer un sens de fonctionnement pour chaque patte des ports du µC. Chaque
bit est accessible individuellement. Pour que la patte soit réglée "en
sortie", il faut lui assigner la valeur 1 dans ce registre.
|
- Lecture de l'état d'un port :
PINA "Pins INput A"
|
PINA7
|
PINA6 |
PINA5 |
PINA4 |
PINA3 |
PINA2 |
PINA1
|
PINA0 |
La
lecture de l'état actuel des pattes du µC peut se faire à travers les
registres "PINx". L'accès individuel aux différents bits du registre
est possible en utilisant le mot clé correspondant.
|
3.2/ Pour les TIMERS (préparez le l'aspirine !) :
Vous l'avez compris, il peut y avoir plusieurs TIMERS dans un
µC. Donc plusieurs "chronomètres" ! On retrouve quasiment les mêmes
registres dans chacun d'eux. Ils sont différenciés par leur numéro de 0
à 4 (voire 8 pour les plus gros).
Leur comportement est géré de façon extrêmement précise. Et surtout,
l'énorme avantage, c'est que une fois réglé, ce comportement est
automatique ! Le coeur n'a plus besoin de s'en occuper. Inutile de dire
que ces TIMERS sont TRES utilisés !
Ci-dessous, je détaille les principaux registres pour le timer1 :
- Compter le temps qui passe grâce à la la valeur du compteur
TCNT1 :
TCNT1
"TimerCouNTer 1"
|
La valeur du compteur est
automatiquement incrémentée de 1 à des intervalles de temps réguliers. Ainsi, si vous avez
correctement réglé la durée du délai , il est
possible de connaître la durée d'un évènement en consultant la valeur
du TIMER. Les TIMERS sont généralement codés sur 8 bits. Leur valeur
maxi est donc 255. Dans certains cas, ils peuvent être codés sur 10
bits (maxi=1024) , voire 16 bits (maxi=65535).
Arrivés à leur valeur maxi, on dit qu'ils "débordent" (Overflow) ce qui les fait revenir à leur valeur mini.
|

|
- Comparer le compteur avec une valeur donnée pour déclencher des évènements (=interruptions)
OCR1A
"Output Compare Register 1A"
|
Le
registre OCR1A contient une valeur (attribuée par le programme) qui sera continuellement comparée à
TCNT1 afin de déclencher des choses.
Par exemple, la comparaison
réussie peut provoquer le déclenchement
d'un sous-programme et/ou l'inversion
de la valeur
sur la patte "OC1A" du µC (Ça dépend des réglages
sur les registres de contrôle). C'est donc comme ça qu'on peut générer
un signal MLI sur une patte sans que le µC ait à s'en occuper.
Dans le même genre, il existe OCR1B
qui influe sur la patte OC1B....idem
pour OCR1C, OCR1D, etc...
|

|
- Régler le comportement du compteur 1 : registre TCCR1B
TCCR1B "Timer /
Counter Control Register 1B"
|
PWM1X
| PSR1
| DTPS11
| DTPS10
| CS13
|
CS12
|
CS11
| CS10
|
- Les bits CS "Clock Select" permettent de choisir la durée de
en divisant l'horloge globale. Ainsi, la durée d'incrémentation du
chronomètre est ralentie ou accélérée suivant les valeurs choisies.
Voir l'exemple ci-dessous
- Le bit "PWMx" sert à autoriser
(ou non) les changements d'états sur les pattes OC1x
- Les bits "DTP" servent à diviser encore l'horloge du compteur 1 si nécessaire.
|
Exemple
de division de l'horloge pour le compteur 1 :
Si je règle CS13=0, CS12=0, CS11=1, CS10=1 alors je divise
l'horloge globale par 4 (c'est le datasheet qui le dit). Ça signifie
que le compteur sera incrémenté toutes les 4 impulsions d'horloge.
Ainsi, si mon quartz me donne une horloge à 16MHz (une impulsion tous
les 0.0625µs), cela signifie que le compteur augmentera de "1" toutes
les 0.25µs.
|
- Régler le comportement des sorties en fonction du timer : TCCR1A
TCCR1A "Timer /
Counter Control Register 1A"
|
COM1A1
| COM1A0
| COM1B1
| COM1B0
| FOC1A
| FOC1B
| PWM1A
| PWM1B
|
ET OUI !
Chaque timer peut contrôler l'état d'au-moins 2 pattes du µC. Et ceci de
façon absolument indépendante du contrôleur central. Cool !
Mais il faut définir comment ça va se comporter.
Ce
registre permet de régler l'influence du TIMER1 sur les pattes "OC1A" et
"OC1B".
- Les bits "COM" permettent de gérer l'influence du TIMER sur les
pattes de sortie (mise à 1, mise à 0, inversion) lorqu'une comparaison
a réussi.
- Les bits "FOC" permettent de forcer une comparaison
Personnellement, je ne l'utilise pas.
- Les bits "PWM" servent à autoriser
(ou non) les changements d'états sur les pattes concernées.
|
3.3/ Pour les interruptions :
Vous l'avez compris, les registres gèrent beaucoup de choses.
Ceci dans un seul but : alléger le travail du calculateur ! Pour qu'un
programme soit efficace, il faut qu'il soit aussi rapide que possible.
Celui-ci ne peut pas passer son temps à faire des vérifications
inutiles la majorité du temps. Il doit déléguer cette tâche à un "bloc
d'interruptions".
Ce bloc sera capable d'informer le coeur au moment précis où
l'évènement intervient. Ainsi, le coeur interrompra le traitement du
programme pour se consacrer au sous-programme déclenché par
l'interruption. Il reprendra le programme là où il l'avait laissé dès
la fin de l'interruption.

"INT0" est l'une des interruptions possibles. Elle est déclenchée par un changement de niveau sur une patte spécifique du µC
Ainsi, certaines parties du programmes ne seront exécutées que sous certaines conditions. Toute l'astuce consiste à autoriser convenablement ces interruptions au début du programme avec les registres adaptés :
- Autorisation générale des Interruptions : SREG... INDISPENSABLE sinon rien ne s'interromp!
SREG "Status REGister"
|
I
|
T
|
H
|
S
|
V
|
N
|
Z
|
C
|
Le
bit qui nous intéresse le plus c'est le bit "I". S'il est mis à "1",
cela autorise le déclenchement de toutes les interruptions qui ont été
demandées. Si ce bit est à "zéro", aucune interruption ne sera déclenchée. Les autres bits sont utilisés de façon automatique par le coeur, et nous ne les utiliserons pas avec notre programme.
Pour autoriser les interruptions : SREG = $80; (ce qui correspond à 10000000 en binaire)
|
- Masque d'interruptions pour les TIMERS : TIMSK
TIMSK "Timer Interrupt MaSK"
|
OCIE1D
|
OCIE1A
|
OCIE1B
|
OCIE0A
|
OCIE0B
|
TOIE1
|
TOIE0
|
TICIE0
|
OCIExx
= Output Compare Interrupt Enable ==> Ces bits sont réglés au
départ du programme, dans la phase d'initialisation. Ils servent à
autoriser l'interruption lorsqu'une comparaison a réussi sur le timer
concerné. Par exemple, pour le timer 1, OCIE1A autorise l'interruption lorsque la comparaison de OCR1A est réussie. Même logique pour les 4 autres !
TOIEx = Timer Overflow
Interrupt Enable ==> Ces bits autorisent à déclencher les
interruptions lorsque les timers débordent. Un compteur qui compte sur
8 bits vaudra 255 au maximum. Si on lui ajoute 1, il "déborde". Cet
évènement peut provoquer le déclenchement d'une interruption si ces
bits l'autorisent. Par exemple TOIE1 surveille le débordement du timer 1.
|
- Masque d'interruptions externes : GIMSK INT1 INT0 PCIE1 PCIE0
GIMSK "General Interrupt MaSK register"
|
INT1
|
INT0
|
PCIE1
|
PCIE0
|
-
|
-
|
-
|
-
|
Et voilà enfin la fameuse interruption "INT0" dont je vous parle depuis si longtemps !
Les bits INT servent à autoriser une interruption lorsqu'une patte du
µC change d'état. L'interruption vient de l'extérieur du µC. Par
exemple, pour l'ATtiny861, les pattes concernées sont : "PORTB6' pour
INT0 et "PORTA2" pour INT1. C'est spécifique à chaque µC.
PCIEx = Ces bits
permettent de surveiller tout changement sur les ports du µC. Remarque
personnelle Cette interruption n'est pas assez spécifique d'après
moi. Elle surveille tout à la fois et se déclenche trop souvent pour
avoir un réelle efficacité. Je ne l'utilise pas.
|
MCUCR "MicroControler Unit Control Register"
|
-
|
-
|
-
|
-
|
-
|
-
|
ISC01
|
ISC00
|
J'associe
ce registre au précédent car les deux bits surlignés ici permettent de
décider quel type de changement provoquera l'interruption INT0 :
ISC01 = 0 / ISC00=0 ==> INT0 déclenchée par un niveau bas
ISC01 = 0 / ISC00=1 ==> INT0 déclenchée par tout changement de niveau
ISC01 = 1 / ISC00=0 ==> INT0 déclenchée par un front descendant
ISC01 = 1 / ISC00=1 ==> INT0 déclenchée par un front montant
Consultez la page "programmation" pour comprendre comment j'ai utilisé ces registres
|
Pour conclure résumer cet aperçu des registres, vous vous rendez compte qu'ils
sont indispensables à la bonne marche de votre programme car ils ont de
l'influence sur les principaux paramètres du µC. Suivant vos besoins,
certains seront utilisés et d'autres non.
Evidemment, cette liste n'est pas exhaustive mais je vous ai
présenté là les principaux registres utiles à la création de votre
décodeur. Je vous invite à consulter le datasheet de votre µC pour avoir le détail complet des registres.
Je propose de voir quelques exemples de programmation sur la page "programmation en C"
Evidemment, si vous souhaitez apporter des compléments ou me faire part de vos commentaires, n'hésitez pas à me laisser un message
.