LOCODUINO

Les Timers

Les Timers (III)

Comparaisons et interruptions

.
Par : Christian

DIFFICULTÉ :

Nous allons voir dans ce troisième article le rôle des registres OCR (Output Compare Register). Ces registres ont la même taille que le timer auquel ils sont associés et il y en a deux par timers, repérés par les lettres A et B. Pour le timer 2 qui a une taille de 8 bits, nous trouvons donc deux registres de 8 bits appelés OCR2A et OCR2B, et ces deux registres jouent des rôles assez identiques ; nous n’en détaillerons qu’un seul (OCR2A) mais vous trouverez dans la documentation du constructeur comment utiliser OCR2B.

Rôle d’un registre OCR

On peut charger un nombre dans le registre OCR2A ; lorsque le timer 2, en comptant, devient égal à OCR2A (les deux valeurs correspondent, ce qui se dit Match en anglais), cela provoque la mise à 1 d’un flag appelé OCF2A , qui est le bit 1 du registre TIFR2 (Timer/Counter 2 Interrupt Flag Register) et si on a autorisé les interruptions, ce flag va commander une interruption qui sera traitée par la routine d’interruption ISR(TIMER2_COMPA_vect). Pour autoriser les interruptions, il faut les autoriser de façon générale (avec sei() ou bien interrupts() suivant que vous utilisez le C ou le langage Arduino) et il faut aussi autoriser l’interruption produite par le flag OCF2A . Ceci se réalise en positionnant à 1 le bit OCIE2A (Timer/Counter 2 Output Compare Match A Interrupt Enable). Ce bit OCIE2A est le bit 1 du registre TIMSK2 (Timer/Counter 2 Interrupt Mask Register).

Ce que nous venons de dire doit certainement vous rappeler ce que nous avions développé dans l’article Les Timers (II), et si vous aviez bien compris ce précédent article, vous ne devez pas avoir de problème pour suivre celui-ci.

Le mode CTC

Dans les deux premiers articles, nous avions utilisé notre timer 2 dans un mode normal et il avait été nécessaire de positionner à 0 trois bits appelés WGM2 situés dans les registres TCCR2A et TCCR2B (voir les deux précédents articles Les Timers (I) et Les Timers (II)). Nous allons découvrir aujourd’hui un autre mode appelé CTC (Clear Timer on Compare Match Mode) ; dans ce mode, le timer/counter est remis à zéro quand sa valeur correspond à celle du registre OCR2A. Le timer 2 compte traditionnellement de 00 (appelé BOTTOM) à FF (appelé MAX) ; grâce au mode CTC, nous pouvons le faire compter de BOTTOM à la valeur de OCR2A (appelée TOP).

En effet, dans les deux premiers articles Les Timers (I) et Les Timers (II), nous faisions compter notre timer de 6 à 255 (FF) pour qu’il compte 250 coups, et nous lui faisions faire cela 125 fois pour obtenir un temps total égal à la demi-période de clignotement de notre DEL. Nous allons faire exactement la même chose, avec les mêmes paramètres, pour obtenir le même résultat (clignotement de la DEL du module Uno à la fréquence de 1 Hz), mais cette fois, nous ferons compter le timer de 0 à 250. Pour comprendre ces chiffres, reportez-vous au paragraphe « Calcul théorique du comptage » de l’article Les Timers (I).

Différence entre mode normal et mode CTC

Figure 1
Figure 1

La figure 1 montre comment notre timer 2 comptait en mode normal ; nous avons tracé une droite allant de 00 à FF, mais nous aurions dû plutôt dessiner un escalier puisque le compteur s’incrémente d’une unité à la fois. Pour obtenir 250 coups, nous chargions le timer avec la valeur 6 (256 – 250), dans la routine d’interruption, si bien que le timer comptait de 6 à 255 puis débordait, provoquant l’interruption générée par le flag TOV2 , interruption qui remettait à zéro le timer.

Figure 2
Figure 2

La figure 2 montre notre timer 2 dans le mode CTC ; il compte de 00 à TOP (qui vaut OCR2A soit 250) et lorsqu’il atteint cette valeur, il est remis à zéro et une interruption est générée par le flag OCF2A .

Programme par le mode CTC

Nous avons tout sous la main pour réécrire notre programme de clignotement de la DEL du module Uno (raccordée à la sortie 13). Voyons quels sont les bits à positionner à 1 dans les différents registres de contrôle.

Le prédiviseur doit diviser la fréquence de l’horloge (16 MHz) par 256 ; les bits CS22 , CS21 et CS20 doivent être respectivement égaux à 1, 1, 0. Ce sont les bits 0 à 2 du registre TCCR2B.

Le timer doit être utilisé en mode CTC ; pour cela, il faut que les bits WGM22 , WGM21 et WGM20 soient respectivement égaux à 0, 1, 0. Le bit WGM22 est le bit 3 du registre TCCR2B et les bits WGM21 et WGM20 sont les bits 0 et 1 du registre TCCR2A.

Enfin, il faut autoriser l’interruption générée par le flag OCF2A en positionnant à 1 le bit OCIE2A qui est le bit 1 du registre TIMSK2.

