LOCODUINO

Les Timers

Les Timers (V)

L’unité de capture d’entrée

.
Par : Christian

DIFFICULTÉ :

Nous avons appris dans les quatre premiers articles à utiliser les timers de nos microcontrôleurs ATmega328P équipant les modules Uno, Nano et Mini et même à générer notre propre PWM. Dans cet article, nous allons nous intéresser plus particulièrement au timer1 qui a la particularité d’opérer sur 16 bits contrairement aux timer0 et timer2 qui opèrent sur 8 bits. La lecture du présent article suppose d’avoir lu et compris les quatre premiers articles sur les Timers, Les Timers (I), Les Timers (II), Les Timers (III) et Les Timers (IV).

Structure du timer1

La figure 1 montre le schéma synoptique du timer1 ; dans cette figure, la lettre n qui est le numéro du timer vaut 1. On remarque tout d’abord une grande ressemblance avec la figure 1 de l’article Les Timers (IV).

Figure 1
Figure 1
Architecture du bloc timer à l’intérieur du microcontrôleur ATmega328P

Le signal d’horloge clkTn (en jaune) arrive à la logique de contrôle, en provenance de l’extérieur (Tn) ou bien de l’horloge interne et du prescaler. La logique de contrôle incrémente ou décrémente le timer TCNTn (en vert) et celui-ci est comparé aux valeurs des registres OCRnA et OCRnB (en bleu). Lorsqu’il y a égalité, un signal est envoyé à un bloc appelé « Waveform Generation » (Génération de la forme d’onde) (en rouge) et c’est ce bloc qui met en forme la PWM en positionnant à 0 ou à 1 le registre des broches OCnA ou OCnB (en violet). Remarquons au passage que cette PWM est générée sur les broches OC1A et OC1B qui correspondent aux broches 15 et 16 du microcontrôleur ATmega328P (en bleu foncé sur la figure 2), c’est-à-dire aux sorties 9 et 10 du module Arduino Uno comme le montre le schéma de câblage du module :

http://arduino.cc/en/uploads/Main/A...

En fait, nous n’allons pas nous intéresser à la génération de la PWM dans cet article puisque nous en avons parlé en détail dans l’article Les Timers (IV). Nous allons plutôt nous intéresser aux différences que ce timer1 présente par rapport au timer0 ou au timer2.

La première est que le timer1 opère sur 16 bits, donc les registres qu’on peut voir sur la figure 1 sont des registres de 16 bits, notamment le timer lui-même TCNT1 mais aussi OCR1A et OCR1B. Comme le bus de données (DATABUS) ne fait que 8 bits, cela implique que ces registres sont constitués d’une partie basse (L) comprenant les 8 premiers bits et d’une partie haute (H) comprenant les 8 bits suivants (par exemple TCNT1L et TCNT1H). Il faut donc deux opérations de lecture ou d’écriture au CPU pour lire ou écrire ces registres.

La deuxième différence est représentée en bleu turquoise sur la figure 1 : la présence d’un registre ICR1 (rappelons que n = 1 pour ce timer) appelé Input Capture Register ou encore registre de capture d’entrée. En fait, ce registre peut capturer la valeur du timer1 lors d’un événement externe donné soit sur la broche de capture d’entrée (Input Capture Pin ICP1), soit sur les broches du comparateur analogique. La broche ICP1 est représentée en bleu turquoise sur la figure 2 : elle correspond à la broche 14 du microcontrôleur ou encore à la sortie 8 du module Uno.

Figure 2
Figure 2
Brochage du microcontrôleur ATmega328P

Cet article va donc s’intéresser à ce registre de capture, à la façon de l’utiliser et à ce qu’on peut en faire.

L’unité de capture d’entrée

La figure 3 montre le schéma synoptique de l’unité de capture d’entrée (Input Capture Unit). Elle est extraite de la datasheet du microcontrôleur ATmega328P. Il faut se rappeler que n = 1 sur cette figure puisque seul le timer1 dispose d’une telle unité. De plus, tout ce qui est en grisé ne fait pas partie de l’unité proprement dite mais intervient dans son fonctionnement. Pour expliquer le fonctionnement de cette unité, nous nous limiterons à la capture d’un événement ayant lieu sur la broche d’entrée ICP1 entourée de jaune, mais le raisonnement est similaire pour un événement en provenance du comparateur analogique (en grisé).

