TIMER1 16Bits Test

Bonjour, je voudrais utiliser le timer1 16 bits associer au compteur TCNT1, mais avec ce test il ne lit que 8 bits (255) et retourne à 0, savez-vous pour quel raison et me dire ou est mon erreur. Merci.
Marc.

unsigned int NB_Clic = 0;
unsigned int Temps = 0;
int Precision = 4; //4µs prédiviseur 64
volatile int BP=HIGH;

void setup()
{ 
  Serial.begin(115200); //Vitesse du port serie 
  attachInterrupt (0, Lecture_interruption, FALLING);// pin PD2-INT0 Atmega pin 2 ARDUINO 
  TCCR1B = 0 << CS12 | 1 << CS11 | 1 << CS10; // prédiviseur sur 64
  //Compteur d'horloge timer 16Bits TCNT1 = 1 :  compte 1 clic d'horloge = 4µs pour 16Mhz
  //Maxi comptage  2 octet = 65535 clic d'horloge : 4µs * 65535 = 262140µs soit 262ms après TCNT1 revient à 0
  bitSet (DDRB,5);  //pin PB5 Atmega en sortie pin led 13 ARDUINO 
  Serial.println("Appuyez sur BP ");
} 
void loop() 
{ 
  if (BP==LOW)
  {
    Cycle(); // Fonction Cycle 
  }  
}
//********************FONCTIONS*******************
void Lecture_interruption()
{
  BP=LOW;  
}
//******************Fonction Cycle ***************
void Cycle()
{
  TCNT1 = 0;// Raz du compteur        
  while(TCNT1<255)
  { 
    bitSet (PORTB,5);// niveau Haut sur le Pin led 13 ARDUINO          
  } 
  NB_Clic = TCNT1;
  BP=HIGH; 
  bitClear (PORTB,5);// niveau Bas sur le Pin led 13 ARDUINO
  Serial.print("NB_Clic TCNT1 = ");
  Serial.print(NB_Clic);
  Serial.print(" Clics\n"); 
  Serial.print("Temps = ");
  Temps = NB_Clic * Precision;
  Serial.print(Temps);
  Serial.print(" microsecondes\n\n"); 
  Serial.print("Appuyez sur BP\n");
}

TCNT1 est un registre 16 bits comme l'atmega est un micro 8 bits il lit les registres 16 bits en deux fois.

Pour ce faire TCNT1 est découpé en deux TCNT1H et TCNT1L. Fudufnews a rappelé il y a peu de jours le quels il faut lire en premier. Si tu ne retrouve pas son post l'explication est dans la datasheet.

C’était là: http://forum.arduino.cc/index.php?topic=293610.msg2052221#msg2052221

Dans la mesure où on ne maîtrise pas comment le code compilé accède aux registres, il faut faire 2 accès consécutifs en lisant des unsigned char et reconstruire l’unsigned int et non faire la lecture directe d’un unsigned int.

Je suis surpris, si la lecture des registres se faisait “à l’envers” le résultat ne serait pas celui que décrit QUINQUIN07.

J’utilise avec succès la lecture directe des registres 16-bits tel qu’il est écrit dans la datasheet :

unsigned int i;
...
/* Set TCNT1 to 0x01FF */
TCNT1 = 0x1FF;
/* Read TCNT1 into i */
i = TCNT1;
...

Même si j’ai conscience qu’à la compilation je ne maitrise pas ce qui ressort.

Pour autant je ne comprends pas le problème rencontré ici, peux-tu écrire une valeurs plus grande que 255 dans le registre TCNT1 et la lire juste après ?

Je pencherait plutôt pour un mauvais paramétrage du timer mais je maîtrise mal le bitwise, du coup je ne comprends pas ceci :

TCCR1B = 0 << CS12 | 1 << CS11 | 1 << CS10; // prédiviseur sur 64

De ce que je comprends du code : tu attends une action sur un bouton pour entrer dans Cycle Dans Cycle tu mets le compteur à zéro tu attends que la valeurs du compteur soit supérieur ou égale à 255 tu affiches le résultat tu retournes

Donc si j'ai bien compris tu devrais toujours afficher 255 et toujours repartir de zéro Mais j'ai peut être lu un peu vite et mal compris

La piste “fdufnews” est très inintéressante, peux-tu nous en dire plus le signal que tu veux mesurer ? sa fréquence, sa variation ?

