B = ( 2327 + 55 * x) / ( 4654 - x);

Bonjour,

Je cherche à calculer avec une Uno l'expression:
b = ( 2327 + 55 * x) / ( 4654 - x);
Dans laquelle b est un byte.
x est défini par x=1000+random(10);

Sauf si je fais des erreurs, je trouve que:
− si x est un float, il me faut 80 cycles d'horloge
− si x est un unsigned long, il me faut 150 cycles d'horloge
− si x est un word, il me faut 225 cycles d'horloge

J'aurais parié que c'est plus rapide à faire l'opération avec des entiers plutôt qu'avec des réels et d'autant plus rapide que les entiers sont petits. J'observe l'inverse. C'est normal?

En fait j'ai plutôt b = ( 2327 + 55 * x) / ( 4654 - x); dans laquelle x est un word et b un byte
Je peux faire:
(1)     b = ( 2327 + 55 * x) / ( 4654 - x); // Calcul avec des word
(2)     b = ( 2327ul + 55ul * x) / ( 4654ul - x); // Calcul avec des 32 bits
(3)     b = ( 2327.0 + 55.0 * x) / ( 4654.0 - x); // Calcul avec des float

Et pareil, le calcul le plus long est avec des word, le plus courte avec des réels!

Une hypothèse serait que le 328p n'a pas d'unité de division matériel contrairement aux multiplications, additions et soustractions gérées directement par le matériel (donc très rapides en 8 bits). C'est le compilateur qui gère la division. Ceci peut expliquer que les calculs ne sont pas plus rapides avec des entiers ...

Bonne journée.

PS : C'est une hypothèse que je place sous le contrôle des experts mais je l'avais lue ou vue dans une vidéo YouTube lorsque je travaillais sur les 328p.

L’intuition semble logique dans le cas général.

Dans votre cas particulier ,sur un AVR 8 bits, les multiplications et divisions sur des entiers de 16 ou 32 bits doivent être découpées en plusieurs instructions 8 bits, ce qui prend beaucoup de cycles.

Les opérations float sont traitées par des routines logicielles optimisées dans AVR-GCC, qui exploitent des raccourcis (par exemple une multiplication flottante simple peut se faire en combinant des décalages et additions sur les mantisses plutôt qu’en multipliquant chaque octet d’un entier 16 bits sur plusieurs cycle) et des registres de manière plus efficace pour des calculs simples.

Avec ce programme minimaliste

/*
b = ( 2327 + 55 * x)  /  ( 4654 - x);
Dans laquelle b est un byte.
x est défini par x=1000+random(10);
Sauf si je fais des erreurs, je trouve que:
− si x est un float, il me faut 80 cycles d'horloge
− si x est un unsigned long, il me faut 150 cycles d'horloge
− si x est un word, il me faut 225 cycles d'horloge*/

void  x_est_float(void){
  float x;
  byte b;
  unsigned long t0, t1;
  t0 = micros();
  x=1000+random(10);
  b = ( 2327.0 + 55.0 * x)  /  ( 4654.0 - x);
  t1 = micros();
  Serial.print("x is float time spent :"); Serial.print(t1-t0);Serial.println("μs");
  Serial.println(b);
}

void  x_est_ul(void){
  unsigned long x;
  byte b;
  unsigned long t0, t1;
  t0 = micros();
  x=1000+random(10);
  b = ( 2327ul + 55ul * x)  /  ( 4654ul - x);
  t1 = micros();
  Serial.print("x is unsigned long time spent :"); Serial.print(t1-t0);Serial.println("μs");
  Serial.println(b);
}

void  x_est_word(void){
  word x;
  byte b;
  unsigned long t0, t1;
  t0 = micros();
  x=1000+random(10);
  b = ( 2327 + 55 * x)  /  ( 4654 - x);
  t1 = micros();
  Serial.print("x is word time spent :"); Serial.print(t1-t0);Serial.println("μs");
  Serial.println(b);
}

void setup() {
  Serial.begin(115200);

  x_est_float();
  x_est_ul();
  x_est_word();
}

void loop() {
  // put your main code here, to run repeatedly:

}

J'ai

x is float time spent :92μs
15
x is unsigned long time spent :60μs
15
x is word time spent :60μs
15