Tout d’abord, il convient de définir ce qu’on appelle un événement sur cette broche ICP1 : tout changement de niveau logique sur cette broche, c’est-à-dire le passage de 0 à 5V (LOW à HIGH) ou bien le passage de 5 à 0 V (HIGH à LOW). Le passage de LOW à HIGH s’appelle un front ascendant (ou positif), le passage de HIGH à LOW s’appelle un front descendant (ou négatif). Le programmeur choisit de faire réagir l’unité de capture d’entrée soit aux fronts ascendants, soit aux fronts descendants, en positionnant un bit d’un registre comme nous le verrons plus loin.

Figure 3
Figure 3
Architecture du bloc d’unité de capture d’entrée à l’intérieur du microcontrôleur ATmega328P

C’est le détecteur de front (Edge Detector), entouré en rouge, qui confirme le changement de niveau logique sur la broche ICP1 et qui choisit s’il doit s’intéresser aux fronts ascendants ou bien descendants en fonction de la valeur du bit ICES . Pour arriver jusqu’au détecteur de front, le signal est passé par le réducteur de bruit (Noise Canceler), entouré en violet, qui permet éventuellement d’augmenter l’immunité au bruit du signal en effectuant ou non un filtrage en fonction du bit ICNC  ; on y reviendra plus en détails un peu plus loin. Toujours est-il que lorsque le détecteur de front repère un événement sur la broche ICP1, il envoie un signal d’écriture (WRITE) pour que la valeur binaire du timer1 (TCNT1), entouré en vert, soit recopiée dans le registre de capture d’entrée (Input Capture Register) ICR1, entouré en bleu turquoise, et au même instant, positionne à 1 le drapeau ICF1 (Input Capture Flag). Cette opération effectue une datation de l’événement puisqu’un timer, comme son nom l’indique, est fait pour compter le temps qui s’écoule.

Si le programmeur a autorisé les interruptions, le drapeau ICF1 génère une interruption appelée Input Capture Interrupt, qui repositionnera automatiquement à zéro le flag ICF1 lorsqu’elle sera exécutée, mais le programmeur peut aussi le faire lui-même et pour cela, il doit écrire un 1 logique à l’endroit du bit ICF1 . La lecture par le CPU du registre ICR1 se fait en commençant par les 8 bits de poids faible (ICR1L) puis les 8 bits de poids fort (ICR1H). Quand les bits de poids faible sont lus, les bits de poids fort sont copiés dans un registre temporaire TEMP, entouré en marron, et quand le processeur lit les bits de poids fort, il accède à ce registre temporaire.

Revenons un peu sur le réducteur de bruit (Noise Canceler). On l’autorise à jouer son rôle en positionnant à 1 le bit ICNC1  ; dans ce cas, l’entrée du réducteur de bruit surveille quatre échantillons durant quatre cycles d’horloges, et tous les quatre doivent être égaux pour changer la sortie qui est à son tour utilisée par le détecteur de front (Edge Detector). Cela introduit un petit retard égal à quatre cycles d’horloge avant de recopier le contenu du timer1 dans le registre ICR1. Le réducteur de bruit utilise l’horloge système et n’est donc pas affecté par le prescaler (voir le rôle du prescaler ou prédiviseur dans l’article Les Timers (I)).

Pour résumer, un changement de niveau logique sur la broche ICP1, éventuellement filtré par le réducteur de bruit (Noise Canceler), est pris en compte par le détecteur de front (Edge Detector) qui envoie un signal d’écriture pour recopier la valeur du timer1 (TCNT1) dans le registre de capture d’entrée (ICR1), tout en positionnant un drapeau ICF1 servant à générer une interruption.

Utilisation de l’unité de capture d’entrée

Le principal défi dans l’utilisation de l’unité de capture d’entrée est d’avoir suffisamment de disponibilité du processeur pour ne pas manquer des événements. En effet, le temps entre deux événements est critique ; si le processeur n’a pas lu la valeur capturée dans le registre ICR1 avant qu’un nouvel événement se produise, le registre ICR1 sera écrasé par la nouvelle valeur et la valeur précédente sera perdue. Le processeur doit donc lire au plus vite chaque nouvelle valeur du registre ICR1 dans la routine d’interruption générée par le drapeau ICF1 .

Le registre ICR1 peut aussi être utilisé pour définir la valeur TOP utilisée par le timer TCNT1 pour générer de la PWM et dans ce cas, la fonction de capture n’est plus possible. Cette configuration, très spéciale, est déterminée par la valeur des bits WGM13:0 du registre TCCR1A, notamment les combinaisons 1000, 1010, 1100 et 1110. Nous ne développerons pas cette possibilité ici, mais il faut être vigilant à la valeur de ces bits si on veut utiliser la fonction de capture.