Un conseil : lorsque tu paramètre un timer, écris tous les registres de paramétrage toi-même, c’est plus fiable (TCCR1A, TCCR1B & TCCR1C), car tu ne sais pas comment l’environnement Arduino les a paramétrés et certains paramètre sont dans plusieurs registres

Par exemple :
Dans ton code, on ne sais pas comment le timer1 est paramétré puisque tu ne connais pas l’état des bit WGM11 & WGM10 qui sont dans le registre TCCR1A.

J’ajoute en pièce jointe un petit PDF que j’ai fait pour m’aider à paramétrer les timers.

MEMO-CounterTimer.pdf (15.5 KB)

Yep!

Pour répondre à la judicieuse remarque de UniseV ;)

Par defaut, le registre TCCRx est à zero sur WGM11, 12 et 13, il n'y a pas d'équivoque possible, on est en mode normal, génération sur TOP à 0xFF = 255 (table 14.8 - Atmega328).

Même si au fond, je suis d'accord sur le fait que je ne vois pas bien ce que veux faire QUINQUIN07, du CTC apparemment...

Généralement, on essaie d'obtenir un timer régulier ou une fréquence régulière via les registres TCCRx et en jouant sur le registre OCRx correspondant. Ainsi, on a un rapport cyclique régulier qui peut servir de base à plein de chose.

Même s'il y a des exemples d'écriture/lecture du registre TCNTx dans le datasheet, j'vois peu d'intérêt à lire ce registre...

@+

Zoroastre.

Bonjour, à tous,

Réponse à “fdufnews”
De ce que je comprends du code :
Je rentre un nombre de clic entre 2 et 65534 soit 8µs à 262.140ms (temps d’un niveau haut pendant un temps précis)
tu attends une action sur un bouton pour entrer dans Cycle
Dans Cycle
tu mets le compteur à zéro
Valeur haute sur pin 13 durant tout le comptage
tu attends que la valeurs du compteur soit supérieur ou égale au nombre de clics entrées
tu affiches le résultat
tu retournes
J’attends une autre interruption

Je joins le croquis modifié, est-ce correct et utilisable?

unsigned long NB_Clic = 0;
unsigned long Temps = 0;
int Precision = 4; //4µs prédiviseur 64
volatile int BP=HIGH;

void setup()
{ 
  Serial.begin(115200); //Vitesse du port serie 
  attachInterrupt (0, Lecture_interruption, FALLING);// pin PD2-INT0 Atmega pin 2 ARDUINO
  TCCR1A = (0<<COM1A1) | (0<<COM1A0) | (0<<WGM11) | (0<<WGM10);  //mode d'exploitation normal
  TCCR1B = TCCR1B = (0<<CS12) | (1<<CS11) | (1<<CS10); // prédiviseur sur 64
  TCNT1H = 0xFF;
  TCNT1L = 0xFF;
  //Compteur d'horloge timer 16Bits TCNT1 = 1 :  compte 1 clic d'horloge = 4µs pour 16Mhz
  //Maxi comptage  2 octet = 65535 clic d'horloge : 4µs * 65535 = 262140µs soit 262ms après TCNT1 revient à 0
  bitSet (DDRB,5);  //pin PB5 Atmega en sortie pin led 13 ARDUINO 
  Serial.println("Appuyez sur BP ");
} 
void loop() 
{ 
  if (BP==LOW)
  {
    Cycle(); // Fonction Cycle 
  }  
}
//********************FONCTIONS*******************
void Lecture_interruption()
{
  BP=LOW;  
}
//******************Fonction Cycle ***************
void Cycle()
{
  TCNT1 = 0;// Raz du compteur        
  while(TCNT1<=65534)//entrer un nombre < 65535
  { 
    bitSet (PORTB,5);// niveau Haut sur le Pin led 13 ARDUINO          
  } 
  NB_Clic = TCNT1;
  BP=HIGH; 
  bitClear (PORTB,5);// niveau Bas sur le Pin led 13 ARDUINO
  Serial.print("NB_Clic TCNT1 = ");
  Serial.print(NB_Clic);
  Serial.print(" Clics\n"); 
  Serial.print("Temps = ");
  Temps = NB_Clic * Precision;
  Serial.print(Temps);
  Serial.print(" microsecondes\n\n"); 
  Serial.print("Appuyez sur BP\n");
}

Je suis ok sur le fonctionnement du bouton, quand tu appuie sur le bouton en PIN D2, tu déclenche Cycle().

Dans Cycle, tu va remettre le compteur à 0, et allumer la lumière pendant toute la durée du compteur… soit 262ms. (attention, ton while est une fonction bloquante !!)

