Nanosecondes

Bonjour, existe-t-il un moyen de mesurer une impulsion < 1µs comme avec les commandes millis et micros du language Arduino.
Merci pour vos infos ou vos réponses.
Marc.

QUINQUIN07:
Bonjour, existe-t-il un moyen de mesurer une impulsion < 1µs comme avec les commandes millis et micros du language Arduino.
Merci pour vos infos ou vos réponses.
Marc.

bonsoir
si tu reste sur un arduino basique , la resolution de micros() est déjà de 4µs , et résolution n'est pas précision :grin:
si tu veux "traquer la nano" , les mcu de base arduino ne sont pas des bons candidats = pas du tout adaptés pour jouer correctement là :grin:

Le mieux que l'on puisse faire c'est utiliser un timer avec un prédiviseur (prescaler) de 1.
La lecture du registre TCNTx (x = numéro du timer) donne le nombre de cycles (périodes) horloge écoulé.

Avec un quartz de 16 MHz la période est T= 1/F = 62,5 ns, pas possible de faire mieux.

uint8_t lecture ;
TCNTx = 0  ;   // Raz du compteur pour ne pas avoir à gérer les débordements de compteur
/*
Chose à mesurer
*/
lecture = TCNTx;

La fonction arduino micros est limitée à 4µs parce que arduino fait tourner le timer avec un préscaler de 64.
Il faut donc oublier les fonctions arduino et changer des valeurs directement dans les registres, une lecture de la datasheet s'impose.
Mais c'est très facile de changer ce paramètre ex "TCNTx = 0;" ce n'est pas plus compliqué que cela.

Bonsoir, merci pour ta réponse, mais je vais poser ma question autrement :
Comment peut-on compter le nombre de click d'horloge 16Mhz sachant qu'un click fait 62.5ns?

Le timer avance d'un cran à chaque cycle de son horloge. Le résultat est lu dans un registre 8 bits (TCNTx ) qui compte de 0 à 255, au dela il repasse à 0.
L'horloge du timer est obtenue a partir de l'horloge système. Elle passe dans un certain nombre de diviseurs binaires.
Donc si le diviseur est égal à 1 le timer recoit l'horloge système cad 16 MHz
Si le diviseur est placé à 64 le timer reçoit 16 e6/64 = 250 kHz.

Le nom du compteur se nomme TCNT0 pour le timer T0, TCNT1 pour le timer T1, etc.....

Le compteur du timer ne travaille pas en secondes, il ne sait que compter des périodes horloge. C'est le programmeur qui peut associer des secondes aux nombre de périodes horloge en fonction des réglages qu'il a fait.

Merci à tous je vais essayer les mesures.
Marc.

dans l'idéal il faudrait utiliser le timer 1 en mode capture

pour mesurer la durée d'une période il faut rapidement sauvegarder la valeur de la première capture. Ensuite, la valeur de la seconde capture moins la valeur sauvegardée = le nombre de tics (à multiplier par 62.5ns pour avoir la durée)

pour mesurer la durée d'une impulsion c'est moins simple, car il faut également changer le sens de l'"edge détector" entre temps. Dans les cas critiques il faut écrire ce paragraphe en assembleur

Bonjour, j'ai fait un petit résumé de vos messages j'en est déduit ceci :