Pour bien comprendre l’intérêt de l’unité de capture d’entrée, imaginons qu’un signal périodique tel que celui de la figure 4 soit appliquée à l’entrée ICP1.

Figure 4
Figure 4
Evénements sur la broche ICP1 du microcontrôleur ATmega328P

Supposons qu’on s’intéresse aux fronts ascendants, dans ce cas l’unité de capture donnera une datation pour chaque front ascendant (événements A et B de la figure par exemple). La différence entre deux datations consécutives, pour peu qu’on connaisse à quelle vitesse travaille le timer1, nous donne la période du signal, donc sa fréquence qui est l’inverse de la période.

On peut faire encore plus fort. Imaginons qu’après avoir obtenu une datation pour l’événement A (front ascendant), nous demandons à notre détecteur de front de s’intéresser aux fronts descendants ; nous obtiendrons alors une datation de l’événement C (front descendant) et la différence de datation entre l’événement A et l’événement C, toujours en connaissant la vitesse de travail du timer1, nous donne la durée où le signal est à l’état haut, ce que les anglais appelle duty cycle. Il suffit simplement, dans la routine d’interruption, de changer la valeur du bit ICES1 le plus vite possible après chaque lecture du registre ICR1. Après un changement de front, le flag ICF1 doit être remis à zéro par logiciel, c’est-à-dire en écrivant un 1 logique à son emplacement.

Le principe utilisé pour déterminer le « duty cycle » d’un signal peut être utilisé pour déterminer la durée d’enfoncement d’un bouton poussoir. On date l’enfoncement par la détection d’un premier front, puis son relâchement par la détection du front complémentaire, et on accède ainsi à la durée d’enfoncement. Le logiciel peut alors faire des choses différentes selon que le poussoir est enfoncé brièvement ou bien de façon plus prolongée, ce à quoi nous sommes habitués avec les appareils modernes.

Etude des registres de contrôle

Nous avons parlé de beaucoup de registres et également de certains bits qui jouent un rôle dans l’unité de capture d’entrée. Comme nous l’avions dit dans les précédents articles, utiliser les timers, c’est apprendre à utiliser leurs registres associés. Nous allons donc les passer en revue avant de traiter un exemple pratique. Bien entendu, nous nous limiterons aux registres 8 bits jouant un rôle dans l’utilisation de l’unité de capture d’entrée ; pour plus de détails, référez-vous à la datasheet du µC ATmega328P.

TC1 Control Register A ou encore registre de contrôle A du timer-compteur 1 (TCCR1A) est le premier de trois registres de contrôle du timer1 (repéré par la lettre A). Les bits 0 et 1 contiennent les bits WGM10 et WGM11 , deux des quatre bits servant à régler le mode de fonctionnement du timer1 (WGM pour Waveform Generation Mode ou encore mode de génération de la forme d’onde). Attention, les bits WGM12 et WGM13 ne sont pas les bits 2 et 3 de TCCR1A mais se retrouvent dans le registre de contrôle TCCR1B. Les bits 4 à 7 de TCCR1A sont les bits COM1 (Compare Output Mode for Channel ou encore mode de comparaison de sortie pour chaque canal) ; leur combinaison dépend de la combinaison des bits WGM13:0 , ce qui permet une fois de plus de sélectionner différents modes de fonctionnement du timer1.

TC1 Control Register B (TCCR1B) est le deuxième registre de contrôle (repéré par la lettre B) et est un peu plus intéressant pour l’utilisation de l’unité de capture d’entrée. Les bits 0, 1 et 2 sont les bits CS10 , CS11 et CS12 permettant de sélectionner la source de signaux d’horloge (CS signifie Clock Select ou encore sélection d’horloge). Par exemple, avec la combinaison 101 pour les bits CS12:0 , nous choisissons la sortie du prédiviseur (prescaler) qui divise le signal d’horloge par 1024, pour faire avancer le timer1. Dans les bits 3 et 4 se trouvent WGM12 et WGM13 dont nous avons parlé plus haut. Le bit 6 contient ICES1 (Input Capture Edge Select) : si le bit est égal à 0, le détecteur de front réagit aux fronts descendants, si le bit est égal à 1, le détecteur de front réagit aux fronts ascendants. Enfin, le bit 7 contient le bit ICNC1 (Input Capture Noise Canceler) qui, s’il est égal à 1, permet d’activer le réducteur de bruit (Noise Canceler) décrit plus haut.