On retrouve dans la documentation du constructeur l’emplacement des différents bits des registres de contrôle et leur utilité. Dans les schémas ci-dessous, on a mis en jaune les bits à positionner à 1.

TCCR2A = 0b00000010

PNG - 13.4 kio

TCCR2B = 0b00000110

PNG - 12.5 kio

TIMSK2 = 0b00000010

PNG - 10.8 kio
// Clignotement d'une LED (mot anglais pour DEL) à 1 Hz par le mode CTC et une 
// interruption en provenance du timer 2, flag OCF2A, pendant que le programme 
// principal fait clignoter une autre LED (période 10s).
     
const byte Led = 13; // Pour utiliser la LED du module
const byte Led2 = 5; // Led clignotante du programme principal
#define LedToggle digitalWrite (Led, !digitalRead(Led))
#define Led2Toggle digitalWrite (Led2, !digitalRead(Led2))
    
void setup () {
  pinMode (Led, OUTPUT);
  pinMode (Led2, OUTPUT);
  cli(); // Désactive l'interruption globale
  TCCR2A = 0b00000010;
  TCCR2B = 0b00000110; // Clock / 256 soit 16 micro-s et WGM22 = 0
  TIMSK2 = 0b00000010; // Interruption locale autorisée par OCIE2A
  OCR2A = 250;
  sei(); // Active l'interruption globale
}
    
byte varCompteur = 0; // La variable compteur
     
// Routine d'interruption
ISR(TIMER2_COMPA_vect) {
  if (varCompteur++ > 125) { // 125 * 4 ms = 500 ms (demi-période)
    varCompteur = 0;
    LedToggle;
  }
}
     
void loop () {
  // Mettre ici le programme. Exemple :
  Led2Toggle;
  delay (5000); // Demi-période de la deuxième LED
}

Ce programme fait exactement la même chose que le programme de l’article Les Timers (II), mais cette fois le timer 2 est commandé par le mode CTC.

Les autres timers

N’allez pas imaginer qu’il suffit de recopier un programme et de changer le numéro du timer et des registres de contrôle pour que cela fonctionne, car vous auriez tout faux. En effet, les bits des registres de contrôle ne s’utilisent pas toujours de la même façon. Comparons par exemple les tableaux des bits permettant de régler le prédiviseur, pour les timers 1 et 2, en examinant la documentation du constructeur. Pour diviser la fréquence de l’horloge par 256 (comme dans notre programme), il faut que les trois premiers bits du registre TCCR2B soient égaux à 1 1 0 pour le timer 2, mais pour le timer 1, les trois premiers bits du registre TCCR1B doivent être égaux à 1 0 0. Ceci est montré par la figure 3.

Figure 3
Figure 3

De même, pour passer le timer en mode CTC, il faut que les bits WGM22 , WGM21 et WGM20 soient égaux à 0 1 0 pour le timer 2 et à 1 0 0 pour le timer 1 (ces bits s’appellent alors WGM12 , WGM11 et WGM10 car c’est pour le timer 1 auxquels se rajoute un autre bit WGM13  ; ces quatre bits sont répartis dans les registre TCCR1A et TCCR1B). Ceci est montré par la figure 4. On voit qu’il est extrêmement important, quand on veut utiliser les timers, de bien étudier la documentation du constructeur, mais cela n’a rien de compliqué si vous avez bien compris les principes décrits dans cette série d’articles.

Figure 4
Figure 4

Timer 1

En guise de conclusion, je vous propose d’utiliser maintenant le timer 1, qui est sur 16 bits, pour faire clignoter notre DEL à la fréquence d’un Hertz. Vous verrez que ce n’est pas plus compliqué, c’est même plus simple car sur 16 bits, le cycle du timer 1 est plus long, et il n’est plus nécessaire de faire appel à la variable varCompteur qui comptait jusqu’à 125.

Nous allons dans un premier temps, diviser notre fréquence horloge par 256 et faire tourner notre timer à la fréquence de 16 MHz / 256 = 62500 Hz, soit une période de 16 µs. Pour faire clignoter notre DEL à 1 Hz, il faut inverser cette LED toutes les 500 ms (demi-période) ; Il faut donc que notre timer compte 500 ms / 16 µs = 500000 / 16 = 31250 fois. C’est cette valeur qu’on chargera dans le registre OCR1A et cela rentre puisque ce registre est également de 16 bits. En faisant référence à la documentation du constructeur, on voit tout de suite les bits qu’il faut positionner à 1, d’une part pour régler le prédiviseur, d’autre part pour faire passer le timer en mode CTC. Dans les schémas ci-dessous, on a mis en jaune les bits à positionner à 1.

TCCR1A = 0b00000000

PNG - 12.1 kio

TCCR1B = 0b00001100

PNG - 12.7 kio

TIMSK1 = 0b00000010

PNG - 10.5 kio

OCR1A = 31250

Voici donc le programme, que vous pouvez comparer au précédent :

// Clignotement de la LED (mot anglais pour DEL) avec une interruption 
// générée par le mode CTC du timer 1 - Fréquence 1 Hz
    