void setup() 
{
  Serial.begin(115200); //Vitesse du port serie 
  bitSet (DDRB,5);  //pin PB5 Atmega en sortie pin 13 ARDUINO
  TCCR2B = 0 << CS22 | 0 << CS21 | 1 << CS20; // prédiviseur (prescaler) de 1.
  // Un clic d'horloge = 0.0625µs pour 16Mhz
}
void loop() 
{
  int lecture_pin_13 =0; // périodes Pin 13
  int lecture_delay =0; // périodes du delay
  int lecture_somme =0; // somme Pin 13 + delay
  
  TCNT2 = 0  ;   // Raz du compteur pour ne pas avoir à gérer les débordements de compteur
  // Maxi comptage  1 octet = 255 : 62.5ns * 256 = 16µs après TCNT2 revient à 0
  bitSet (PORTB,5);// niveau Haut sur le Pin 13 ARDUINO = 2 périodes x 0.625 = 0.125µs
  lecture_pin_13 = TCNT2;
  TCNT2 = 0  ;// Raz du compteur 
  delayMicroseconds(10);
  lecture_delay = TCNT2;
  
  lecture_pin_13 = lecture_pin_13 -1; // -1 : (0*période*1*période*2) : 3 chiffres = 2 périodes
  lecture_delay = lecture_delay -1; 
  lecture_somme = lecture_pin_13 + lecture_delay;
  Serial.print("lecture_pin_13 = ");
  Serial.print(lecture_pin_13);
  Serial.println(" periodes horloge");
  Serial.print("lecture_pin_13 = ");
  Serial.print(lecture_pin_13*0.0625,4);// affiche la valeur 4 décimale après la virgule
  Serial.println(" microsecondes");
  Serial.print("lecture_delay = ");
  Serial.print(lecture_delay);
  Serial.println(" periodes horloge");
  Serial.print("lecture_delay = ");
  Serial.print(lecture_delay*0.0625,4);
  Serial.println(" microsecondes");
  Serial.print("lecture_somme = ");
  Serial.print(lecture_somme);
  Serial.println(" periodes horloge");
  Serial.print("lecture_somme = ");
  Serial.print(lecture_somme*0.0625,4);
  Serial.println(" microsecondes");
  Serial.println();
  
  bitClear (PORTB,5);// niveau Bas sur le Pin 13 ARDUINO
  delay(1000);              
}

bitSet (PORTB,5), prend 2 périodes d'horloge comme prévu, par contre
delayMicroseconds(10), prend 155 périodes d'horloge alors qu'il en faudrait 160, c'est peu être du à ce que dit " Artouste " résolution ne veut pas dire précision!!!!!!!!!!!!!!

Enfin si je veux mesurer des temps supérieurs à 16µs, il faudra utiliser les interruptions.
Là encore il va falloir potasser!!!!

Encore merci à tous.
Marc.

Bonjour

Autre suggestion : selon les propriétés connues du signal que tu veux mesurer, une approche statistique est peut-être réalisable.

C'est-à-dire passer par un grand nombre de mesures et en faire la moyenne.

A voir dans ton cas si c'est applicable ou pas.

bitSet (PORTB,5), prend 2 périodes d'horloge comme prévu,

Oui si tu tiens compte de l'opération de lecture de TCNTx sinon en fait cela n'en prend qu'une.

Enfin si je veux mesurer des temps supérieurs à 16µs, il faudra utiliser les interruptions.

Mouais, quand tu veux mesurer des temps supérieurs à 16µs as-tu toujours besoin d'une précision de 62,5 ns ?
Dans la majorité des cas je ne pense pas. Il est alors possible de jouer sur la valeur du préscaler par exemple avec un préscaler de 8 le pas de mesure sera 0,5 µs . Dans le cas le plus défavorable t= 16µs cela donne quand même une précision de 3% à laquelle s'ajoute celle du quartz du résonnateur céramique (0,5 %).

68tjs:
Le mieux que l'on puisse faire c'est utiliser un timer avec un prédiviseur (prescaler) de 1.
La lecture du registre TCNTx (x = numéro du timer) donne le nombre de cycles (périodes) horloge écoulé.

J’ai testé la solution que tu as proposé, ça fonctionne bien.
Juste une remarque à ajouter: le prédiviseur des timers est configuré par défaut à 64.
Il est donc indispensable de l’initialiser avant utilisation.
J’ai trouvé un tuto pour ça ici (en anglais):
http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/

Exemple de code pour mesurer le temps d’exécution de morceaux de codes:

int t[7]; // stocker le timing

void setup() {
  int i;
  pinMode(2, INPUT); 
  Serial.begin(115200);
//  TCCR1B = 0;     // initialiser le prédiviseur
//  TCCR1B |= (1 << CS10); // prédiviseur à 0
  TCNT1 = 0; // initialiser le timer
  t[0] = TCNT1; // marqueurs temporels
  t[1] = TCNT1;
  i = digitalRead(2);
  t[2] = TCNT1;
  delayMicroseconds(1);
  t[3] = TCNT1;
  delayMicroseconds(2);
  t[4] = TCNT1;
  delayMicroseconds(4);
  t[5] = TCNT1;
  delayMicroseconds(16);
  t[6] = TCNT1;

  for (i = 1; i <= 6; i++) { // on affiche le résultat
    Serial.print("temps "); 
    Serial.print(i); 
    Serial.print(": "); 
    Serial.println(t[i] - t[i-1]); // dif de temps
  }
}
void loop() {
}