Ensuite, tu relève NB_Clic = TCNT1; alors que tu sais que TCNT1 était à 65535 lors de l’instruction précédente…

Donc NB_Clic donnera toujours la même valeur qui est complètement lié à ton " while(TCNT1<=65534)"

Tu remet BP à HIGH… ok

tu éteinds la lumière… ok

et tu donne la valeurs en µs de la durée du compteur… mais attention, il y a de grande chance qu’au moment ou tu as relevé le compteur, il venait de dépasser son max…

A propos du setup :

TCCR1A = (0<<COM1A1) | (0<<COM1A0) | (0<<WGM11) | (0<<WGM10);  //mode d'exploitation normal
TCCR1B = TCCR1B = (0<<CS12) | (1<<CS11) | (1<<CS10); // prédiviseur sur 64
TCNT1H = 0xFF;
TCNT1L = 0xFF;

Encore une fois, je ne maîtrise pas cette façon de paramétrer les registres… du coup je ne peux pas te dire si c’est bon, mais je suis étonné par le “TCCR1B = TCCR1B = (0<<CS12)…”

La mise à 0 des registre TCNT1 me semble inutile, et en plus tu le fais lors de l’entrée dans Cycle()…

Erreur de copier coller "je suis étonné par le “TCCR1B = TCCR1B = (0<<CS12)…”
il faut corriger “TCCR1B = (0<<CS12)…”

Ok, voici ce que je te propose :

unsigned long NB_Clic = 0;
unsigned long Temps = 0;
int Precision = 4; //4µs prédiviseur 64
volatile int BP=HIGH;

void setup()
{ 
  Serial.begin(115200); //Vitesse du port serie 
  attachInterrupt (0, Lecture_interruption, FALLING);// pin PD2-INT0 Atmega pin 2 ARDUINO
  TCCR1A = (0<<COM1A1) | (0<<COM1A0) | (0<<WGM11) | (0<<WGM10);  //mode d'exploitation normal
  TCCR1B = (0<<CS12) | (1<<CS11) | (1<<CS10); // prédiviseur sur 64
  TCNT1H = 0xFF;
  TCNT1L = 0xFF;
  
  OCR1A=65534 // Valeur du compteur à laquelle l'interruption doit se lancer


  bitSet (DDRB,5);  //pin PB5 Atmega en sortie pin led 13 ARDUINO 
  Serial.println("Appuyez sur BP ");
} 
void loop() 
{ 
  if (BP==LOW)
  {
    InCycle(); // Fonction Cycle 
  }  
}
//********************FONCTIONS*******************
void Lecture_interruption()
{
  BP=LOW;  
}
//******************Fonction InCycle ***************
void InCycle()
{
  detachInterrupt(0)  
  TCNT1 = 0;// Raz du compteur        
  bitSet (PORTB,5);// niveau Haut sur le Pin led 13 ARDUINO          
  BP=HIGH;  
  // mettre à 1 le Bit OCIE1A dans le registre  TIMSK1 (à toi de choisir la syntaxe)
} 

//******************Fonction OutCycle ***************
ISR(TIMER1_COMPA_vect){
 bitClear (PORTB,5);// niveau Bas sur le Pin led 13 ARDUINO
 attachInterrupt (0, Lecture_interruption, FALLING);
  // mettre à 0 le Bit OCIE1A dans le registre  TIMSK1 (à toi de choisir la syntaxe)

}

Il y a peut-être des erreurs et 2 lignes reste à écrire, mais cette façon de faire est plus “multi-taches”.

Dans mon exemple, tu appuie sur le bouton ça lancer InCycle qui :

  • Détache l’interruption du bouton
  • remet à zero le compteur
  • allume la LED
  • remet BP à HIGH
  • active l’interruption “OCIE1A”.

Ensuite on retourne dans le LOOP et quand le compteur atteind OCR1A, l’interruption OCIE1A intervient pour :

  • éteindre la LED
  • Ré-active l’interruption du bouton
  • désactive l’interruption du compteur

Comme ça, le fonctionnement est le même (tu appuis sur bouton et ça allume une LED pendant un temps précis), mais ton contrôleur est disponible TOUT le reste du temps, pas de fonction bloquante, qu’en penses-tu ?

Je vais essayé de mettre en oeuvre tout ça. Merci.

UniseV: J'utilise avec succès la lecture directe des registres 16-bits tel qu'il est écrit dans la datasheet :

