Dernière révision : avril 2025










Les timers de l'ATmega328

Philippe Notez (philippe.notez@inmc.fr)





Sommaire

Introduction

L’électronique et l’informatique ont profondément modifié notre société. C’est certainement la révolution industrielle la plus rapide de l’histoire de l’humanité. Aujourd'hui, les systèmes embarqués sont omniprésents dans notre vie quotidienne et nous emmènent vers un monde de plus en plus connecté, avec ses avantages et ses inconvénients...

L’auteur ne pourra en aucun cas être tenu responsable des dommages qui résulteraient de l’utilisation des informations publiées sur ce site, sous licence Creative Commons BY-NC-SA. Toute reproduction ou modification d'un document, même partielle, est autorisée à condition que son origine et le nom de l'auteur soient clairement indiqués (BY), qu'il soit utilisé à des fins non commerciales (NC), que son mode de diffusion soit identique au document initial (SA), et que cela ne porte pas atteinte à l’auteur.

Ce document vous propose deux sketchs utilisant les timers (ce sont en fait des compteurs) de l'ATmega328P
(P = low power), en espérant toujours être le plus clair et précis possible. Le premier sketch utilise le mode Fast PWM (Pulse Width Modulation) pour faire varier la valeur moyenne d'une tension, le deuxième sketch utilise le mode CTC (Clear Timer on Compare) pour obtenir une durée précise et déclencher une interruption. Malgré tout le soin apporté à la rédaction, l'auteur vous remercie de bien vouloir le contacter si vous relevez la moindre erreur ou omission, et vous souhaite une agréable lecture.


timer0_ATmega328.ino
// timer 0 ATmega328 en mode Fast PWM
// philippe.notez@inmc.fr
// cc-by-nc-sa

void delayMilliseconds(unsigned temps)
  {
  unsigned i=0;
  
  while (i<temps)
    {
    delayMicroseconds(1000);
    i++;
    }
  }

void setup()
  {
  // le timer 0 (registre TCNT0, 8 bits) est utilisé par les fonctions "delay", "millis", "micros" et "analogWrite" (PWM) sur D5 et D6
  // le timer 1 (registre TCNT1, 16 bits) est utilisé par la bibliothèque Servo et la fonction "analogWrite" (PWM) sur D9 et D10
  // le timer 2 (registre TCNT2, 8 bits) est utilisé par la fonction "analogWrite" (PWM) sur D3 et D11

  // en mode Fast PWM, le timer compte à partir de 0 jusqu'à l'overflow (débordement), puis revient à 0. On inverse l'état d'une sortie quand :
  //  - le timer atteint la valeur contenue dans un registre de comparaison
  //  - le timer passe de l'overflow (débordement) à 0

  // registre de contrôle TCCR0A :
  //  - COM0A1 = 1 et COM0A0 = 0 -> clear OC0A on Compare Match, set OC0A at BOTTOM (0x00) -> non-inverting mode
  //  - COM0A1 = 1 et COM0A0 = 1 -> set OC0A on Compare Match, clear OC0A at BOTTOM (0x00) -> inverting mode

  // registres de comparaison :
  // - registres OCR0A et OCR0B pour le timer 0
  // - registres OCR1A et OCR1B pour le timer 1
  // - registres OCR2A et OCR2B pour le timer 2

  // timer0 (8 bits) -> fréquence PWM = f / prescaler x 256, avec :
  //  - f = fréquence du µc (Hz)
  //  - prescaler = diviseur de fréquence (prescaler par défaut = 64 -> fréquence PWM = 16000000 / 64 x 256 = 976,5625 Hz)

  // prescalers (diviseurs de fréquence) :
  //  - registre TCCR0B pour le timer 0 (1, 8, 64, 256 ou 1024)
  //  - registre TCCR1B pour le timer 1 (1, 8, 64, 256 ou 1024)
  //  - registre TCCR2B pour le timer 2 (1, 8, 32, 64, 128, 256 ou 1024)

  pinMode(6,OUTPUT);  // OC0A
  
  // mode FastPWM pour le timer 0 : registre TCCR0A = 0bXXXXXX11 (WGM01 et WGM00) et registre TCCR0B = 0bXXXX0XXX (WGM02)
  TCCR0A|=0b00000011;
  TCCR0B&=0b11110111;

  // non-inverting mode pour le timer 0 : registre TCCR0A = 0b10XXXXXX (COM0A1 et COM0A0)
  TCCR0A|=0b10000000; // bitSet(TCCR0A,COM0A1);
  TCCR0A&=0b10111111; // bitClear(TCCR0A,COM0A0);

  // prescaler = 1 (fréquence = 62500 Hz) pour le timer 0 : registre TCCR0B = 0bXXXXX001 (CS02, CS01 et CS00)
  TCCR0B|=0b00000001;
  TCCR0B&=0b11111001;

  // RAZ timer 0
  TCNT0=0;

  // la valeur de comparaison doit se trouver dans le registre OCR0A
  OCR0A=0;
  }