Sans initialiser le prédiviseur on obtient ça:
temps 1: 0
temps 2: 1
temps 3: 0
temps 4: 1
temps 5: 1
temps 6: 4 // 256+2 cycles attendu. 258/ 64 = 4, conclusion le prédiviseur par 64 est configuré par défaut.

Après avoir décommenté l’initialisation du prédiviseur:

TCCR1B = 0;     // initialiser le prédiviseur
TCCR1B |= (1 << CS10); // prédiviseur à 0

on obtient ça:
temps 1: 8 // nombre de cycles horloges
temps 2: 66
temps 3: 22 // 16+2 attendu
temps 4: 34 // 32+2 attendu ok
temps 5: 66 // 64+2 attendu ok
temps 6: -142 // 256+2 attendu, overflow (normal car 8 bits)

Note: d’après le datasheet Atmel, le timer 1 utilise un registre 16 bits.
Pourtant TCNT1 renvoie le résultat sur 8 bits.

Note: d'après le datasheet Atmel, le timer 1 utilise un registre 16 bits.
Pourtant TCNT1 renvoie le résultat sur 8 bits.

La variable que tu as déclaré pour recevoir les valeurs de TCNT1 est bien 16 bits elle aussi ?
Sinon dans la datesheet on peut voir que TCNT1, tout comme OCC2A/B et ICR1 sont disponibles sous forme de 2 registres 8 bit H et L : TCNT1H et TCNT1L.

Il faut faire attention, pour la lecture d'un registre 16 bits, il faut lire en premier les poids faibles car c'est cet accès qui place les poids forts dans un registre tampon pour éviter les erreurs de lectures.

68tjs:
La variable que tu as déclaré pour recevoir les valeurs de TCNT1 est bien 16 bits elle aussi ?
Sinon dans la datesheet on peut voir que TCNT1, tout comme OCC2A/B et ICR1 sont disponibles sous forme de 2 registres 8 bit H et L : TCNT1H et TCNT1L.

Oui 16 bits, mais ça ne vient pas de là, TCNT1 semble être un alias de TCNT1L.
J’ai écrit le programme suivant:

void setup() {
  uint16_t h, l;

  Serial.begin(115200);
  TCCR1B = 0;     // initialiser le prédiviseur
  TCCR1B |= (1 << CS10); // prédiviseur à 0
  TCNT1L = 0; // initialiser le timer
  TCNT1H = 0; 
  delayMicroseconds(17); // temporisation voir plus bas
  l = TCNT1L;
  h = TCNT1H;
  Serial.print("H: "); 
  Serial.print(h); 
  Serial.print(" L: "); 
  Serial.println(l); 
}
void loop() {
}

J’ai dû oublier quelque chose car le programme fonctionne bien jusqu’à 255 cycles, puis il tourne à l’envers (TCNT1L décrémente de un à chaque cycle).
Quant à TCNT1H il reste à 0 (l’initialisation «TCNT1H = [n];» est sans effet).
Résultats pour:
delayMicroseconds(14): H:0 L: 225 // 1614+1 ok
delayMicroseconds(15): H:0 L: 241 // 16
15+1 ok
delayMicroseconds(16): H:0 L: 253 // 16*16+1-4?
delayMicroseconds(17): H:0 L: 237 // 253-16?

ChristopheFr:

int t[7]; // stocker le timing

...
t[0] = TCNT1; // marqueurs temporels
t[1] = TCNT1;
...
Serial.println(t[i] - t[i-1]); // dif de temps





temps 6: -142 // 256+2 attendu, overflow (normal car 8 bits)

Pour le coup, ton résultat négatif n'est pas dût au la longeur de la variable mais à son type qui doit être unsigned.

trimarco232:
dans l'idéal il faudrait utiliser le timer 1 en mode capture

pour mesurer la durée d'une période il faut rapidement sauvegarder la valeur de la première capture. Ensuite, la valeur de la seconde capture moins la valeur sauvegardée = le nombre de tics (à multiplier par 62.5ns pour avoir la durée)

pour mesurer la durée d'une impulsion c'est moins simple, car il faut également changer le sens de l'"edge détector" entre temps...

+1000, peux-tu nous en dire plus sur ton signal à mesurer ? sa période, sa variation ?

Bonjour , es que il y a une solution de ce problème merci !

Deterrer une discussion vielle de trois ans et envoyer un mp à tous ses participants n'est pas la bonne solution.
Ouvre ta propre discussion, explique ton projet en plus d'une ligne

ok, Monsieur bricoleau