Leçons de niveau 14

Micro contrôleurs AVR/Le Timer 1

Une page de Wikiversité.
Sauter à la navigation Sauter à la recherche
Début de la boite de navigation du chapitre
Le Timer 1
Icône de la faculté
Chapitre no 5
Leçon : Micro contrôleurs AVR
Chap. préc. :Le Timer 0
Chap. suiv. :Le Timer 2
fin de la boite de navigation du chapitre
Icon falscher Titel.svg
En raison de limitations techniques, la typographie souhaitable du titre, « Micro contrôleurs AVR : Le Timer 1
Micro contrôleurs AVR/Le Timer 1
 », n'a pu être restituée correctement ci-dessus.

Le timer 1 est un timer étendu sur 16 bits. Cette taille de timer, 8 bits pour le timer 0 (du précédent chapitre) et 16 bits pour le timer 1 n’est pas sans rappeler l'architecture des PIC 16F. Rappelons par contre que le timer 0 est passé à 16 bits dans les PIC 18F. Mais revenons-en à notre sujet principal : les AVRs.

Documentation du timer 1 seul[modifier | modifier le wikicode]

Le timer 1 est un compteur sur 16 bits avec une horloge calibrée réalisée à partir de l'horloge (quartz externe). Son utilisation est possible en compteur. Dans ce cas c’est un signal externe qui fera avancer le compteur.

Timer1 de l'ATMega8[modifier | modifier le wikicode]

Voici sous forme schématique la documentation du timer 1. Il y a dans ce schéma toute l'information nécessaire pour réaliser la programmation du timer 1 dans n’importe quel langage.

Documentation du timer 1 de l'ATMega8

Les registres utilisés par le Timer1 sont donc :

  • TCNT1H et TCNT1L, (H pour High = poids fort, L pour LOW = poids faible)
  • TCCR1B
  • éventuellement le PORTD, et TIFR.

Au risque de se répéter, l'accès d'un registre 16 bits par un processeur 8 bits est toujours un peu complexe puisque cela se fait en deux écriture. En ce qui concerne l'architecture que l’on étudie, il y a un registre temporaire (partagé avec d'autres registres). On retiendra :

  • pour une écriture : on écrit d’abord le poids fort puis le poids faible
  • pour une lecture : on lit d’abord le poids faible puis le poids fort.

La bonne nouvelle est que notre compilateur C est capable de manipuler directement les 16 bits (TCNT1=0x1FF;) comme le montrent les définitions suivantes :

#define TCNT1	_SFR_IO16(0x2C) 
#define TCNT1L	_SFR_IO8(0x2C) 
#define TCNT1H	_SFR_IO8(0x2D)

La mise à zéro du bit TOV1 est un peu particulière. Elle se réalise en mettant un 1 dans le bit correspondant ou en écrivant une valeur dans le timer.

Le bit du PORTD qui sert éventuellement à piloter le compteur 1 s’appelle T1.

Timer1 de l'ATMega328[modifier | modifier le wikicode]

Le fonctionnement du Timer 1 est identique à celui de l'ATMega8 mais des registres ont changés légèrement de nom et des fonctionnalités ont été ajoutées. Mais ces nouvelles fonctionnalités ne sont pas présentées ici.

Documentation du Timer 1 de l'ATMega328

C'est maintenant le registre TIFR1 qui contient le drapeau de débordement TOV1.

Exercice 1[modifier | modifier le wikicode]

1°) L'AVR ATMega8 possède un oscillateur de 4 MHz. Quelle fréquence minimale de l'interruption TOV1 peut-il générer avec son horloge si la documentation du timer 1 de l'ATMega8 est la même que celle de l'ATMage328 ?

 /*********************************************************************
 Includes
 ***********************************************************************/
 #include <avr/io.h>
 #include <stdbool.h>
 #include <avr/interrupt.h>
 unsigned char nb=0;
 volatile unsigned char vPORTB=1; // cette variable peut être évitée
 /*********************************************************************
 Interrupt Routine
 **********************************************************************/
 // timer1 overflow
 ISR(TIMER1_OVF_vect) {
   vPORTB ^= 0x01;
   PORTB = vPORTB; 
 }
 
 /**********************************************************************
 Main
 **********************************************************************/
 int main( void ) {
 // Configure PORTB as output
     DDRB = 0xFF;
     PORTB = 0xFF;
 // enable timer overflow interrupt for both Timer0
     TIMSK |= (1<<TOIE1);
 // set timer0 counter initial value to 0
     TCNT1=0x0000;
 // start timer0 with 1024 prescaler
     TCCR1B = (1<<CS12) | (1<<CS10);
 // enable interrupts
     sei(); 
     while(true) { // grace a stdbool.h
     }
 }

Remarquez la présence d'une variable qualifiée de volatile. Il faut mieux déclarer les variables globales utilisées par une interruption comme volatile. Cela évite au compilateur de faire des optimisations qui seraient dommageable au bon fonctionnement. Cette variable est commentée comme non nécessaire car l'interruption pourrait se faire avec la simple instruction :