je ne veux pas m'éparpiller mais si j'ai bien compris sur un 328p :

  • La division n'est pas gérée matériellement qu'elle soit entière ou flottante ;
  • Pour les flottants, ce sont les routines de la libgcc qui sont utilisées, celles-ci sont optimisées selon la taille du code et pour des cas simples.

Pour le programme de test :
A moins de raconter des bêtises, si on utilise le Timer1 (16 bits) avec le registre TCNT1 et que l'on configure le prescaler à 1 ; on travaille sur des cycles d'horloge de 16 MHz et on peut plus précisément convertir en µs le temps qui s'écoule entre chaque mesure ?

@fdufnews le sait bien sûr puisqu'il précise :

@J-M-L également et connaissant @vileroi qui n'a pas donné de code, il a du prendre en compte ce fait. A moins que je raconte des idioties, ce qui reste possible :grinning_face:

aucune opération de division dans le jeu d’instruction AVR des ATMega328 listé dans leur Data Sheet

Bonsoir @al1fch
Oui c’est bien ce que j’avais compris :wink:

Merci
Bonne soirée

C'est ce que je fais. Voici le code qui me donne un résultat complètement différent depuis que j'ai nettoyé le code:

// Variables utilisées pour faire la mesure
volatile byte b;

// Variables pour le programme
word t;

