un #define un peu compliqué pour gérer un time out

Salut la troupe!

Alors voilà, j'ai dans l'idée d'une fonction "time out".

Les bases : j'utilise le timer 0 (volontairement, je réécris un core utile et sans fioritures de micros(), delay() etc...).

Ce timer 0 tourne en continu, à vide.

Je définis une ISR ainsi :

volatile uint_16t msB;
ISR(TIMER0_COMPB_vect){
  msB--;
}

Puis vient la complication :

L'idée est d'avoir un define qui génère un code de time out et serait appelé ainsi :

boolean ok = timeOut(2000, PIND&0x10);  // attendre max 2000ms que l'entrée D4 passe à 1

Donc dans cet exemple, je j'attends que la condition "PIND&0x10" soit vraie, mais au bout d'un certain temps, on abandonne et on considère que le système qui doit valider la condition ne répond pas, et on en tiendra compte par la suite.

Une autre façon d'appeler ce time out pourrait être :

timeOut(2000, PIND&0x10);
if (timeOutOK) {  // la condition est vérifiée
...
} else {  // on a atteint le time out
...
}

La variable boolean timeOutOK ferait partie du core.

Le timeOut() serait un truc du genre :

boolean timeOutOK;

#define timeOut(tps,condition) (\
  timeOutOK=false;\ // initialiser la variable de résultat : false si la condition ne se réalise pas dans le temps imparti.
  msB=(tps);      // initialiser le compteur msB
  timer0_start;\    // démarrer le timer0
  while(msB){\      // tourner en boucle tant que msB n'est pas à 0 
    if((condition)){\  // la condition est vérifiée?
      timeOutOK=true;\ // alors on arrête tout, c'est bon!
      break;\
    }\
  }\
  timer0_stop;\  // arrêter le timer
)

D'après vous, c'est réalisable? ou tant pis, je me ferai hiech à mettre plusieurs lignes de code à chaque utilisation du time out...

??????

Merci!

Cinci.

J'ajoute tout de même que ce qui me pose problème, c'est l'utilisation de "condition" dans un #define. Je suis vraiment pas sûr que ça marche...

Par contre, le code en dur suivant marche à la perfection :

boolean timeOutOK;

  timeOutOK = false; // initialiser la variable de résultat : false si la condition ne se réalise pas dans le temps imparti.
  msB = 2000;      // initialiser le compteur msB
  timer0_start;    // démarrer le timer0
  while(msB) {      // tourner en boucle tant que msB n'est pas à 0 
    if(PIND & 0x10) {  // la condition est vérifiée?
      timeOutOK = true; // alors on arrête tout, c'est bon!
      break;
    }
  }
  timer0_stop;  // arrêter le timer
  if (timeOutOK) {  // la condition a été vérifiée
    ...
  } else {  // la condition n'a pas été vérifiée
    ...
  }

Et pourquoi pas une fonction?

J-M-L:
Et pourquoi pas une fonction?

C'est justement le dilème : dans timeOut(temps, condition), condition est une expression de test. Codée telle-qu'elle, condition sera évaluée avant l'appel de la fonction timeOut, alors que je voudrais que l'expression soit conservée et reportée dans le if() interne à timeOut()..

Si je prends l'exemple des boucles for() et while(), la condition passée en paramètre est conservée et évaluée à chaque itération. Je voudrais donc créer une boucle du même genre, mais qui dépend de deux conditions (la mienne, et la variable msB).

Ma question est comment passer cette expression en paramètre dans une macro #define sans qu'elle soit évaluée lors de l'appel? Puis-je faire confiance au préproc pour qu'il comprenne à coup sûr la manip? (je sais bien que timeOut() disparaîtra avant la compilation, et sera remplacée par le code que j'aurai réussi à faire)

En fait, je maitrise vraiment pas les multiples façons de jouer avec le préproc, bien qu'il ait l'air hyper puissant...

pepe:
Bonsoir

Si l'on a la garantie que les interruptions sont autorisées avant l'accès à la variable, alors on peut simplifier l'opération comme suit :

La remarque est judicieuse, je n'en étais pas encore arrivé là :wink: . Dans mon cas, on a plutôt intérêt à ce que les INT soient autorisées, puisque msB est décrémentée par une INT (toutes les millisecondes). le sei() trouve tout naturellement sa place ici.

Pour justifier de la complexité de mon affaire, ma fonction timeOut(temps, condition) servira à beaucoup de choses (attente d'un appui sur BP, attente d'un retour sur port série...), et condition peut être n'importe quelle expression booléenne, voire même des imbrications (condition1 || condition2 && condition3) par exemple. Avec ce genre de fonction magique, je m'affranchis d'un bon nombre de plantages dus à des boucles infinies...

Bonsoir à tous.

Ce soir, j'ai donc écrit ce bout de code :

volatile boolean T0_delay, T0_timeOut;  // flag de fin de delay(ms)
volatile uint32_t T0_msA;      // variable de comptage delay(ms)

#define delay(ms) \
                 T0_msA=(ms);\
                 TCNT0=0;\
                 TIMSK0|=0x02;\
                 while(!T0_delay){}\
                 T0_delay=false
                 
#define timeOut(tps, condition) \
                 T0_msA=(tps);\
                 TCNT0=0;\
                 T0_timeOut=true;\
                 TIMSK0|=0x02;\
                 while(!T0_delay){\
                   if(condition){\
                     T0_timeOut=false;\
                     break;\
                   }\
                 }\
                 T0_delay=false
                 
ISR(TIMER0_COMPA_vect){          // delay ISR
  if (!(T0_msA--)) {
    T0_delay = true; // flag
    TIMSK0 &= 0xFD;  // désactiver l'interruption
  }
}

Je ne peux pas encore le tester, mais si ce que j'ai lu est bon, ça devrait marcher. Ainsi, ma fonction delay(ms) remplacera celle d'origine arduino dont j'ai toujours besoin, car j'utilise encore la lib liquidChrystal dont je ne vais pas tarder à me libérer, car elle utilise des digitalWrite et ça me donne des boutons...

A suivre!

PS : est-il obligatoire ou simplement conseillé d'inclure la fonction yield() dans mes while? Personne ne semble avoir compris à quoi ça sert vraiment...