// timer1 overflow
 ISR(TIMER1_OVF_vect) {
   PORTB ^= 0x01;
 }

2°) Quelle fréquence et sur quel bit est-elle générée dans les même conditions que la question précédente ?

3°) Quel bit de quel registre autorise l'interruption ?

La comparaison[modifier | modifier le wikicode]

La comparaison sert à générer une fréquence calibrée sur une sortie quelconque (ou un bit interne). Elle est aussi utilisée pour générer une MLI (Modulation de Largeur d'Impulsion, PWM en anglais).

Documentation de la comparaison et de la MLI[modifier | modifier le wikicode]

Les différents modes de comparaison sont choisis à l'aide des bits WGM13, WGM12, WGM11 et WGM10 (un bit de plus que le timer 0). Ces choix sont conformes au tableau suivant.

Description des bits pour la génération de forme d'onde
Mode WGM13 WGM12 WGM11 WGM10 Mode de fonctionnement Bas si Mise à jour de OCRAx si Drapeau TOV1 positionné si
0 0 0 0 0 Normal 0XFFFF immédiatement MAX
1 0 0 0 1 PWM à phase correcte (8 bits) OXFF TOP BOTTOM
2 0 0 1 0 PWM à phase correcte (9 bits) OX1FF TOP BOTTOM
3 0 0 1 1 PWM à phase correcte (10 bits) OX3FF TOP BOTTOM
4 0 1 0 0 CTC (Clear Timer on Compare) OCR1A immédiatement MAX
5 0 1 0 1 PWM rapide (8 bits) OXFF BOTTOM TOP
6 0 1 1 0 PWM rapide (9 bits) OX1FF BOTTOM TOP
7 0 1 1 1 PWM rapide (10 bits) OX3FF BOTTOM TOP
14 1 1 1 0 PWM rapide ICR1 BOTTOM TOP
15 1 1 1 1 PWM rapide OCR1A BOTTOM TOP

Comme le montre ce tableau, le timer1 est un timer 16 bits capable de fonctionner sur 8, 9 et 10 bits (modes 5, 6 et 7). Il est même aussi capable de fonctionner avec une valeur maximale quelconque (modes 14 et 15) où la valeur maximale du comptage est fixée par un registre. Cela ressemble au mode CTC mais il s'agit en fait de fixer la fréquence de la MLI. Le rapport cyclique sera fixé par OCR1A et/ou OCR1B pour le mode 14 et seulement OCR1B pour le mode 15.

Une application classique du mode 14 est la commande d'un servomoteur qui nécessite la réalisation d'un signal MLI à une fréquence de 50Hz.

Pour chacun des modes de ce tableau ci-dessus, les bits COM1A1 et COM1A0 auront un fonctionnement différent. Ces bits sont destinés à gérer les formes d'onde du signal de sortie.

Comparaison simple[modifier | modifier le wikicode]

Mode non PWM pour la comparaison
COM1A1/COM1B1 COM1A0/COM1B0 Description
0 0 Opération Normale PORT, OC1A/OC1B déconnecté
0 1 Bascule OC1A/OC1B sur la comparaison
1 0 Mise à 0 de OC1A/OC1B sur la comparaison
1 1 Mise à 1 de OC1A/OC1B sur la comparaison

Et voici donc la documentation correspondante :

La comparaison avec le timer 1

Il est possible de passer le Timer 1 en mode 8 bits, ce qui n'apparaît pas sur la figure : mais sera expliqué plus tard.

Mode PWM rapide[modifier | modifier le wikicode]

Mode PWM rapide et comparaison
COM1A1/COM1B1 COM1A0/COM1B0 Description
0 0 Opération Normale PORT, OC1A/OC1B déconnecté
0 1 WGM02=0 Opération Normale PORT, OC1A/OC1B déconnecté
0 1 WGM02=1 Basculement de OC1A/OC1B sur la comparaison
1 0 Mise à 0 de OC1A/OC1B sur la comparaison et à 1 à BOTTOM
1 1 Mise à 1 de OC1A/OC1B sur la comparaison et à 0 à BOTTOM

Ce tableau nous montre clairement que seuls les modes 2 et 3 sont importants pour la MLI. Ces deux modes fonctionnent :

  • mode 2 on augmente OCR1A/OCR1B pour augmenter la largeur d'impulsion
  • mode 3 on augmente OCR1A/OCR1B pour diminuer la largeur d'impulsion

Mode PWM à phase correcte[modifier | modifier le wikicode]

Ce mode utilise le compteur 0 dans les deux sens : en comptage et décomptage.

Mode PWM à phase correcte et comparaison
COM1A1/COM1B1 COM1A0/COM1B0 Description
0 0 Opération Normale PORT, OC1A/OC1B déconnecté
0 1 WGM02=0 Opération Normale PORT, OC1A/OC1B déconnecté
0 1 WGM02=1 Basculement de OC1A/OC1B sur la comparaison
1 0 Mise à 0 de OC0A sur la comparaison quand comptage et mise à 1 de OC1A/OC1B sur la comparaison quand décomptage
1 1 Mise à 1 de OC0A sur la comparaison quand comptage et mise à 0 de OC1A/OC1B sur la comparaison quand décomptage

Exemple de programme en C[modifier | modifier le wikicode]

Nous donnons, pour commencer, le programme suivant destiné à faire clignoter une LED à environ à 1 Hz sur une platine Arduino MEGA2560. Cette platine fonctionnant à 16MHz, il faut compter jusqu'à 7812 si l’on utilise un prédiviseur par 1024 et un mode de basculement de bit. Attention, 1024 * 7812 ne donne pas 16MHz mais 8MHz à cause du basculement !

#include <avr/io.h> 
//*********** Pour platine MEGA2560 mais fonctionne probablement avec platine UNO
void main(){ 
// initialisation pour comparaison
  DDRB |= (1<<DDB5);   // 1 = sortie
  TIFR0 |= 0x04; // clr TOV1 with 1
  // Prescaler 1024 (Clock/1024) 
  TCCR1B = (1<<CS12) | (1<<CS10);
  TCCR1B |= (1<<WGM12); //RAZ timer quand comparaison
  TCCR1A |= (1<<COM1A0); //bascule sortie a chaque comparaison
  TCCR1A &= ~(1<<COM1A1); //bascule sortie a chaque comparaison
  OCR1A = 7812;//16 000 000 /(1024*2)
  while(1);
}

Il n'y a aucune interruption dans ce programme ! La boucle while(1) peut être remplacée par n’importe quelle activité sans déranger le matériel.

Interruption de comparaison[modifier | modifier le wikicode]

La comparaison permet de faire basculer une broche particulière de manière entièrement matérielle. Pour le timer 1 les broches sont appelées OC1A et OC1B.

Et pour l'ATMega328p ces broches sont :

  • PB1 broche 15 pour OC1A
  • PB2 broche 16 pour OC1B

Mais il suffit que le cahier des charges impose le basculement d'une broche (qui n'est ni OC1A ni OC1B) pour que l’on soit obligé de faire ce travail de basculement à l'aide d'une interruption.

