[Résolu] interruption, delay et delayMicroseconds

Bonjour à tous
J'ai une télécommande Profalux qui émet 3 trames, ne voulant que la troisième trame j'ai écrit ce code mais les résultats sont différents suivant que j'utilise delay ou delayMicroseconds.
Je ne comprends pas pourquoi je ne peux pas récupérer que la troisième trame.(sortie pin 7 HIGH)

volatile byte Raz = 0;
void setup() {
  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);
  pinMode(3, INPUT);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(3), emets, RISING);
}



void loop() {
  
 if(Raz==1) {
 noInterrupts();
 digitalWrite(7, LOW);
 delay(210);
 digitalWrite(7, HIGH);
 delay(110);
 
 Raz=0;
 interrupts();
 }
   
    
  }

void emets() {
  Raz = 1;
 }
volatile byte Raz = 0;
void setup() {
  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);
  pinMode(3, INPUT);
  
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(3), emets, RISING);
}

void loop() {
  
 if(Raz==1) {
 noInterrupts();
 digitalWrite(7, LOW);
 delayMicroseconds(210000);
 digitalWrite(7, HIGH);
 delayMicroseconds(110000);
 
 Raz=0;
 interrupts();
 }
   
    
  }

void emets() {
  Raz = 1;
 }

Voir les copies d'écran.
delay
Microseconds

Merci pour les explications.
Cordialement Chabot380

Pour faire simple ce n’est pas une bonne idée de désactiver les interruptions puis d’essayer d’utiliser les fonctions de delay qui dépendent d’un timer et des interruptions

Les fonctions n’ont pas le même comportement et suivant l’attente nécessaire vous verrez aussi une variation de comportement

Dans votre cas ne désactivez pas les interruptions et soit vous utilisez le drapeau pour faire quelque chose de différent dans l’interruption, soit vous détachez juste la fonction pour qu’elle ne soit pas appelée dans la loop en cas de traitement

Pour faire simple ce n'est pas une bonne idée de désactiver les interruptions puis d'essayer d'utiliser les fonctions de delay qui dépendent d'un timer et des interruptions

delay() avec l'interruption normale activée... dure le temps qui est indiqué a quelques microsecondes (utilise micros()). Si on désactive l'inter système, delay va être beaucoup plus rapide (1). Essayez:

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  noInterrupts();}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(200000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(200000);                       // wait for a second
}

Par contre delayMicroseconds() ne dépend pas des interruption et fonctionne normalement interruptions ou pas;


(1) Si on coupe les inters le timer continue de tourner et les bits de poids faible de micros() évoluent, mais pas les bits de poids fors qui sont mis à jour par les interruptions. micros() fonctionne avec un débordement toutes les millisecondes environ, et la boucle d'attente du type micros()-départ>période ne joue plus son rôle d'attente.

Il me semble que delayMicroseconds() Ne compense pas le temps gagné par l’absence des interruptions (celles liées au débordement de timer 0). Je n’ai pas fait le calcul mais sur une durée demandée de 210000 ce sera visible et très supérieur à la granularité typique de 4 microseconds.

Problème résolu comme ça :

volatile byte Raz = 0;

void setup() {

  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);
  pinMode(3, INPUT);
  
  Serial.begin(9600);
  
}

void loop() {
 attachInterrupt(digitalPinToInterrupt(3), emets, RISING); 
 if(Raz==1) {
 detachInterrupt(digitalPinToInterrupt(3));
 digitalWrite(7, LOW);
 delay(214);
 digitalWrite(7, HIGH);
 delay(112);
 //attachInterrupt(digitalPinToInterrupt(3), emets, RISING);
 Raz=0;
 
 }
   
    
  }

void emets() {
  Raz = 1;
 }

Merci pour vos conseils
Cordialement
Chabot380

Problème résolu comme ça :

Avant de te dire au revoir, je viens de voir ceci:

delayMicroseconds(210000);

delay() a besoin d'un paramètre uint32_t, mais delayMicrosecond() a un paramètre uint14_t qui est transmis par un uint16_t (les deux bits de poids forts sont ignorés. Et si on prends les 14 bits de poids faible de 210000, cela donne
13392. En conséquence delayMicrosecond(210000) dure 13ms et pas 210ms.
Pour confirmer que ce que je raconte n'est pas complètement faux, il suffit de voir le référence de la fonction delayMicroseconds() On y voit que la durée maximale est de 16383µs

Il me semble que delayMicroseconds() Ne compense pas le temps gagné par l'absence des interruptions (celles liées au débordement de timer 0). Je n'ai pas fait le calcul mais sur une durée demandée de 210000 ce sera visible et très supérieur à la granularité typique de 4 microseconds.

Alors pas du tout d'accord.
Si je regarde le code pour la carte Uno (ma préférée, et je ne vais pas du tout analyser toutes les fréquences, seulement la 16MHz). La fonction microseconds() est une des très rares fonction ou il y a plus de commentaire que de code (je diras presque 10 fois plus ce caractères pour expliquer que pour faire). Cette fonction en 16MHz donc va faire une boucle qui dure 4 cyles, soit 0,25µs. Si on veut faire une temporisation de N µs, il faut faire 4N boucles. On passe le paramètre dans un entier, puis on le multiplie par 4. C'est pour cela qu'avec un AVR 16MHz on ne peut aller que jusqu'à 16ms. Avec un 8MHz, on peut aller jusqu'à 32ms (15 bits).

Il n'y a pas de compensation des 8µs pris par le débordemment du timer 0. Il compensent par contre l'appel de la fonction, son retour, la multiplication par 4 (5µs en tout). C'est donc le contraire, delayMicroseconds est exacte si on désactive le timer 0. sinon elle est trop lente de 0.8% environ.

D'autre part la granularité de 4µs n'est que pour le timer 0, et des fonctions qui en dépendent, micros() en particulier qui a donc une résolution de 4µs. Mais delayMicroseconds() a une résolution de 1µs. Et millis() qui retourne un entier qui saute de temps en temps des valeurs, a donc une résolution de 2ms.

On retrouve cette granularité de 4µs dans Servo.h au cas ou la période serait supérieur à 32ms. Mais si ce n'est pas le cas, cela n'aurait rien coûté d'avoir une résolution de 0,5µs. Dans la plupart des bibliothèques de pas à pas on retrouve cette résolution de 4µs, mais c'est parce qu'elles utilisent micros().

La doc dit

This function works very accurately in the range 3 microseconds and up. We cannot assure that delayMicroseconds will perform precisely for smaller delay-times.

Mais en fait en regardant à nouveau le code la doc n’est pas à jour et ils essayent de traiter cela correctement à 16MHz en effet

Et oui - j’avais oublié aussi que c’est un uint16_t et qu’il ne tient pas compte de tous les bits.

Sinon ça ira mieux demain, j’ai mélangé des bouts de fonctionnement de millis et micros pas des delay associés... donc j’ai dit des bêtises... ah le champagne....

Cela dit ce serait intéressant de regarder à nouveau ce que génère le compilateur pour cette fonction et voir si les timings de l’époque sont toujours vrais ou si le compilateur génère / optimise différemment (le code date d’il y a 3 ans, le compilo a évolué je pense)