const byte ledPin = 13;
    
void setup() {
  pinMode (ledPin, OUTPUT);
  noInterrupts();
  TCCR1A = 0;
  TCCR1B = 0b00001100;
  TIMSK1 = 0b00000010;
  TCNT1 = 0;  
  OCR1A = 31250; 
  interrupts();
}
    
ISR(TIMER1_COMPA_vect)
{
  digitalWrite (ledPin, !digitalRead (ledPin)); // LED Toggle 
}
     
void loop() {
  // Mettre ici le programme principal.
}

Conclusion

Utiliser les timers n’est vraiment pas compliqué ; il suffit seulement de bien comprendre l’importance des bits des registres de contrôle, de manière à les positionner à 0 ou à 1 en fonction de ce que l’on veut faire. Il faut aussi faire attention de ne pas mal positionner des bits qu’on n’utilise pas, comme par exemple les bits ICNC1 ou ICES1 dont nous n’avons pas parlé et qui servent pour les captures en entrée. On peut soit manipuler la valeur du registre, soit manipuler les bits en les mettant à 0 (bitClear) ou à 1 (bitSet). Concernant les bits dont nous n’avons pas parlé, il y a là matière pour d’autres articles ; j’espère seulement, avec ces trois premiers articles, avoir éveillé votre curiosité et surtout avoir réussi à démystifier les timers. Les timers servent aussi à fabriquer de la PWM (Pulse Width Modulation ou Modulation par Largeur d’Impulsion), mais ceci est aussi une autre histoire.