TC1 Control Register C (TCCR1C) est le troisième registre de contrôle (repéré par la lettre C) mais ne présente pas un grand intérêt pour l’utilisation de l’unité de capture d’entrée. Les bits 6 et 7 contiennent les bits FOC1B et FOC1A , Force Output Compare for Channel A (ou B).

Timer/Counter 1 Interrupt Mask Register (TIMSK1) ou encore registre de masque d’interruption pour le timer1, est un registre de 8 bits qui permet d’autoriser ou d’inhiber les interruptions provenant de différents composants du timer1. Bien entendu, les interruptions doivent être autorisées de façon globale en positionnant le I-flag du Status Register. Par exemple, le bit 5 est le bit ICIE (Input Capture Interrupt Enable) qui, positionné à 1, permet de déclencher une interruption lorsqu’un événement a provoqué la recopie du timer1 dans le registre ICR1. Le bit 0 est le bit TOIE (Timer Overflow Interrupt Enable) qui permet de déclencher une interruption lorsqu’il y a débordement du timer1.

TC1 Interrupt Flag Register (TIFR1) est le dernier registre de contrôle de 8 bits, associé au timer1 et contient les différents drapeaux (flags) qui se positionnent à 1 lorsque des événements surviennent. Par exemple, dans le bit 5 de ce registre, on retrouve le drapeau ICF (Input Capture Flag) et dans le bit 0, on retrouve le drapeau TOV (Timer Overflow Flag). Le premier est mis à 1 lorsqu’il y a capture de la valeur du timer1 dans le registre ICR et le deuxième est mis à 1 lorsqu’il y a débordement du timer1. Rappelons ici que ces drapeaux sont automatiquement remis à 0 lorsqu’une routine d’interruption est exécutée ; pour les remettre à 0 de façon manuelle dans le programme, il faut écrire un 1 logique dans l’emplacement du bit considéré.

Datation d’un événement

La figure 5 montre que pour dater un événement, il faut savoir sur quel cycle du timer il a lieu.

Figure 5
Figure 5
Datation d’un événement

En effet, si nous ne connaissons pas le cycle sur lequel a lieu l’événement, c’est comme si tous les événements avaient lieu sur le même cycle, par exemple le cycle 1 et on voit que la durée entre événements ne veut plus rien dire. Or, il est très facile de connaître le cycle du timer1 ; chaque fois que celui-ci déborde, c’est-à-dire que sa valeur passe de 65535 à 0, le flag TOV se positionne à 1. Il suffit de compter le nombre de fois qu’il se positionne à 1. En examinant la figure 5, on sait que l’événement 2 a lieu après 2 cycles complets ; pour dater l’événement, il suffit de rajouter la valeur du timer1 (en temps) à la durée (connue) des 2 cycles précédents.

Programme de mise en application des connaissances

Voici un programme très simple qui permet de mettre en application ce que nous venons de dire sur l’unité de capture d’entrée. Il fonctionne avec un module Arduino/Genuino Uno et un bouton poussoir branché entre la broche 8 du module et la masse (voir figure 6). La broche 8 doit donc être déclarée en entrée et sa résistance de pull-up doit être activée. Le programme utilise aussi le moniteur série de l’IDE pour afficher, chaque fois qu’un appui est fait sur le poussoir, le nombre de cycles complètement écoulés du timer1, la date de l’événement observée avec la fonction millis() et la date de l’événement calculée avec les données du timer1 comme cela a été expliqué à la fin du paragraphe précédent [1]. Vous pourrez alors constater que les deux dernières valeurs sont très proches l’une de l’autre.

Figure 6
Figure 6
Montage à réaliser pour le programme

Commençons par décrire ce que nous voulons faire. Le programme fait travailler le timer1 en mode normal, en le faisant compter de 0 à 65535. Le prédiviseur (prescaler) est réglé pour diviser le signal d’horloge (de 16 MHz) par 1024, ce qui représente un pas de 64 µs (soit 0,064 ms). La durée d’un cycle de timer1 est donc de 4,194304 secondes (soit 4194,304 ms). Les calculs sont faits en millisecondes puisque la fonction millis() donne un résultat en millisecondes [2]. Le nombre de cycles complètement écoulés du timer1 au moment de l’événement (appui sur le poussoir) est donné par le nombre de fois que le timer1 a débordé ; la routine d’interruption augmente ce nombre d’une unité à chaque fois qu’elle est exécutée. La résistance de pull-up étant activée sur la broche 8, un appui sur le poussoir fera passer la broche 8 de l’état HIGH à l’état LOW ; l’unité de capture d’entrée est donc réglée pour réagir à un front descendant. Le réducteur de bruit n’est pas utilisé. La routine d’interruption déclenchée par l’unité de capture d’entrée, calcule et affiche les données sur le moniteur série. Les interruptions doivent être autorisées globalement et localement (overflow et capture). Petit plus pour bien contrôler ce qui se passe, la DEL du module Uno reliée à la broche 13, s’allume un cycle de timer1 sur deux : la durée d’allumage ou d’extinction est donc de 4,194 s.