unsigned int i;
...
/* Set TCNT1 to 0x01FF */
TCNT1 = 0x1FF;
/* Read TCNT1 into i */
i = TCNT1;
...

Bonjour, J’ai testé ton bout de code, mais ça ne marche pas.

void setup() {
  Serial.begin(115200);
  unsigned i;
  TCNT1 = 0x1FF;
  i = TCNT1;
  Serial.print("i: "); 
  Serial.println(i); 
}
void loop() {
}

Je devrais lire 0x1FF + 1 (512) mais au lieu de ça je lis 255 (0xFF). Donc je reste au même point que précédemment: http://forum.arduino.cc/index.php?topic=293610.msg2054932#msg2054932

Tu a tapé :
unsigned i;
au lieu de
unsigned int i;

Mais le compilo a du râler, donc je penche plutôt pour un faute de frappe sur le forum.

Ca m’intrigue ton truc, tu utilise quoi comme carte ?
…Je vais peut-être faire des tests

"unsigned int" et "unsigned" sont équivalents.

Le code suivant:

unsigned int Cnt;

void setup() {
  TCCR1A=B00000000; // Timer: normal mode
  TCCR1B=B00000010; // Timer:normal mode, Prescaler=clk/8 
  Serial.begin(115200);
  Serial.println("Start");
}

void loop() {
  Cnt = TCNT1;
  Serial.println(Cnt);
  delay(1);        // delay in between reads for stability
}

Me donne le résultat suivant :

Start
288
2731
5275
7819
10377
13028
15673
18320
20979
23624
26272
28930
31576
34225
36900
39548
42202
44849
47507
50155
52804
55466
58114
60762
63412
536
2980
5519
8065
10626

UniseV: TCCR1A=B00000000; // Timer: normal mode

Merci, c’est le bout de code qui me manquait. Par défaut TCCR1A est initialisé à 1 (PWM PC8bits). Le problème venait de là. Maintenant ça marche.

void setup() {
  unsigned int i;
  Serial.begin(115200);
  TCCR1A = 0; // mode normal
  TCCR1B = 1; // TCCR1B = 1; // clk/1, TCCR1B = 2; // clk/8, TCCR1B = 3; // clk/64, TCCR1B = 4; // clk/256, TCCR1B = 5; // clk/1024,
  TCNT1 = 0;
  delayMicroseconds(20); // code à mesurer
  i = TCNT1; // entre 0 et 65535
  Serial.print("Temps: "); 
  Serial.println(i); 
 }
void loop() {
}

Merci pour le mémo sur les timers.

ChristopheFr: Par défaut TCCR1A est initialisé à 1 (PWM PC8bits).

Non plus.

Le datasheet précise bien que les valeurs initiales "initial value" sont à 0 sur tous les registres.

Tu as du précedemment modifié le registre d'une manière ou d'une autre.

Quoi qu'il en soit, la piste d'UniseV était la bonne, même si cette façon d'écrire : TCCR1A=B00000000; n'est pas trés parlante...

Par défaut TCCR1A est initialisé à 1 (PWM PC8bits).

Le datasheet précise bien que les valeurs initiales "initial value" sont à 0 sur tous les registres

Parfaitement, ce n'est pas une valeur initiale "Atmel", c'est une modification "Arduino" par l'intermédiaire de la fonction init(). Lecture des registres juste après le démarrage sans rien faire d'autre : registre T0 T1 T2 TCCRxA 3 2 1 TCCRxB 3 3 4 TCCR1C - 0 - OCRAx 0 0 0 OCRBx 0 0 0 IRC1 - 0 - TIMSKx 1 0 0 TIFRx 6 7 7 GTCCR 0 0 0

68tjs: Parfaitement, ce n'est pas une valeur initiale "Atmel", c'est une modification "Arduino" par l'intermédiaire de la fonction init().

Oui, j’aurais dû écrire «Arduino initialise le registre TCCR1A à 1; (mode PWM). le code:

void setup() {
  Serial.begin(115200);
  Serial.print("TCCR1A: "); 
  Serial.println(TCCR1A); 
 }
void loop() {
}

affiche «TCCR1A: 1». Le timer 1 est utilisé par Arduino pour le mode PWM. Il faut donc le réinitialiser avec le registre TCCR1B si on veux utiliser le PWM dans la suite du programme:

TCCR1A = 1; // valeur Arduino par défaut, mode PWM
TCCR1B = 3; // prédiviseur par 64