34 Messages

  • Les Timers (III) 3 février 2015 16:19, par Francis8

    Merci une nouvelle fois pour cet article passionnant et permettant d’y voir plus clair dans ce qui semble être un complexe et qui ne l’est pas tant que ça au final.

    Dans les programmes d’exemple, vous modifier les valeurs des registres en adressant directement le nom du registre en majuscule. En cas de manipulation de bit de ces mêmes registres (ou d’autres), peut-on procéder de la même façon ?

    Par exemple, si on désire modifier le bit CS12 du registre TCCR1B, peut-on écrire :
    CS12 = 1;
    Je ne me rappel plus ce qu’Arduino permet et ne permet pas dans les notations lorsqu’on s’adresse aux registres et aux bits.

    Il me semble, qu’on peux par exemple pour mettre un bit à 1, utiliser cette expression :
    TCCR1B |= _BV(CS12);

    Répondre

  • Les Timers (III) 4 février 2015 13:25, par Jean-Luc

    Je réponds pour Christian qui est absent en ce moment.

    En fait pouvoir écrire dans un registre particulier comme TCCR1B en Arduino ou en C repose sur les pointeurs. En effet, les registres sont implantés à des adresses mémoire particulières. Comme pour les autres registres, TCCR1B est une macro qui défini un déréférencement de pointeur. Donc si addr_tccr1b est l’adresse du registre en mémoire, TCCR1B est défini comme (*(volatile uint8_t *)(addr_tccr1b)). et donc écrire TCCR1B = 0 provoque l’écriture de 0 à l’adresse addr_tccr1b en mémoire et donc l’écriture dans le registre.

    Les bits nommés comme CS12 sont seulement définis comme des offset à l’intérieur d’un octet (on ne peut pas définir un pointeur vers un bit, c’est impossible). Donc on ne peut pas écrire CS12 = 1; car CS12 est équivalent à 2 et 2 = 1; produirait une erreur de compilation.

    _BV est une macro définie comme ceci :

    #define _BV(bit) (1 << (bit))

    et donc écrire

    #define _BV(bit) (1 << (bit))>
    TCCR1B |= _BV(CS12);

    à pour effet de lire en mémoire à l’adresse de TCCR1B, faire un ou bit à bit avec 1 << 2 (donc le bit correspondant à CS12 ) et réécrire le résultat en mémoire à l’adresse de TCCR1B. Ce qui fait le travail voulu.

    Répondre

    • Les Timers (III) 15 février 2015 12:55, par Christian Bézanger

      Merci à Jean-Luc qui a répondu mieux que je n’aurais été capable de le faire moi-même.

      Comme le dit Francis8, tant qu’on n’a pas essayé de manipuler les timers, cela peut paraître complexe, mais en fait il n’en est rien : il faut simplement bien comprendre le rôle des registres de contrôle et bien faire attention à ce qu’on écrit dans ces registres. Le nom des registres est connu de l’IDE d’Arduino, ce qui simplifie bien la tâche.

      En conséquence, les articles qui ont été écrits pour la carte Arduino Uno (µC ATmega328P) peuvent facilement être adaptés pour une autre carte, comme par exemple la carte Leonardo (µC ATmega32u4), mais pour cela, il faut se pencher sur la documentation constructeur pour connaître ces fameux registres de contrôle et les bits associés.

      J’espère très sincèrement que cette série d’articles vous donnera envie d’essayer de programmer vos timers.

      Répondre

  • Les Timers 18 juin 2016 14:33, par Cisco

    Bonjour,

    Je dois créer un PWM de 20kHZ et un rapport cycle de 0,42 pour la conception d’un convertisseur DC DC ( BUCK). J’ai fait des recherches et la plupart des gens utilisent le TIMER 1 pour générer cette fréquence.

    Ma question est pourquoi le choix du TIMER 1 et pas le 0 ou 2 ? Il y a t-il un interet particulier pour ce choix ou c’est juste dû au choix de la pin qu’on veut utiliser ?

    Cordialemnt
    DC

    Répondre

    • Les Timers 19 juin 2016 00:30, par Christian

      Logiquement, vous pouvez utiliser n’importe lequel des trois timers pour générer de la PWM ; je vous invite d’ailleurs à lire le quatrième volet de la série sur les timers où on décortique la façon dont la PWM est produite. A la fin, deux exemples sont donnés pour fabriquer une PWM de rapport cyclique 50% avec le timer 2.

      L’intérêt d’utiliser le timer 1 est qu’il opère sur 16 bits au lieu de 8 ; on peut donc réaliser des choses bien plus précises. Raison pour laquelle les gens le choisissent en premier. Le choix de la pin me parait secondaire par rapport au fait d’obtenir exactement le signal qu’on veut.

      Cordialement.

      Répondre

  • Les Timers 21 juin 2016 17:47, par Cisco

    Merci pour reponse. C’est bien clair maintenant.

    Répondre

  • Les Timers (III) 31 mars 2018 16:34, par alexandre

    bonjour,
    j’aurai un question car j’ai essayé le code où le timer est en mode CTC.
    j’aurai voulu savoir comment remplacer le digitalwrite(ledPin, !digitalRead(ledPin)) ;
    avec PORTB = 0b10000000 ;

    Répondre

    • Les Timers (III) 31 mars 2018 17:11, par christian

      Bonjour Alexandre,

      Tout ceci est expliqué dans le chapitre 28 (La face cachée des fonctions) de mon cours d’électronique que vous pouvez télécharger gratuitement à partir de cet article : Démarrer en électronique
      La manipulation directe des ports et des registres associés est intéressante soit pour réduire le code et gagner de la place, soit pour aller plus vite dans une opération de lecture-écriture, soit encore pour positionner plusieurs bits simultanément.
      La question à se poser est : en ai-je vraiment besoin ? Si ce qui vous intéresse est d’apprendre à le faire, alors la lecture de ce chapitre sera bien meilleure que les explications que je pourrais donner ici.
      Bonne lecture.

      Christian

      Répondre

      • Les Timers (III) 31 mars 2018 19:01, par alexandre

        merci pour ce lien et votre réponse rapide.
        alors pour vous répondre, oui dans mon cas j’en ais besoin car je souhaite positionner plusieurs bit simultanément.
        ceci dit je ne voit pas comment effectuer l’inversion

        Répondre

        • Les Timers (III) 31 mars 2018 19:29, par christian

          Seuls 6 bits (0 à 5) du PORTB sont reliés aux sorties de la carte Uno, les bits 6 et 7 sont utilisés par le quartz (voir figure 28.1).
          Chacun de ces 6 bits peut être lu ou écrit.
          Pour inverser un bit, il suffit d’écrire sur ce bit une valeur égale à 1 - valeur actuelle du bit.
          Si le bit est 1, on écrit 1 - 1 donc 0
          Si le bit est 0, on écrit 1 - 0 donc 1
          On inverse bien le bit considéré. Le calcul étant fait pour chacun des bits qui vous intéressent, il suffit ensuite de reconstituer l’octet et de l’écrire avec PORTB.
          Ainsi, vos bits sont positionnés à ce que vous voulez en une seule fois.

          Répondre

          • Les Timers (III) 31 mars 2018 20:18, par alexandre

            merci beaucoup.
            je commmence un peut a comprendre
            au passage vos cours sont bien expliquer

            Répondre

  • Les Timers (III) 7 mai 2019 10:00, par Jeremy

    Bonjour, je souhaiterai générer une PWM de fréquence 4kHz sur les pins 9 et 10.
    Pour cela je pensais modifier le timer 1 en rajoutant ces deux lignes de commandes à mon codes :
    TCCR1B &= 0xF8 ;
    TCCR1B |= B00000010 ;
    Est-ce correct ?

    Répondre

    • Les Timers (III) 7 mai 2019 11:28, par Christian

      Bonjour Jeremy,
      Tout ce qui concerne la génération de la PWM est expliqué dans l’article suivant "Les Timers (IV)" que je vous invite à lire.
      Déjà une bonne nouvelle : si vous voulez récupérer cette PWM sur les broches 9 ou 10, c’est bien avec le Timer 1 qu’il faut travailler.
      L’article explique entre autre comment régler le diviseur d’horloge (prescaler) et vous pourrez vous inspirer des exemples donnés pour obtenir une PWM à 4 kHz. Attention, le Timer 1 compte sur 16 bits et non 8... et de plus, la bibliothèque Servo ne sera plus disponible puisqu’elle utilise aussi ce timer.
      Cordialement.
      Christian

      Répondre

  • Initialisation valeur TOP du TIMER 1 en mode CTC .... 11 mai 2019 14:25, par Michel RIAZUELO

    Bonjour à tous,
    Je ne peux poser ma question sans préalablement remercier les rédacteurs des articles de LOCODUINO ...
    Clarté, précision, pédagogie et en supplément, une grande qualité de rédaction ... aussi agréable que rare :-)

    Ma question ...
    Est-il possible de "charger" la valeur TOP dans OCR1A pendant que le comptage est en cours (juste après l’overflow et éventuellement dans la routine d’interruption) sans perturber le comptage ?

    L’intérêt de la manip est de ne pas être pénalisé par le durée du calcul de la valeur TOP, quand cette valeur n’est pas fixe bien sûr.

    Merci par avance pour la réponse,
    Michel

    Répondre

    • Bonjour Michel,

      J’ai bien pris en compte votre question mais y répondre va sans doute me demander un peu de temps car il faut pour cela se replonger dans la doc du µC. J’ai en effet souvenir que modifier certains registres en cours de comptage peut modifier le comptage en cours. Pour le cycle suivant, cela repart à zéro avec les nouvelles valeurs en registre, mais là on parle de ce qui se passe pour le comptage en cours et il vaudrait mieux ne pas le perturber s’il a besoin d’être très précis.
      Si vous lisez l’anglais, téléchargez la notice du ATmega328 sur le site du constructeur et jetez y un oeil : on confrontera ce qu’on aura trouvé prochainement.
      Cordialement.

      Christian

      Répondre

      • Initialisation valeur TOP du TIMER 1 en mode CTC .... 12 mai 2019 10:45, par Michel RIAZUELO

        Bonjour Christian et merci pour la réaction rapide,

        Pour être franc, je n’aurais pas pensé il y a encore peu de temps que j’irais me plonger dans l’intimité du microcontrôleur ATmega328. Je suis très reconnaissant en même temps qu’admiratif pour ceux qui ont rendu sa contribution essentielle à nos projets via l’IDE d’ARDUINO. Je sais par expérience que ce genre d’exploration est chronophage et ... que le temps est compté :-)
        Je peux rajouter qu’il en est de même pour les timers dont je connais l’existence depuis le début de mon utilisation (très ancienne) des cartes ARDUINO mais qui me semblaient un peu trop près du cœur de la bête pour être simple d’emploi.

        C’est le code d’un ami qui avait "plongé" dans l’usage des timers qui m’a motivé et, forcément, utiliser les timers conduit un jour ou l’autre à se plonger dans la doc du microcontrôleur.

        Ce petit préambule est à destination des pratiquants de mon espèce, afin qu’ils se décident à faire le pas ...

        La doc de l’ATmega328 (DS40002061A) fait plus de 600 pages et je me permets de dire qu’elle est bien faite ! J’ai trouvé en moins de 2 mn l’endroit ou pourrais se trouver la réponse à ma question, et c’est pour moi un critère auquel bien peu de doc de lave-linge répondent :-)

        C’est à la fin du paragraphe 16-2-1 et ça dit :
        The TOP value, or maximum Timer/Counter value, can in some modes of operation be defined by either the
        OCR1A Register, the ICR1 Register, or by a set of fixed values. When using OCR1A as TOP value in a PWM
        mode, the OCR1A Register can not be used for generating a PWM output. However, the TOP value will in this case be double buffered allowing the TOP value to be changed in run time. If a fixed TOP value is required, the ICR1 Register can be used as an alternative, freeing the OCR1A to be used as PWM output.

        La partie en gras semble dire qu’on peut changer OCR1A (via son buffer) pendant que le compteur tourne ... Et tel que c’est dit, on peut penser que cette façon de procéder est courante.

        Je me pose ces questions existentielles car l’idée de cette pratique m’est venu alors que je ne suis pas chez moi et que je ne peux pas tester en live. Lundi soir, le rapprochement entre ce qui devrait être et ce qui est ce qui est sera fait ! On sait tous que cela peut réserver des surprises ...

        Dans ma lecture diagonale des 600 pages j’ai identifié qu’il y a des endroits que mes connaissances ne me permettront pas d’explorer, mais que la rédaction est claire et accessible, il ne faut donc pas se laisser impressionner !

        En attendant le jugement des faits, merci Christian pour l’incitation à l’exploration de cette documentation.

        Bien cordialement,
        Michel

        Répondre

        • Heureux que vous ayez trouvé cette réponse.
          J’attends les tests en live pour la confirmation.

          Christian

          Répondre

          • Initialisation valeur TOP du TIMER 1 en mode CTC .... 29 septembre 2019 23:18, par Michel RIAZUELO

            Bonjour à tous,

            Je me préparais à faire un petit retour pour remercier les rédacteurs des articles sur les timers qui m’ont permis de découvrir et d’explorer leur monde merveilleux et présenter pour exemple un sujet sur lequel je travaille.

            Il s’agit de commander 4 servos de RC (en fait les 4 ESC d’un drone) dans le temps le plus court possible. La durée maxi si on les commande les uns après les autres est de 8 ms (4 x 2) ce qui pour mon application est trop long.
            J’ai donc imaginé un système qui réalise les commandes (sur 4 pins de l’ARDUINO) en décalant leurs débuts de 1 ms.

            Ainsi la durée maxi de transmission est de 5 ms (3 + 2, 3 pour le début du 4ème créneau et 2 étant la durée maxi d’un créneau de commande d’un servo et donc du dernier)
            J’ai donc fait un proto (qui marche fort bien) en utilisant le TIMER 1 en mode CTC et dont l’interruption réalise l’intégralité du travail en 7 passes, les actions sur le PORTD (pins qui commandent les servos) et l’enchainement.
            Cela se réalise en changeant à chaque passe la valeur de comparaison pour le déclenchement de l’interruption suivante.
            En "pistant" les différentes actions avec le moniteur, les choses semblent se passer comme attendu, un servo connecté à une des 4 sorties se comporte aussi comme attendu et l’oscilloscope confirme aussi que les broches de sorties évoluent comme attendu.

            C’est de ceci que je voulais parler mais patatras, en retrouvant mon post du 11 Mai, j’ai relu l’article sur le mode CTC et HORREUR, je me suis rendu compte que j’avais considéré que le TIMER continuait à compter après avoir lancé l’interruption. Ce comportement m’allait bien pour cadencer mon enchainement depuis le moment initial (avec RAZ de TCNT1) ou la "salve" des 4 créneaux est lancée.

            Hors il est dit qu’EN MÊME TEMPS que l’interruption est lancée il se remet à ZERO AUTOMATIQUEMENT ....

            J’ai donc refait toute une série de vérifications et il semble bien qu’avec mon code, ce n’est pas le cas.

            Cela me plonge évidemment dans un abime de réflexion. J’ai fait une version du code en considérant cette RAZ automatique en modifiant en conséquence les valeurs successives de OCR1A et j’obtiens n’importe quoi. Avec ce code, en forçant la RAZ de TCNT1 à chaque occurence de l’interruption, ça remarche !
            J’ai aussi constaté qu’à la fin du cycle, après le déclenchement des 7 interruptions, la valeur TCNT1 montre bien qu’il ne s’est pas remis à 0 !

             ?????
            Est-ce que ma façon de paramétrer le TIMER1 n’est pas orthodoxe ? Est-ce que je me laisse aveugler par une évidence ?

            Le code peut être récupéré ICI :

            Merci par avance aux curieux qui prendront le temps de chercher à comprendre. Je sais, c’est un souhait déraisonnable :-)

            Bien cordialement,
            Michel

            Répondre

            • Initialisation valeur TOP du TIMER 1 en mode CTC .... 29 septembre 2019 23:48, par Michel RIAZUELO

              SUITE ........

              Je viens de trouver à l’instant CECI qui pourrait aider ( un curieux !) à comprendre ...

              Répondre

            • Initialisation valeur TOP du TIMER 1 en mode CTC .... 30 septembre 2019 14:04, par Jean-Luc

              Bonjour,

              Savez vous qu’il existe la bibliothèque Servo qui fait exactement ce que vous cherchez à faire ?

              Cordialement

              Répondre

              • Initialisation valeur TOP du TIMER 1 en mode CTC .... 30 septembre 2019 15:22, par Michel RIAZUELO

                Merci Jean-Luc,

                Je connais la bibliothèque SERVO, je sais que je peux commander les servos avec la valeur en micros dont je dispose, mais je ne sais pas comment les choses se déroulent dans le temps, et les moyens de le savoir ne sont pas forcément simple ...
                Mais je peux creuser de ce coté là.

                Mais cela laisse entière la question que j’ai posée ... :-)

                Bien cordialement,
                Michel

                Répondre

                • Initialisation valeur TOP du TIMER 1 en mode CTC .... 6 octobre 2019 11:30, par Michel RIAZUELO

                  Bonjour à tous,

                  Voilà, j’ai réussi à faire ce que je recherchais, mais je ne suis pas satisfait, car je ne suis pas bien sûr de savoir pourquoi cela marche :-)
                  C’est moins gênant que de ne pas savoir pourquoi cela ne marche pas, mais ...

                  Pour faire bref, car ce n’est pas un sujet ferroviaire, avec le timer2 je décode une trame CPPM issue d’un récepteur de radiocommande, je mixe ces infos avec celles d’une centrale à inertie (à base d’un BNO055 pour les curieux) et avec le résultat, je commande avec le TIMER 1 les 4 moteurs d’un drone dont les interfaces de puissance se commandent comme des servos RC (créneaux de 1 à 2 ms).

                  J’utilise le TIMER1 avec le prescaler à 8 en mode CTC (comparateur) mais, et c’est mon interrogation (que je reformule !) contrairement à ce qui devrait se passer, TCNT1 ne se remet pas à 0 au moment de l’interruption.
                  A chaque interruption je "recharge" une nouvelle valeur dans OCR1A pour le job suivant à réaliser par l’interruption suivante. Les valeurs successives correspondent à ce qu’on pourrait appeler la "time line" des évènements (mise à 1 ou à 0 des 4 sorties).

                  Je suis donc certain que cette RAZ de TCNT1 ne se fait pas ...

                  Ci-dessous les paramètres de configuration du timer :
                  cli() ;//stop interrupts

                  // ****************************************** début timer 1 en mode CTC pour la génération des commandes ESC
                  //ATTENTION : contrairement à ce qui est attendu, pour une raison inconnue, TCNT1 ne repasse pas à ZERO au moment ou il atteint OCR1A
                  // TCNT1 est RAZ au début le la séquence et s’incrémente jusqu’au STEP 7
                  // cela fonctionnerait aussi s’il faisait sa RAZ à chaque interruption à condition de changer les valeurs de OCR1A à chaque STEP

                  //set timer1 interrupt pour générer la longueur du créneau
                  TCCR1A = 0 ; // set entire TCCR1A register to 0
                  TCCR1B = 0 ; // same for TCCR1B
                  TCNT1 = 0 ; //initialize counter value to 0

                  // Le timer va de 0 à OCR1A et à chaque overflow déclenche son interruption
                  OCR1A = 60000 ; // supérieur au cycle des trames

                  // turn on CTC mode
                  TCCR1B |= (1 << WGM12) ; // <---- CTC mode

                  TCCR1B = 0b00000010 ; // prescaler à 8 chaque pulse timer vaut 0.5 micros d’où 2000 pas de timer pour avoir 1000 micros

                  // enable timer compare interrupt
                  TIMSK1 |= (1 << OCIE1A) ; // <---- CTC mode
                  // ********************************************************* fin timer 1

                  sei() ;//allow interrupts

                  Il est possible que cette façon de préparer le TIMER ne soit pas "orthodoxe" ..... Ma crainte, c’est que cela ne marche pas de cette façon sur n’importe quelle carte ARDUINO.

                  A vot’ bon coeur :-)

                  Michel

                  Répondre

                  • Initialisation valeur TOP du TIMER 1 en mode CTC .... 6 octobre 2019 11:49, par Christian

                    Bonjour Michel,

                    Arduino est très utilisé dans la commande de drones et il existe vraisemblablement de nombreux forums où vous pourriez trouver de l’aide.
                    Pour ma part, je me sens impuissant à vous répondre car je ne connais pas la structure des trames CPPM issues du récepteur ni les informations en provenance de la centrale à inertie BNO055. Je ne peux donc donner aucun avis concernant la non RAZ de TCNT1.
                    De plus, ce sujet n’étant pas du modélisme ferroviaire, nous nous refusons à y consacrer le temps nécessaire. J’espère que vous nous comprendrez et je vous souhaite bonne chance pour trouver une aide plus efficace.
                    Cordialement.

                    Répondre

  • Les Timers (III) 14 mai 2019 09:07, par Christophe

    Bonjour,

    tout d’abord, merci pour ces explications sur l’utilisation des Timers.
    J’ai repris vos exemples et tenté de l’adapter à mon besoin mais sans y parvenir.
    Je me demande si c’est possible.... j’explique avec un exemple :
    J’aurais besoin de générer une interruption toutes les 39000 secondes (ça correspond à 10h20min)
    J’ai indiqué cette valeur dans OCR1A mais le comptage se fait trop rapidement et les interruptions se produises toutes les seconde environ. Peut-on utiliser le mode CTC dans mon cas ou sinon, comment pourrais-je y parvenir.
    Merci d’avance pour vos retour.
    Bonne journée

    Voici mon code de test :

    int compteur = 0 ;
    void setup()
    Serial.begin(9600) ;
    noInterrupts() ;
    TCCR1A = 0 ;
    TCCR1B = 0b00001101 ;
    TIMSK1 = 0b00000010 ;
    TCNT1 = 0 ;
    OCR1A = 39000 ; //39000 secondes soit 10h20min
    interrupts() ;

    ISR(TIMER1_COMPA_vect)

    Serial.println("Interruption") ;

    void loop()
    delay(1000) ;

    Répondre

    • Les Timers (III) 14 mai 2019 12:24, par Christian

      Bonjour,

      Ce n’est pas parce qu’on rentre 39000 dans le registre OCR que le timer comprend 39000 secondes. Cela n’a même rien à voir et je vous invite à relire tranquillement les deux premiers articles.
      C’est la fréquence de l’horloge et le prescaler qui permet de régler la vitesse de comptage d’un timer et même si on ralentit au maximum, le timer compte encore trop rapidement. Pour obtenir une durée de comptage très longue (à l’échelle des phénomènes électroniques), il faut donc gérer une variable et l’incrémenter chaque fois que notre timer déborde. Au bout d’un certain nombre de débordements, vous obtiendrez la durée souhaitée et cela nécessite de faire un calcul pour connaître ce nombre de débordements nécessaires. C’est d’ailleurs ce qu’on faisait en cherchant un événement toutes les secondes (inversion d’une LED).
      Dans votre cas personnel, qu’est-ce qui justifie le fait d’utiliser un timer plutôt que les fonctions C++ permettant de compter une durée (millis() par exemple) ?
      Cordialement.

      Christian

      Répondre

      • Les Timers (III) 15 mai 2019 20:37, par christophe

        Bonjour,

        Merci pour votre réponse.
        Les raisons pour lesquels je souhaitais utiliser les Timers est de ne pas trop monopoliser le déroulement du programme en comptant les secondes passées. J’ai besoin de compter les secondes pour démarrer progressivement 4 systèmes d’éclairage avec une heure d’allumage converti en seconde et les temps de variation eux aussi en seconde.

        Répondre

  • Les Timers (III) 15 mai 2019 21:55, par Jean-Luc

    Bonsoir,

    vous vous embêtez avec les timers. Regardez La bibliothèque ScheduleTable.

    Répondre

  • Les Timers (III) 16 mai 2019 10:07, par Christophe

    Bonjour,

    J’ai consulté votre lien et c’est une fonctionnalité que je ne connaissais pas. Votre conseille me semble très bon pour mon besoin. Je pense que çà vas simplifié mon programme. Un grand merci pour votre disponibilité. Je vais travaillé là dessus et vous fait un retour.
    Encore merci

    Christophe

    Répondre

    • Les Timers (III) 16 mai 2019 12:34, par Christian

      Jean-Luc a raison. L’utilisation des Timers dans un programme n’est pas forcément nécessaire d’autant que leur mise en oeuvre réclame une très bonne connaissance du microcontrôleur.
      Et c’est justement pour simplifier leur utilisation qu’il existe des fonctions spécialisées ou des bibliothèques comme celles développées par LOCODUINO.
      Cette série d’articles a été écrite pour faire découvrir ces objets mais leur maitrise nécessite tout de même beaucoup de travail.

      Répondre

  • Les Timers (III) 19 décembre 2019 16:45

    Bonjour, auriez vous d’autres exemples d’utilisations concrète du TC 1 ?
    Merci :)

    Répondre

  • Les Timers (III) 11 mars 2021 09:45, par aubriet louis

    Bonjour,

    Juste un message pour vous féliciter de la qualité et de la concision de vos articles qui sont excellents .

    J’ai appris enormement grâce a vous sur un sujet qui est pas facile et demande d’être rigoureux .

    Juste un commentaire :

    tous ça pour faire du modélisme ferroviaire ?

    Vous êtes des fous furieux !

    garder cette passion qui vous anime .

    Encore merci.

    Répondre

    • Les Timers (III) 11 mars 2021 10:07, par Christian

      C’est justement toute la question : dois-je me considérer plus comme un modéliste ferroviaire ou plus comme un électronicien ?
      Cela fait une trentaine d’années que je pratique les deux domaines, alors il était évident que je devais mettre un hobby au service de l’autre. Et comme je ne suis pas le seul, un petit groupe s’est constitué et a créé Locoduino : site d’électronique programmable mais spécialisé en modélisme ferroviaire. Notre but : mettre en public notre expérience pour que d’autres en profitent.
      Fous furieux ? Oui, sans doute. . . ;-)

      Répondre

  • Les Timers (III) 3 octobre 14:02, par françois

    Bonjour Monsieur Christian ,

    J’ai commencé à apprendre Arduino à l’âge de 68 ans , si je devais recommander un site pour apprendre la programmation , je recommanderais sans hésiter le vôtre : complet et tres compréhensif si on y met le temps et la volonté . J ’ai travaillé dans l’industrie et les chariots suspendus se déplaçaient par des rails aériens , ma crainte à l’époque etait la coupure de courant durant les transferts ..!
    Voici mes deux questions : 1) pourquoi l ISR ne se trouve pas dans la fonction Loop () . 2) en cas de coupure de courant quel est le premier programme que vous lancez , si le microprocesseur n’a plus la position de votre locomotive ( barriere(s) à abaisser , vitesse lente ..??

    Répondre

    • Les Timers (III) 7 octobre 12:00, par Christian

      Merci pour vos éloges : effectivement, toute l’équipe fait de son mieux pour partager son savoir.
      L’ISR ne se trouve pas dans loop car c’est un sous-programme qui ne doit s’exécuter que s’il y a une interruption (et pas tout le temps comme ce qui est inclus dans loop). Et comme chaque sous-programme ou fonction, c’est à part.
      En cas de coupure de courant, lorsque celui-ci est rétabli, le programme reprend au début (et donc oublie ce qu’il a fait avant). Pour ne pas perdre la position des trains, il faut utiliser des capteurs comme la détection d’occupation par consommation de courant et non des capteurs comme ILS, Hall ou IR.
      Mais si c’est la coupure de courant qui vous préoccupe, vous pouvez prévoir une alimentation de secours comme une batterie qui continue à fournir du courant s’il y a une coupure.
      En fait, tout dépend de votre projet : ajouter de la sécurité est possible, parfois au prix d’une électronique plus sophistiquée (donc plus chère). Par exemple, dans un avion de ligne, les instruments sont doublés et certains triplés, ce qui fait que le transport aérien est le plus sûr du monde. On pourrait en faire autant sur un réseau miniature, mais peu le font car nous sommes dans le domaine du jeu.

      Répondre

Réagissez à « Les Timers (III) »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Programmation »

Les derniers articles

Les articles les plus lus