void setup()
{
  Serial.begin(115200);

  float f=1000+random(10);
  TCNT0 =0; // 1ms de calme
  TCCR1B = 1; // Horloge à 16HHz
  TCNT1 = 0; // RAZ du compteur de cycle
                    b = ( 2327.0 + 55.0 * f)  /  ( 4654.0 - f);
  t = TCNT1-1;
  Serial.print("Code avec float:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t/0.016);
  Serial.print(" ns   b=");
  Serial.println(b);

  long l=1000+random(10);
  TCNT0 =0; // 1ms de calme
  TCCR1B = 1; // Horloge à 16HHz
  TCNT1 = 0; // RAZ du compteur de cycle
                    b = ( 2327ul + 55ul * l)  /  ( 4654ul - l);
  t = TCNT1-1;
  Serial.print("Code avec long:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t/0.016);
  Serial.print(" ns   b=");
  Serial.println(b);

  word w=1000+random(10);
  TCNT0 =0; // 1ms de calme
  TCCR1B = 1; // Horloge à 16HHz
  TCNT1 = 0; // RAZ du compteur de cycle
                    b = ( 2327 + 55 * w)  /  ( 4654 - w);
  t = TCNT1-1;
  Serial.print("Code avec word:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t/0.016);
  Serial.print(" ns   b=");
  Serial.println(b);

 }

void loop()
{
}

Avec son résultat:

Code avec float:  95 cycles(s)   5937.50 ns   b=15
Code avec long:  3 cycles(s)   187.50 ns   b=15
Code avec word:  2 cycles(s)   125.00 ns   b=15

Je vais chercher l'erreur. A priori je dois pouvoir mesurer 65535 cycles soit 4095µs. je ne devrait pas dépasser.

Je réfléchis à ça.

Bonsoir @vileroi
Déjà une remarque tu peux mettre tes constantes en flash Serial.print(F
Quoique tu utilises le moniteur série après tes calculs :wink:
Tu ne bloques pas les interruptions pendant tes calculs ?

il faudrait un Serial.flush() avant les calculs pour s'assurer que effectivement les impressions ne soient pas encore en cours quand on lance la mesure du temps.

question 1 : comment 3 appels différents de la fonction random() peuvent donner systématiquement le même nombre ? si b=15 à chaque fois c’est le cas et ça me semble étrange.

question 2 : qui trouve normal qu’un tel calcul puisse être effectué en seulement 2 ou 3 cycles ? rien que le chargement des constantes en consomme plus.

1/
Le random sauf erreur donne x entre 1000 à 1009

—> du coup si tu fais les calculs b varie de
15,68 et 15,87 et comme b est de type byte : b est toujours égal à 15 le Cast en byte supprime les décimales

2/
Oui 2 ou 3 cycles c’est impossible

Oui, c'est bien ça. Je suis arrivé au même résultat.

c’est vrai, tu as raison : j’avais oublié le +1000 qui fausse le vrai effet aléatoire !

Paresse... quand le programme est petit (13% d'occupation de mémoire dynamique) et quand il n'y a pas d'appels de fonctions (pile peu utilisée), je ne le fais pas.

En mettant TCNT0 =0; // 1ms de calme le timer 0, celui de la remise à l'heure, il faudra attendre 256 fois 4µs avant la prochaine interruption, soit 1ms sans interruptions. Pendant la mesure, si elle dure moins de 1ms, je n'ai pas d'interruption. Si la mesure est supérieure, les 6µs ne se voient pas devant la mesure.

Ne change rien!

Sauf si le compilateur fait le calcul et a déjà préparé la bonne valeur!

Je suppose que si je donne une valeur fixe, le compilateur connait le résultat et remplace le calcul par la solution. en lui donnant un random, je pensais qu'il serait obligé de faire le calcul. Mais j'ai l'impression qu'il donne remplace le calcul par
b=15; transfert de deux octets -> cela peut prendre 2 cycles

En passant les variables en volatile, j'oblige le compilateur à ne pas optimiser.

  volatile float f=1000+random(10);
  volatile long l=1000+random(10);
  volatile word w=1000+random(10);

j'obtiens:

Code avec float:  79 cycles(s)   4937.50 ns   b=15
Code avec long:  152 cycles(s)   9500.00 ns   b=15
Code avec word:  225 cycles(s)   14062.50 ns   b=15

Si il n'y a pas d'erreurs, c'est contraire à ce que je pensais (float plus rapides, word plus lents).

En fait je ne cherche pas un nombre aléatoire, mais à faire croire au compilateur qu'il ne peut pas connaître la solution.

[edit] Nouveau programme complet:

// Variables utilisées pour faire la mesure
volatile byte b;

// Variables pour le programme
word t;

void setup()
{
  Serial.begin(115200);

  volatile float f=1000+random(10);
  TCNT0 =0; // 1ms de calme
  TCCR1B = 1; // Horloge à 16HHz
  TCNT1 = 0; // RAZ du compteur de cycle
                    b = ( 2327.0 + 55.0 * f)  /  ( 4654.0 - f);
  t = TCNT1-1;
  Serial.print("Code avec float:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t/0.016);
  Serial.print(" ns   b=");
  Serial.println(b);
  Serial.flush();
  

  volatile long l=1000+random(10);
  TCNT0 =0; // 1ms de calme
  TCCR1B = 1; // Horloge à 16HHz
  TCNT1 = 0; // RAZ du compteur de cycle
                    b = ( 2327ul + 55ul * l)  /  ( 4654ul - l);
  t = TCNT1-1;
  Serial.print("Code avec long:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t/0.016);
  Serial.print(" ns   b=");
  Serial.println(b);
  Serial.flush();

  volatile word w=1000+random(10);
  TCNT0 =0; // 1ms de calme
  TCCR1B = 1; // Horloge à 16HHz
  TCNT1 = 0; // RAZ du compteur de cycle
                    b = ( 2327 + 55 * w)  /  ( 4654 - w);
  t = TCNT1-1;
  Serial.print("Code avec word:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t/0.016);
  Serial.print(" ns   b=");
  Serial.println(b);

 }

void loop()
{
}

Mais cela n'explique pas la différence énorme avec le résultat du post#4

x is float time spent :92μs
15
x is unsigned long time spent :60μs
15
x is word time spent :60μs
15

Je reprends en me mettant plus près de mon problème. Dans le calcul d'un numéro de bouton pour lire un keypad de façon analogique (donc sur une seule broche), le numéro du bouton est donné par la formule
b = ( 2327 + 55 * w) / ( 4654 - w);
b est le numéro du bouton, que je renvoie dans un octet (je me limite alors à un keypad de moins de 256 boutons que de toutes façon je ne pourrait pas lire de façon fiable sur une seule entrée analogique)
w est un word, c'est le résultat d'un analogRead, donc compris entre 0 et 1023
2327 c'est la moitié de 4654
4654 devrait valoir 4654,5454 que je peux arrondir à 4654 ou 4655, cela ne fait pas une erreur bien grande. C'est un nombre fixe qui ne change pas.
55 est un nombre qui doit être dans une variable car il dépend du montage. Je devrais mettre 55,5 mais en arrondissant à 55 ou 56, cela fait une erreur acceptable. Mais ce serait mieux d'avoir 55,5 et donc d'utiliser des float si le temps de calcul n'est pas trop long. DAns certains cas il pourrait dépasser 61 et le calcul ne tient plus dans un word.

Je me pose donc des questions sur l'utilisation de word, long ou float

Les variables b et w seront certainement globales, ainsi qu'une variable contennant 55.

Premier cas: j'utilise des word, je limite le "55", je pense aller plus vite!
Deuxième cas: la valeur 55 peut dépasser 61 et je dois passer aux long
Troisième cas: je passe tout en float, je pense que c'est plus long mais cela colle plus au montage et me permet d'avoir un keypad plus grand car il y a moins d'erreurs dans le calcul.

C'est pour cela que je cherche à mesurer le temps de ces instructions.

Je peux prendre le programme de @fdufnews modifié avec mes besoins plus exacts:

  volatile word w=1000+random(10);;
  volatile byte b;
  volatile float cinquanteCinqFloat = 55.0;
  volatile unsigned long cinquanteCinqLong = 55L;
  volatile word cinquanteCinqWord = 55;
  unsigned long t0, t1;


void cinquanteCinq_est_float(void){
  t0 = micros();
  b = ( 2327 + cinquanteCinqFloat * w)  /  ( 4654 - w);
  t1 = micros();
  Serial.print("cinquanteCinq is float time spent :"); Serial.print(t1-t0);Serial.println("μs");
  Serial.println(b);
}

void cinquanteCinq_est_long(void){
  t0 = micros();
  b = ( 2327 + cinquanteCinqLong * w)  /  ( 4654 - w);
  t1 = micros();
  Serial.print("cinquanteCinq is long time spent :"); Serial.print(t1-t0);Serial.println("μs");
  Serial.println(b);
}

void cinquanteCinq_est_word(void){
  t0 = micros();
  b = ( 2327 + cinquanteCinqWord * w)  /  ( 4654 - w);
  t1 = micros();
  Serial.print("cinquanteCinq is word time spent :"); Serial.print(t1-t0);Serial.println("μs");
  Serial.println(b);
}

void setup() {
  Serial.begin(115200);

  cinquanteCinq_est_float();
  cinquanteCinq_est_long();
  cinquanteCinq_est_word();
}

void loop() {
  // put your main code here, to run repeatedly:

}

J'obtiens alors

cinquanteCinq is float time spent :60μs
15
cinquanteCinq is long time spent :44μs
15
cinquanteCinq is word time spent :16μs
15

Cela donne une bonne idée, mais dans le temps peut se trouver une remise à l'heure de l'hologe système, et mesure en même temps le temps mis par micros() et son affectation. La précision est de 4µs.

Je peux aussi compter les impulsions du quartz via le timer 1, cela supprime les interruptions (si la mesure dure moins de 1ms) et la précision est d'un cycle d'hologe. Le programme que j'utilise est:

volatile byte b;
volatile word w=1000+random(10);

volatile float cinquanteCinqFloat = 55.0;
volatile uint32_t cinquanteCinqlong = 55L;
volatile word cinquanteCinqWord = 55;

// Variables pour le programme
word t;

void setup()
{
  Serial.begin(115200);



  // Avec float
  TCNT0 =0; // 1ms de calme
  TCCR1B = 1; // Horloge à 16HHz
  TCNT1 = 0; // RAZ du compteur de cycle
                    b = ( 2327 + cinquanteCinqFloat * w)  /  ( 4654 - w);
  t = TCNT1-2;
  Serial.print("Code avec float:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t/0.016);
  Serial.print(" ns   b=");
  Serial.println(b);
  Serial.flush();

  
  
  // Avec long
  TCNT0 =0; // 1ms de calme
  TCCR1B = 1; // Horloge à 16HHz
  TCNT1 = 0; // RAZ du compteur de cycle
                    b = ( 2327 + cinquanteCinqlong * w)  /  ( 4654 - w);
  t = TCNT1-2;
  Serial.print("Code avec long:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t/0.016);
  Serial.print(" ns   b=");
  Serial.println(b);
  Serial.flush();

  
  
  // Avec word
  TCNT0 =0; // 1ms de calme
  TCCR1B = 1; // Horloge à 16HHz
  TCNT1 = 0; // RAZ du compteur de cycle
                    b = ( 2327 + cinquanteCinqWord * w)  /  ( 4654 - w);
  t = TCNT1-2;
  Serial.print("Code avec word:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t/0.016);
  Serial.print(" ns   b=");
  Serial.println(b);

 }

void loop()
{
}

Mais le résultat est surprenant:

Code avec float:  74 cycles(s)   4625.00 ns   b=15
Code avec long:  146 cycles(s)   9125.00 ns   b=15
Code avec word:  229 cycles(s)   14312.50 ns   b=15

Cela n'a aucun rapport avec le résultat précédent.

A priori le timer peut compter jusqu'à 65536 et avec un cycle de 62,5ns cela fait un temps de mesure maximal de 65536*62,5ns soit 4ms. A priori, suivant ce que me dit le code de @fdufnews, ce devrait être bon.

Je penche plutôt à une erreur dans mon programme.

Erreur trouvée: il faut rajouter: TCCR1A = 0; dans mon programme. On a alors le résultat:

Code avec float:  942 cycles(s)   58875.00 ns   b=15
Code avec long:  656 cycles(s)   41000.00 ns   b=15
Code avec word:  229 cycles(s)   14312.50 ns   b=15

Ce qui est cohérent avec le programme de @fdufnews.

Bonjour @vileroi

donc au final ton programme c'est ça :


volatile byte b; // Résultat du calcul
volatile word w = 1000 + random(10);  
volatile float cinquanteCinqFloat = 55.0;  // Coefficient sous forme float
volatile uint32_t cinquanteCinqlong = 55L; // Coefficient sous forme long
volatile word cinquanteCinqWord = 55;      // Coefficient sous forme word
word t; // Variable de mesure du nombre de cycles (16 bits)

void setup()
{
  Serial.begin(115200);

  // Mesure avec FLOAT 
  TCCR1A = 0; // Important : remet le Timer1 en mode normal
  TCCR1B = 1; // Horloge à 16 MHz sans prescaler (CS10 = 1)
  TCNT0 = 0;  
  TCNT1 = 0;  // RAZ du compteur de cycles

  // Calcul à mesurer
  b = (2327 + cinquanteCinqFloat * w) / (4654 - w);

  t = TCNT1 - 2; // Lecture du compteur (2 cycles d'ajustement arbitraire)
  Serial.print("Code avec float:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t / 0.016); // Conversion en nanosecondes (1 cycle = 62.5 ns)
  Serial.print(" ns   b=");
  Serial.println(b);
  Serial.flush(); // Vide le buffer UART avant de continuer


  //  Mesure avec UNSIGNED LONG 
  TCCR1A = 0; // Remise à zéro du mode de Timer1 (obligatoire)
  TCCR1B = 1; // Prescaler = 1 → 16 MHz
  TCNT0 = 0;
  TCNT1 = 0; // RAZ du compteur

  b = (2327 + cinquanteCinqlong * w) / (4654 - w);

  t = TCNT1 - 2;
  Serial.print("Code avec long:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t / 0.016);
  Serial.print(" ns   b=");
  Serial.println(b);
  Serial.flush();


  // Mesure avec WORD (uint16_t)
  TCCR1A = 0; // Toujours remettre à zéro
  TCCR1B = 1; // Prescaler = 1
  TCNT0 = 0;
  TCNT1 = 0;

  b = (2327 + cinquanteCinqWord * w) / (4654 - w);

  t = TCNT1 - 2;
  Serial.print("Code avec word:  ");
  Serial.print(t);
  Serial.print(" cycles(s)   ");
  Serial.print(t / 0.016);
  Serial.print(" ns   b=");
  Serial.println(b);
}

void loop()
{}

Bonjour à tous;

J’ai demandé à ChatGPT d’analyser la solution (message précédent). L’idée n’est pas de te mettre en défaut, mais de soumettre son approche à votre avis ?

:magnifying_glass_tilted_left: Points à corriger / préciser

:one: TCNT0 = 0; n’isole pas la fenêtre de mesure.

Remettre Timer0 à zéro ne coupe aucune interruption (UART, overflow Timer0…). Des ISR peuvent donc encore se produire pendant la mesure.

:two: TCCR1A = 0; indispensable (tu l’as ajouté ensuite).

Sans cette remise à zéro, Timer1 peut rester en mode PWM/compare → mesures incohérentes.

:three: t = TCNT1 - 2; = ajustement arbitraire.

Cela peut masquer un léger décalage (“off-by-one”). Il vaut mieux lire directement t = TCNT1;.

:four: Pas de barrière de compilation.

Même avec volatile, le compilateur peut réordonner des calculs.

Une barrière (asm volatile("" ::: "memory")) garantit que l’expression mesurée reste bien entre la RAZ et la lecture du timer.

:five: Interférences série entre blocs.

Sans Serial.flush() (et/ou cli()/sei()), l’UART peut encore émettre au début du bloc suivant → risque d’interruptions pendant la mesure.

:six: Voie 16 bits : risque de promotion signée / overflow.

Sur AVR, int = 16 bits signé.

55 * w (avec w≈1000) donne 55 000 > 32767 → dépassement.

Forcer l’arithmétique non signée : 2327u, 55u, 4654u et caster en uint16_t.

:seven: Affichage en “ns”.

Plus lisible d’afficher :

ns = cycles * 62.5 ou us = cycles / 16.0 (pour une horloge à 16 MHz).

:eight: volatile utile mais insuffisant seul.

Il garantit l’accès mémoire, pas l’ordre d’exécution. D’où l’intérêt de la barrière mémoire.

:test_tube: Code proposé

(même principe :random() avant, on mesure seulement l’expression)

Fenêtre propre : TCCR1A = 0, prescaler = 1, cli()/sei(), barrière mémoire, voie 16 bits non signée.

#include <Arduino.h>
#include <avr/interrupt.h>

volatile uint8_t b;   // effet observable
uint16_t t;           // cycles mesurés

__attribute__((noinline))
uint8_t calc_float(float x) {
  return (uint8_t)((2327.0f + 55.0f * x) / (4654.0f - x));
}

__attribute__((noinline))
uint8_t calc_ulong(uint32_t x) {
  return (uint8_t)((2327ul + 55ul * x) / (4654ul - x));
}

__attribute__((noinline))
uint8_t calc_word(uint16_t x) {
  return (uint8_t)((uint16_t)(2327u + 55u * x) / (uint16_t)(4654u - x));
}

template<typename F>
uint16_t measure_expr(F&& expr) {
  TCCR1A = 0;                 // Timer1 en mode normal
  TCCR1B = _BV(CS10);         // clk/1 → 1 tick = 1 cycle
  //TCNT1  = 0;

  cli();                      // coupe les ISR pendant la fenêtre
  TCNT1 = 0;                  // départ
  asm volatile("" ::: "memory"); // barrière: empêche tout réordonnancement
  b = expr();                 // *** expression mesurée ***
//expr() est par exemple la lambda [&]{ return calc_float(wf); }
// exemple : b = calc_float(wf);
  asm volatile("" ::: "memory");
  uint16_t cycles = TCNT1;    // arrêt
  sei();                      // réactive les ISR
  return cycles;
}

void setup() {
  Serial.begin(115200);
  while(!Serial) {}

  // FLOAT
  volatile float wf = 1000.0f + (float)random(10); // random AVANT
  Serial.flush();
  t = measure_expr([&]{ return calc_float(wf); });
  Serial.print(F("Code avec float:  "));
  Serial.print(t); Serial.print(F(" cycles   "));
  Serial.print(t * 62.5);
  Serial.print(F(" ns  (")); Serial.print(t / 16.0, 3);
  Serial.print(F(" us)   b=")); Serial.println(b);
  Serial.flush();

  // UNSIGNED LONG
  volatile unsigned long wl = 1000ul + (unsigned long)random(10);
  Serial.flush();
  t = measure_expr([&]{ return calc_ulong(wl); });
  Serial.print(F("Code avec unsigned long:  "));
  Serial.print(t); Serial.print(F(" cycles   "));
  Serial.print(t * 62.5);
  Serial.print(F(" ns  (")); Serial.print(t / 16.0, 3);
  Serial.print(F(" us)   b=")); Serial.println(b);
  Serial.flush();

  // WORD (16 bits)
  volatile uint16_t ww = 1000u + (uint16_t)random(10);
  Serial.flush();
  t = measure_expr([&]{ return calc_word(ww); });
  Serial.print(F("Code avec word:  "));
  Serial.print(t); Serial.print(F(" cycles   "));
  Serial.print(t * 62.5);
  Serial.print(F(" ns  (")); Serial.print(t / 16.0, 3);
  Serial.print(F(" us)   b=")); Serial.println(b);
}

void loop() {}

:wrench: Mesures obtenues (UNO @ 16 MHz)

Code avec float:         ~975 cycles  ≈ 60.94 µs   b=15
Code avec unsigned long: ~684 cycles  ≈ 42.75 µs   b=15
Code avec word:          ~233 cycles  ≈ 14.56 µs   b=15

:blue_book: Interprétation

• Plus les opérandes sont larges (16 < 32 < float), plus les routines de division sont longues → word < ulong < float**.**

:white_check_mark: Commentaire final :

Cette méthode n’invalide pas celle de l’auteur, mais la rend simplement plus “hermétique” et reproductible en neutralisant les effets du compilateur et des interruptions.

Bonne journée.

Le code précédent est celui que j'utilise maintenant. En fait TCCR1A est mis à 0 à l'initialisation du micro et cela a fonctionné pendant longtemps. Sans doute un changement dans le compilateur qui va écrire autre chose fait que cela n'a plus fonctionné.

On pourrait d'ailleurs ne mettre cette instruction qu'une seule fois en début de programme, mais c'est plus simple d'avoir trois blocs complets identiques. Normalement je n'utilise qu'un seul chronométrage à la fois.

 

 

Entièrement d'accord, je n'ai pas l'intention de supprimer les interruptions, mais seulement de ne pas avoir d'interruptions "overflow timer 0" pendant 1ms environ. Cela me permet par exemple de mesurer le temps d'une instruction x=0; ou x=20;

Le code que j'utilise n'a pas été écrit seulement pour cette formule, mais pour un peu tous les chronométrages que j'utilise. Et certains chronométrages ont besoin des interruptions. Par exemple:
Serial.print("A"); ---> 179 cycles(s) 11187.50 ns
Serial.print(F("A")); ---> 92 cycles(s) 5750.00 ns

 

 

Preuve en est que sans elle le programme ne fonctionnait plus. Mais pourquoi ce timer qui n'est pas utilisé par le système est initialisé à 1?

 

 

Ca, je n'ai pas compris. Si je fais

  TCNT1 = 0; // RAZ du compteur de cycle
     // vide
  t = TCNT1-2;

t vaut 2 car il faut deux cycles pour réaliser la deuxième instruction. Comme elle ne fait pas partie de la mesure, j'enlève 2.

L'IA veut dire faire:

  t = TCNT1;
  t -=2;

? Je ne vois pas alors ce qui peut être différent.

 

 

Je ne la connaissait pas.

 

 

Rajouté. En fait je n'avais jamais fait 2 mesures. Plutôt que de copier deux fois le bloc, je faisais une mesure, je modifiais le code et je faisais la deuxième mesure.

 

 

Je fais trop confiance au compilateur pour que 55*w donne un word si w est un word. Mais c'est une précaution que je peux prendre. Mais je ne l'ai jamais vu faire.

 

 

us = cycles / 16.0 c'est pareil que ns = cycles / 0.016
Mais ns = cycles * 62.5 est plus rapide, donc mieux.

 

 

Pour le code proposé, il ne correspond pas à mon code, en particulier x est un word, et il n'y a finalement aucune raison de faire 4654-x en float. Le cast peut se faire plus tard.

 

 

Je n'ai pas bien compris ce que veut dire hermétique. Mais pour moi ce code est compliqué à comprendre. Le code que j'utilise est plus simple, il me donne de bons résultats.

 

 

Pour l'interprétation, cela correspond à l'idée que j'en avais. Ce qui m'intéresse aussi est d'avoir une idée du temps sachant que ce calcul est fait régulièrement toutes les 16ms. Si il me faut 10ms pour faire ce calcul, cela aurait été de trop. mais si il faut moins de 100µs, je peux me permettre de tout faire en float et ainsi avoir moins d'arrondis.

 

 

Je n'ai pas d'abonnement à L'IA et je ne sais pour l'instant pas m'en servir. Il me serait utile pour la musique. Que donne la réponse à:
"Ecrire le code qui procure uniquement avec le timer 0, une interruption toutes les 1024µs, une interruption toutes les 1000µs et une interruption toutes les Nµs (N pouvant varier de 20µs à 1000µs)?"