Maintenant que nous savons ce que nous voulons faire, voyons comment le faire c’est-à-dire comment régler les différents registres de contrôle. La figure 7 nous permet de repérer les différents bits à régler dans les différents registres. Le timer1 doit travailler dans le mode normal en comptant de 0 à 65535 : pour cela, il faut positionner les bits WGM13:0 (en jaune) à 0, ce qui se fait dans les registres TCCR1A (bits 0 et 1) et TCCR1B (bits 3 et 4). Le prescaler doit être réglé pour diviser la fréquence horloge par 1024 : il faut positionner les bits CS12:0 (en rose) respectivement à 1, 0 et 1, ce qui se fait dans le registre TCCR1B (bit 0 = 1, bit 1 = 0 et bit 2 = 1). On veut que l’unité de capture réagisse aux fronts descendants, donc le bit ICES1 (en rouge) doit être égal à 0 (bit 6 de TCCR1B = 0). On ne veut pas utiliser le réducteur de bruit, donc le bit ICNC1 (en violet) doit être égal à 0 (bit 7 de TCCR1B = 0). Pour autoriser ou non les interruptions de façon globale, on utilise les fonctions interrupts() ou noInterrupts(). Pour autoriser les interruptions déclenchées par l’overflow du timer1 et par l’unité de capture, on positionne à 1 le bit TOIE (en vert, bit 0 de TIMSK1) et le bit ICIE (en bleu turquoise, bit 5 de TIMSK1). Conséquence de tout cela, TCCR1A = 0, TCCR1B = 0b00000101 et TIMSK1 = 0b00100001 (pour mieux voir le positionnement des bits, on préfère donner la valeur binaire des registres).

Figure 7
Figure 7
Les registres du timer1 du microcontrôleur ATmega328P

Voici maintenant le programme :

// Input_Capture_timer1.ino
// ------------------------
// On fait fonctionner le timer1 en mode normal, de 0 a 65535
// avec un prediviseur regle sur 1024. La durée du cycle est de 4,194 secondes
// Chaque debordement crée une action sur la LED qui s allume et s eteint
// toutes les 4,194 secondes. 
// Chaque debordement augmente egalement numeroCycle d une unite. 
// Une capture sur l'entree 8 (detection front descendant) provoque l affichage
// du numero de cycle, la dureeTotale entre le debut du programme et l evenement,
// et la date de l evenement calculee avec la valeur du registre ICR1.
//-------------------------------------------------------------------------------

#define ledPin 13
#define poussoirPin 8

int nombreCycle = 0;
unsigned long dureeTotale = 0;
float dateEvent = 0.0;

void setup() {
  pinMode (ledPin, OUTPUT);
  pinMode (poussoirPin, INPUT_PULLUP);
  Serial.begin(9600);
  noInterrupts();
  TCCR1A = 0;
  TCCR1B = 0b00000101; // prediviseur 1024, duree de pas 64 µs, ICES1 front descendant
  TIMSK1 = 0b00100001; // autorisation interruption TOV et Input Capture
  TCNT1 = 0; 
  ICR1 = 0; 
  interrupts();
}

ISR(TIMER1_OVF_vect)
{
  digitalWrite (ledPin, !digitalRead (ledPin)); // A chaque overflow, action sur LED
  nombreCycle = nombreCycle + 1;
}

ISR(TIMER1_CAPT_vect)
{
  dureeTotale = millis();
  dateEvent = nombreCycle * 4194.304 + ICR1 * 0.064;
  Serial.println(nombreCycle);
  Serial.println(dureeTotale); 
  Serial.println(dateEvent); 
}

void loop() {
  // Mettez le programme ici
}

La différence entre les résultats observés par la fonction millis() et ceux calculés avec les éléments du timer1 s’explique de plusieurs façons :

  • La fonction millis() n’affiche pas les décimales
  • La fonction millis() ne démarre pas tout à fait au même moment que la mise à 0 du timer1 dans la fonction setup.
  • Le timer1 travaille lentement, donc avec une précision moindre (durée d’un pas = 64 µs)