Imaginons donc que l’on désire utiliser une des interruptions de comparaison (il y en a deux). La première chose à faire est de trouver comment elles ont été définies dans "avr/interrupt.h" :

#define TIMER1_COMPA_vect _VECTOR(11) /* Timer/Counter1 Compare Match A */ 
#define TIMER1_COMPB_vect _VECTOR(12) /* Timer/Counter1 Compare Match B */

Si l’on désire utiliser la première, il nous faudra donc écrire quelque chose qui ressemble à :

ISR(TIMER1_COMPA_vect) {
  // ... du code ici
}

Ce qui reste donc à faire est donc d'autoriser cette interruption. Cela se réalise en deux étapes :

  • mise à un du bit OCIE1A du registre TIMSK1
  • autorisation générale des interruptions avec un "sei()"

Exercice 2[modifier | modifier le wikicode]

Modifier le code donné en exemple dans la section précédente pour remplacer le basculement matériel de la broche OC1A par le basculement de la broche PB3 (toujours à un Herz).

Commande en largeur d'impulsion : la largeur des impulsions, comprise en général entre 1 et 2 millisecondes, commande la position du servomoteur.

Exercice 3[modifier | modifier le wikicode]

Réaliser une commande simple de servomoteur en C pur (sans la librairie Arduino). Comme le montre la figure ci-contre, il s'agit de réaliser une Modulation de Largeur d'Impulsion de fréquence 50 Hz (période de 20 ms).

Indications

Seul le timer1 permet de réaliser cela en matériel pur, c'est-à-dire sans code spécifique s'exécutant pour le réaliser et donc sans interruption. En effet 16MHz / 50 *1024 = 312,5 qui est une valeur de plus de 8 bits ! Cette valeur correspond à la valeur TOP de TCNT1 pour réaliser 50 Hz à partir de 16 MHz avec un prescaler de 1024. Il serait donc bon de prendre le mode 14 de la PWM et d'initialiser ICR1 à 313.

Le rapport cyclique doit varier entre 1 ms et 2 ms.

Une question importante est de savoir comment brancher le servomoteur ?

  1. Pour cela il faut chercher la broche OC1A sur le microcontrôleur. Sur l'ATMega328p c'est la broche PB1 (broche 15 d'un boîtier DIP).
  2. Chercher ensuite dans google en tapant "pinout arduino uno" et chercher sur une des images proposées soit PB2 soit OC1A.
  3. La correspondance Arduino est trouvée : c'est la broche -9 qui sera amenée au fil orange du servomoteur.
  4. Le fil marron foncé est amené à la masse et le rouge à Vcc (de l'Arduino).

Pour information il est possible de trouver des petits servomoteurs autour de 4 €.

Exercice 4[modifier | modifier le wikicode]

Reprendre l'exercice 3 pour une commande en panoramique et en inclinaison (pan/tilt touret), c'est-à-dire deux servomoteurs.