void loop()
  {
  if (OCR0A<255) OCR0A++;
  else OCR0A=0;
  delayMilliseconds(10);
  }



timer1_ATmega328.ino
// timer 1 ATmega328 en mode CTC
// philippe.notez@inmc.fr
// cc-by-nc-sa

volatile unsigned char etatLED=0;

// Interrupt Service Routine
ISR(TIMER1_COMPA_vect)  // overflow : ISR(TIMER1_OVF_vect)
  {
  digitalWrite(LED_BUILTIN,etatLED);
  etatLED=!etatLED;
  }

void setup()
  {
  // le timer 0 (registre TCNT0, 8 bits) est utilisé par les fonctions "delay", "millis", "micros" et "analogWrite" (PWM) sur D5 et D6
  // le timer 1 (registre TCNT1, 16 bits) est utilisé par la bibliothèque Servo et la fonction "analogWrite" (PWM) sur D9 et D10
  // le timer 2 (registre TCNT2, 8 bits) est utilisé par la fonction "analogWrite" (PWM) sur D3 et D11

  // en mode normal, le timer compte à partir de 0 (ou à partir d'une valeur prédéfinie) jusqu'à l'overflow (débordement), puis revient à 0 (ou à une valeur prédéfinie)
  // t = (overflow + 1) x prescaler / f, avec :
  //  - t = durée du timer (s)
  //  - overflow = dépassement de la limite de comptage du timer (255 pour un timer 8 bits, 65535 pour un timer 16 bits)
  //  - prescaler = diviseur de fréquence
  //  - f = fréquence du µc (Hz)

  // en mode CTC (Clear Timer on Compare), le timer compte à partir de 0 (ou à partir d'une valeur prédéfinie) jusqu'à atteindre la valeur contenue dans un registre de comparaison,
  // puis revient à 0 (ou à une valeur prédéfinie)
  // t = (threshold + 1) x prescaler / f, avec :
  //  - t = durée du timer (s)
  //  - threshold = valeur contenue dans un registre de comparaison
  //  - prescaler = diviseur de fréquence
  //  - f = fréquence du µc (Hz)

  // prescalers (diviseurs de fréquence) :
  //  - registre TCCR0B pour le timer 0 (1, 8, 64, 256 ou 1024)
  //  - registre TCCR1B pour le timer 1 (1, 8, 64, 256 ou 1024)
  //  - registre TCCR2B pour le timer 2 (1, 8, 32, 64, 128, 256 ou 1024)
  
  // registres de comparaison :
  // - registres OCR0A et OCR0B pour le timer 0
  // - registres OCR1A et OCR1B pour le timer 1
  // - registres OCR2A et OCR2B pour le timer 2

  // registre "drapeaux" TIFR0 pour le timer 0 (la levée d'un drapeau déclenche une interruption, le drapeau est remis à zéro à la fin de l'interruption) :
  //  - le bit TOV0 passe à 1 quand le timer atteint l'overflow (la levée du drapeau se fait au prochain cycle d'horloge, on retire donc 1 à l'overflow)
  //  - le bit OCF0A passe à 1 quand le timer atteint la valeur du registre de compararaison OCR0A (la levée du drapeau se fait au prochain cycle d'horloge, on retire donc 1 au seuil)
  //  - le bit OCF0B passe à 1 quand le timer atteint la valeur du registre de compararaison OCR0B (la levée du drapeau se fait au prochain cycle d'horloge, on retire donc 1 au seuil)
  // registre "drapeaux" TIFR1 pour le timer 1 (la levée d'un drapeau déclenche une interruption, le drapeau est remis à zéro à la fin de l'interruption) :
  //  - le bit TOV1 passe à 1 quand le timer atteint l'overflow (la levée du drapeau se fait au prochain cycle d'horloge, on retire donc 1 à l'overflow)
  //  - le bit OCF1A passe à 1 quand le timer atteint la valeur du registre de compararaison OCR1A (la levée du drapeau se fait au prochain cycle d'horloge, on retire donc 1 au seuil)
  //  - le bit OCF1B passe à 1 quand le timer atteint la valeur du registre de compararaison OCR1B (la levée du drapeau se fait au prochain cycle d'horloge, on retire donc 1 au seuil)
  // registre "drapeaux" TIFR2 pour le timer 2 (la levée d'un drapeau déclenche une interruption, le drapeau est remis à zéro à la fin de l'interruption) :
  //  - le bit TOV2 passe à 1 quand le timer atteint l'overflow (la levée du drapeau se fait au prochain cycle d'horloge, on retire donc 1 à l'overflow)
  //  - le bit OCF2A passe à 1 quand le timer atteint la valeur du registre de compararaison OCR2A (la levée du drapeau se fait au prochain cycle d'horloge, on retire donc 1 au seuil)
  //  - le bit OCF2B passe à 1 quand le timer atteint la valeur du registre de compararaison OCR2B (la levée du drapeau se fait au prochain cycle d'horloge, on retire donc 1 au seuil)
  
  // registre d’interruption TIMSK0 pour le timer 0 :
  //  - le bit TOIE0 autorise une interruption (levée du drapeau TOV0) quand le timer atteint l'overflow
  //  - le bit OCIE0A autorise une interruption (levée du drapeau OCF0A) quand le timer atteint la valeur du registre de comparaison OCR0A
  //  - le bit OCIE0B autorise une interruption (levée du drapeau OCF0B) quand le timer atteint la valeur du registre de comparaison OCR0B
  // registre d’interruption TIMSK1 pour le timer 1 :
  //  - le bit TOIE1 autorise une interruption (levée du drapeau TOV1) quand le timer atteint l'overflow
  //  - le bit OCIE1A autorise une interruption (levée du drapeau OCF1A) quand le timer atteint la valeur du registre de comparaison OCR1A
  //  - le bit OCIE1B autorise une interruption (levée du drapeau OCF1B) quand le timer atteint la valeur du registre de comparaison OCR1B
  // registre d’interruption TIMSK2 pour le timer 2 :
  //  - le bit TOIE2 autorise une interruption (levée du drapeau TOV2) quand le timer atteint l'overflow
  //  - le bit OCIE2A autorise une interruption (levée du drapeau OCF2A) quand le timer atteint la valeur du registre de comparaison OCR2A
  //  - le bit OCIE2B autorise une interruption (levée du drapeau OCF2B) quand le timer atteint la valeur du registre de comparaison OCR2B
  
  pinMode(LED_BUILTIN,OUTPUT);

  SREG&=0b01111111; // noInterrupts();
    
  // exemple : si on utilise le timer 1 en mode CTC avec un temps = 1 s, un prescaler = 1024 et une fréquence = 16000000, on obtient un seuil = 15625 - 1 = 15624
  // mode CTC pour le timer 1 : registre TCCR1A = 0bXXXXXX00 (WGM11 et WGM10) et registre TCCR1B = 0bXXX01XXX (WGM13 et WGM12)
  TCCR1A&=0b11111100;
  TCCR1B|=0b00001000; // bitSet(TCCR1B,WGM12);
  TCCR1B&=0b11101111; // bitClear(TCCR1B,WGM13);
  // prescaler = 1024 pour le timer 1 : registre TCCR1B = 0bXXXXX101 (CS12, CS11 et CS10)
  TCCR1B|=0b00000101;
  TCCR1B&=0b11111101;
  // la valeur de comparaison doit se trouver dans le registre OCR1A
  OCR1A=15624;
  // registre TIMSK1 = 0bXXXXXX1X (OCIE1A)
  TIMSK1|=0b00000010;
  // RAZ timer 1
  TCNT1=0;
  
  SREG|=0b10000000;	// interrupts();
  }

void loop()
  {
  }



Documentation complémentaire


Haut de page