Malgré cela, le programme montre comment utiliser l’unité de capture d’entrée du timer1. Amusez-vous à modifier les valeurs du prescaler pour faire travailler plus vite le timer1 et pensez à changer le calcul pour déterminer la date de l’événement (dateEvent).

Vous pouvez bien sûr vous inspirer de ce programme et de ce qui a été dit sur cette unité de capture d’entrée, pour trouver des applications plus appropriées au modélisme ferroviaire, comme déterminer la vitesse de vos trains par exemple ou bien décoder un signal DCC décrit dans l’article L’Arduino et le système de commande numérique DCC. On date la détection d’un front ascendant, puis la détection du front descendant qui suit, et on accède ainsi à la demi-période du signal DCC. Le logiciel peut alors en déduire s’il s’agit d’un bit 1 quand la demi-période est comprise entre 55 et 61 microsecondes ou d’un bit 0 quand la demi-période est comprise entre 95 et 9900 microsecondes. Enfin, n’oubliez pas que dans certains cas l’utilisation de la fonction millis() n’est pas possible, aussi faut-il recourir à ce genre de programme pour pallier cette lacune. Bien entendu, le recours à la datasheet du µC ATmega328P est fortement conseillée voire indispensable dans certains cas.

[1l’utilisation de la fonction millis() est possible puisqu’elle ne fait pas appel au timer1.

[2voici le détail du calcul.
À 16 MHz, la période du signal vaut 1/(16 x 10^6) = 0,0625 µs (soit 62,5 nanosecondes).
À la sortie du prédiviseur, la période est donc de 0,0625 x 1024 = 64 µs = 0,064 ms (le pas d’avance du timer1).
Un cycle de timer1 (sur 16 bits, soit 65536 pas) dure donc 64 x 65536 = 4194304 µs = 4194,304 ms.
La date de l’événement (en millisecondes) est donc égale au nombre de cycles écoulés x 4194,304 auquel s’ajoute la valeur du timer1 (ou du registre ICR1 qui en est la copie) x 0,064.

11 Messages

  • Les Timers (V) Calcul du temps d’appuis d’un BP 16 juin 2017 15:26, par Anoyme

    Bonjour et merci pour les cours, qui m’ont permis de presque tout comprendre sur les TImers. J’essaie de calculer a l’aide du Timer1 le temps qu’un bouton poussoir est appuyé comme vous le dites dans le cours, mais je suis bloqué.
    En effet je n’arrive pas a avoir une aquisition sur front montant puis sur front descendant. J’ai soit l’un ou l’autre.

    #define poussoirPin 8
    unsigned long duree = 0;
    float event = 0;
    int nombre = 0;
    
    void setup() {
    
      pinMode (poussoirPin, INPUT_PULLUP);
      Serial.begin(115200);
      noInterrupts();
      TCCR1A = 0;
      TCCR1B = 0b00000010; // prediviseur 8  table 16-5 doc atmegaICES1 front descendant =06emebit 7eme bit noise cancel
      TIMSK1 = 0b00100001; // autorisation interruption TOV et Input Capture
      TCNT1 = 0;
      ICR1 = 0;
      interrupts();
    }
    
    
    ISR(TIMER1_CAPT_vect) {
      TCCR1B = 0b00000010;   //front descendant
      Serial.println("Appuye");
      /*duree = millis();
      event = nombre * 32.7675 + ICR1 * 0.0000005;
      Serial.println(nombre);
      Serial.println(duree);
      Serial.println(event);*/
      TIFR1 = 0b00100000;            //reset du flag 
      TCCR1B = 0b01000010;          //front montant
      Serial.println("Relache");
    }
    
    void loop() {
    
    }

    Voici mon code, pouvez m’éclairer ? Je suppose que c’est l’endroit ou je demande au Timer de passer en front montant qui est pas au bon endroit. Mais je ne vois pas comment faire.

    Merci de votre aide :)

    Répondre

  • Les Timers (V) 17 juin 2017 10:54, par Jean-Luc

    Bonjour,

    Dans votre ISR, vous n’avez aucun code pour distinguer si l’ISR était appelée sur un front descendant ou sur un front montant. Et quoiqu’il en soit vous terminez en programmant la sensibilité sur front montant. Donc, à part la première fois, et pendant le court intervalle de temps entre le début et la fin de l’ISR, ce n’est jamais sensible au front descendant.

    Il faut que vous teniez compte de la sensibilité qui a déclenché l’ISR afin de la reprogrammer à l’inverse. Quelque chose comme ça :

    ISR(TIMER1_CAPT_vect) {
      TIFR1 = 0b00100000; // reset du flag 
      if (TCCR1B & 0x40) {
        // l'ISR a été déclenchée par un front montant
        TCCR1B = 0b00000010; // programme sensibilité front montant
        Serial.println("Appuye");
      }
      else {
        // l'ISR a été déclenchée par un front descendant
        TCCR1B = 0b01000010;   // programme sensibilité front descendant
        Serial.println("Relache");
      }
    }

    C’est non testé.

    Par ailleurs vous n’avez pas mis ICNC1 à 1.

    Répondre

  • Les Timers (V) 20 juin 2017 11:22, par Anoyme

    Bonjour, après pas mal d’essais j’ai réussi a compter le temps ou mon BP reste enfoncé. Je transmets le code des fois que cela serve a quelqu’un. Comme vous pouvez le voir j’ai abandonné l’idée d’utiliser le Timer1, et je me suis rabattus sur le timer millis(). Merci de l’aide apportée :)

    const byte btnPin = 2;        // Bouton sur pin 2...
    const byte btnIrq = 0;       // Bouton sur pin 2, soit INT0 sur arduino UNO.
    unsigned long tFall = 0;     // Valeur de millis() lors du front descendant
    unsigned long tRise = 0;    // Valeur de millis() lors du front montant
    unsigned long tDiff = 0;    //Valeur appuie Bp
    
    void onRise(void);
    void onFall(void);
    void setup(void);
    void loop(void);
    
    void onFall() {                                     // Interruption lors d'un front descendant
      tFall = millis();                                // On sauvegarde la valeur de millis dans tFall
      attachInterrupt(btnIrq, onRise, RISING);        // Puis on passe la détection d'interruption sur front montant
    }
    
    void onRise() {                                     // Interruption lors d'un front montant
      tRise = millis();                                // On sauvegarde la valeur de millis dans tRise
      attachInterrupt(btnIrq, onFall, FALLING);       // Puis on passe la détection d'interruption sur front descendant
    }
    
    void setup(void) {                                  // Setup:
      attachInterrupt(btnIrq, onFall, FALLING);        // Détection d'interruption sur front descendant au départ
      Serial.begin(9600);                           // Serial à 11500 bauds
    }
    
    void loop(void) {
    
      if ( ( tRise != 0 ) && ( tFall != 0 ) ) {
        tDiff = tRise - tFall;
        Serial.println(tDiff);
        tFall = 0; tRise = 0;                       // on connait maintenant le temps que notre Bp a était appuyé.
      }
      
    }

    Répondre

    • Les Timers (V) 27 octobre 2019 22:43, par ANDRE

      Bonsoir, votre programme m’intéresse beaucoup je l’ai copié et téléversé dans mon arduino uno (officiel) mais j’ai des valeurs qui ne correspond à rien. J’ai fait un copier collé des résultats que je trouve :

      22:08:58.250 -> valeur bas 146725 valeur haute 146525 total 200
      22:08:58.296 -> valeur bas 146731 valeur haute 146741 total 0
      22:09:13.366 -> valeur bas 161842 valeur haute 161842 total 4294966646
      22:09:13.413 -> valeur bas 161846 valeur haute 161860 total 4294967293

      j’ai un résultat qui correspond bien à la différence entre les deux valeurs ; mais j’ai fait beaucoup d’essai mais pratiquement que des valeurs incohérente.

      J’aimerai avoir un programme qui me permettrai de calculer le temps de fonctionnement d’un appareille électrique ; additionner toutes les périodes quand il fonctionne comme un compteur horaire mais pour créer un programme pour moi s’est presque impossible.

      Donc votre programme correspond mais il doit y avoir un problème de compatibilité ou autres.

      J’ai fait quelque recherche mais je ne trouve pas.

      Pouvez-vous m’aider

      Répondre

      • Les Timers (V) 30 octobre 2019 01:25, par Christian

        Bonjour ANDRE,
        De quel programme parlez-vous : celui de mon article ou bien celui posté par un lecteur et situé au-dessus de votre question ?
        Christian

        Répondre

        • Les Timers (V) 30 octobre 2019 22:14, par andreyves@neuf.fr

          Bonjour Christian,

          Je parle du programme juste au-dessus de mon commentaire car il correspond à ce que je cherche. Je cherche à Calculer le temps de fonctionnement d’un appareil électrique avec mon ARDUINO UNO. On va dire un compteur qui additionne les temps de fonctionnement et grasse au temps je peux déterminer une consommation. Et donc dans son programme il parle de connaitre le temps d’appui sur un bouton poussoir , je me suis dis super ; mais j’ai des valeurs qui ne corresponds pas.

          Le programme de votre article je ne l’ai pas trop compris et je sais pas si je peux connaitre le temps d’appui pour après en faire un compteur.

          Voila ma demande n’est peut-être pas prise en charge sur votre site, mais je voulais poser la question.

          Bonne soirée.

          Monsieur ANDRE

          Répondre

          • Les Timers (V) 31 octobre 2019 12:19, par Christian

            Dans ce cas, je ne vais rien pouvoir faire, n’étant pas l’auteur de ce programme. De plus, il y a peu de chance que celui qui l’a écrit ait pu voir votre commentaire sauf s’il revient périodiquement sur le site.
            En plus, votre projet n’étant pas du modélisme ferroviaire, nous n’y donnerons pas suite.
            Bonne chance tout de même dans la réalisation de ce projet.
            Cordialement.
            Christian

            Répondre

  • Les Timers (V) 6 mars 2018 22:25, par stéphane

    Bonjour,
    vraiment génial ce tuto sur le timer1.
    j’ai enfin remplacé mon programme utilisant pulsin par l’utilisation d’une interruption sur front montant de l’entrée 8.
    Je lui entre avec un générateur de fonction un signal carré à fréquence fixe ça fonctionne a 1.675 microseconde près.
    Dès que je change la fréquence le programme plante, je dois faire un reset pour le relancer.
    avez vous une idée de ce qui peut se passer ?
    par avance merci.
    ci dessous mon code :

    //-------------------------------------------------------------------------------
    #define VolantMoteur 8
    int16_t dureeTotale = 0;
    
    void setup()
    {
    pinMode (VolantMoteur, INPUT_PULLUP);
    Serial.begin(115200);
    noInterrupts();
    // registre A
    //-bits 0 et 1 (WGM10 et WGM11)wave form generation
    //-bits 0 a 4 pour le mode de comparaison de sortie
    TCCR1A = 0;
    
    // registre B:
    //-bits 0,1 et 2 : selection source horloge ici 101-->sortie du prescaleur a 1024 duree de pas 64 µs
    //-bits 3 et 4 : WGM12 et WGM13 wave form generation
    //-bit 6 : ICES1 (Input Capture Edge Select), ICES1==0 front descendant/ICES1==1 front montant
    //-bit 7: ICNC1: Input Captur Noise Canceler: 1-->actif
    TCCR1B = 0b01000001; 
    
    //Timer counter Interrupt Mask Register:
    //-bit 5 :(Input Capture Interrupt Enable) qui, positionné à 1, permet de déclencher une interruption lorsqu’un événement a provoqué la recopie du timer1 dans le registre ICR1.
    //-bit 0 est le bit TOIE  (Timer Overflow Interrupt Enable) qui permet de déclencher une interruption lorsqu’il y a débordement du timer1.
    TIMSK1 = 0b00100001;
    
    TCNT1 = 0; 
    ICR1 = 0; 
    interrupts();
    }//fin setup
    
    ISR(TIMER1_CAPT_vect)
    {
    dureeTotale = ICR1;//lecture de la valeur du timer dans le registre ICR1
    TCNT1=0;//remise a zero du timer
    }
    
    void loop()
    {
    Serial.println(1000000/(dureeTotale*0.0625)); 
    } 

    Répondre

    • Les Timers (V) 9 mars 2018 17:40, par Christian

      Bonjour,

      Pour obtenir de l’aide sur la mise au point d’un programme, je vous invite à ouvrir un fil dans le forum de Locoduino.
      L’espace après les articles est plutôt réservé au signalement de passages peu clairs ou bien d’erreurs, donc à ce qui concerne l’article lui-même.
      Merci de votre compréhension.

      Christian

      Répondre

  • Les Timers (V) 19 décembre 2019 16:44, par Aurélien

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

    Répondre

    • Les Timers (V) 20 décembre 2019 18:16, par Christian

      Les programmes donnés dans cette série d’articles sont déjà des exemples.
      Après, c’est à votre imagination de faire le reste, sachant qu’un timer n’est rien d’autre qu’un compteur de temps.
      Enfin, le choix d’utiliser le timer 0, 1 ou 2 dépend de ce que vous voulez faire avec ce timer et aussi de ce que votre programme fait en dehors : en effet, il est important de ne pas interférer avec les timers utilisés par certaines fonctions ou bibliothèques d’Arduino.

      Répondre

Réagissez à « Les Timers (V) »

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