Problème sous IT avec Switch/case

Bonjour à Tous;
Je suis tout nouveau et retraité (personne n’est parfait) jadis j’ai programmé en assembleur du 8048 Intel et je découvre aujourd’hui l’Aduino et le C.
Mon PB est le suivant: je veux compter 4 passages devant un capteur et calculer le temps global qui s’est passé entre le 4 pulses. Le capteur électronique déclenche une IT qui a pour effet d’incrémenter un compteur. Selon la valeur du compteur mon swich /case devrait m’aiguiller vers une action. pour tester lentement j’utilise le moniteur série. L’interruption fonctionne bien mais mais dans ma tache de fond l’aiguillage ne fonctionne pas. Il y a probablement quelque chose que je n’ai pas compris malgré pas mal de temps à teste et de lecture. Ca fonctionne à la première impulsion puis plus rien ??

Le capteur est un capteur de présence sans antirebond et j’utilise une carte Arduino UNO R3
Je vous remercie si vous pouviez m’aider à comprendre où j’ai pu commettre une erreur

/* programme de lecture d'un capteur sous interrution 
le but est de compter le temps entre 4 passages devant un capteur.
il n'y a pas de rebond, le signal est un état qui change.*/

// les constantes
int pin = 2;              //Broche interruption
const int ledpin = 13;    //led (pas utilisée)

//les variables
volatile int nbpulse = LOW;   // déclaration variable volatile = stockée en RAM
//volatile
int pulse;           // variable intermediaire
unsigned long ecart;           // contient l'ecart de temps
unsigned long preval;         // première valeur
unsigned long finvaleur;      // dernière valeur


void setup()
{
  Serial.begin(9600); //Configuration de vitesse de transmission
  pinMode(ledpin, OUTPUT); 
  pinMode(pin, INPUT); 
  attachInterrupt(0,progpri,FALLING);
  Serial.println( "debut");
   
}
void loop()
{            /*en fonction de la valeur de pulse, on execute une action,
  ici on imprime un caractère*/
                       
switch (pulse) {
    case 1: // le compteur vaut 1, on mémorise le nb de millis, et imprime "a"
                 unsigned long premval = millis();
                 Serial.println("a");
                 delay(3000);
                 pulse=0;
                  break; 
        
    case 2:  // le compteur vaut 2,on imprime "b"
                Serial.println("b");
                delay(3000);
                pulse=0;
                break;
                
    case 3: // le compteur vaut 3,  on imprime "c"
                Serial.println("c");
                delay(3000);
                pulse=0;
                break;

    case 4: // le compteur vaut 4,c'est fini on lit millis et on calcule l'écart 
               finvaleur =  millis();
               ecart =  finvaleur -  preval;
               Serial.println("fin");
               pulse=0;
               delay(3000);
               break;
               
              } {Serial.println("z"); //cas ou pulse =0
                delay (200);
                } 
}
void progpri()// programme prioritaire
{
   Serial.println("1");
    nbpulse++;  // on incrémente nbpulse
    pulse = nbpulse; // on met à jour pulse  
}

Bonjour,

A quelle fréquence arrive tes pulses? Peut être que le compteur est déjà supérieur à 4 avant d'entrer dans le switch. Affiche la valeur de pulse au début de la loop.

Le delay(3000) est une très mauvaise idée. Avec ça tu a peut près sur de rater des pulses.

C'est aussi une mauvaise idée de faire un Serial.print() dans l'interruption, mais si c'est juste pour un test de l'interruption et qu'il sera enlevé après, ça passe.

Mais la plus grosse erreur est que premval doit être déclarée static sinon la valeur sera perdue au case 4:, ou plutot que premval est redéclarée alors que c'est une variable globale (qui s'appelle en fait preval).

je verrai bien pulse en volatile, comme l'est nbpulse... Pourquoi deux variables ?

al1fch: je verrai bien pulse en volatile, comme l'est nbpulse...

C'est même indispensable !

-> declarer volatile int pulse;

La raison est que la variable pulse est modifiée sous interruption, le fait de la déclarer volatile oblige le compilateur à relire effectivement cette variable à partir de la mémoire, à chaque utilisation. Sans cela, il peut décider de la cacher sur la pile, ou dans un registre, il optimise. Et le programme ne se rend pas compte que la variable a été modifiée.

Non, ce n’est pas indispensable.

C’est indispensable uniquement si la variable est évaluée plusieurs fois dans la fonction (par exemple while (pulse<4). Ça dit au compilateur que la variable peut avoir varier dans l’instruction.

Par contre le compilateur ne peut pas assumer qu’une variable globale n’a pas été modifiée en entrant dans une fonction, donc dans le cas d’une simple lecture unique de cette variable volatile n’est pas indispensable.

Bien sur, dans le doute, on peut mettre volatile même si non indispensable.

Un changement cosmétique serait de déclarer les compteurs en uint8_t (leur valeur maxi est de 4.... on a un petit peu de marge). Je sais que le gain de cycles est négligeable devant la longueur des délais, mais quand même.

hello un test

/* programme de lecture d'un capteur sous interrution
le but est de compter le temps entre 4 passages devant un capteur.
il n'y a pas de rebond, le signal est un état qui change.*/

// les constantes
int pin = 2;              //Broche interruption
const int ledpin = 13;    //led (pas utilisée)

//les variables
volatile int nbpulse = LOW;   // déclaration variable volatile = stockée en RAM
volatile int pulse;           // variable intermediaire
unsigned long ecart;           // contient l'ecart de temps
unsigned long preval;         // première valeur
unsigned long finvaleur;      // dernière valeur


void setup()
{
  Serial.begin(115200); //Configuration de vitesse de transmission
  pinMode(ledpin, OUTPUT);
  pinMode(pin, INPUT_PULLUP);//si pas de pullup externe
  attachInterrupt(0,progpri,FALLING);
  Serial.println( "debut");
   
}
void loop()
{            /*en fonction de la valeur de pulse, on execute une action,
  ici on imprime un caractère*/
 //delay(500); //antirebonds supprimé car tu dis ne pas avoir de rebonds
                       
switch (pulse) {
    
    case 1: {// le compteur vaut 1, on mémorise le nb de millis, et imprime "a"
                 unsigned long premval = millis();
                 Serial.println("a");
                 //delay(3000);
                // pulse=0;
                }
                  break;
       
    case 2: { // le compteur vaut 2,on imprime "b"
                Serial.println("b");
               // delay(3000);
               // pulse=0;
               }
                break;
               
    case 3:{ // le compteur vaut 3,  on imprime "c"
                Serial.println("c");
                //delay(3000);
                //pulse=0;
                }
                break;

    case 4: {// le compteur vaut 4,c'est fini on lit millis et on calcule l'écart
               finvaleur =  millis();
               ecart =  finvaleur -  preval;
               Serial.println("fin");
               //pulse=0;
               //delay(3000);
               }
               break;
    default:{
              Serial.println("z"); }
              break;                        
              } 
}
void progpri()// programme prioritaire
{
   //Serial.println(nbpulse);Serial.println(pulse);
    nbpulse++;  // on incrémente nbpulse
    if (nbpulse>4){nbpulse=1;}
    pulse = nbpulse; // on met à jour pulse 
}

Bonjour à tous et merci pour votre aide Kamill: La fréquence c'est moi qui la donne en passant devant le capteur on dira 2 Hz. Le programme tel qu'il est écrit avec des print c'est juste pour voir sur le moniteur série ce qui se passe. Dans le programme il y a 2 variables: l'une est sensée compter les pulses et l'autre sert pour l'aiguillage du switch et et remis à zero pour le passer qu'une seule fois.

OK j'ai compris pour PREVAL a ne pas re declarer.

dbrion06: Qu'est-ce que veut dire unit8 pour les compteurs ?

dfgh : désolé c'est idem normalement; je vois afficher Z tant que la valeur pulse =0 a chaque interruption on voit bien le "1" qui s'affiche et aussitôt aprèe le caractère "a" puis on revient bien sur la tache de fond et la 2 interruption pulse devrait être incrémente et la ca plante si vous avez d'autres idées merci je continue a chercher

dbrion06: Qu’est-ce que veut dire unit8 pour les compteurs ?

je crains d’avoir fait une faute de frappe: il fallait lire uint8_t : les compteurs ne dépassent pas 4, et tiennent confortablement sur 8 bits; les int -d- de l’arduio uno sont des 16 bits;
Ca a des conséquences désagréables, de travailler sur 16 bits: supposons que le programme principal modifie une variable partagée avec une routine d’interruption, et que, au moment où il a modifié la moitié, il soit interrompu: la routine d’interruption aura une valeur incohérente à sa disposition (ce sera une bug qui se présente rarement, mais qui n’en est que plus abominable à corriger). Dans votre cas, cela n’a pas -pour le moment- d’objet, mais il faut prévoir des évolutions futures.
De plus, un incrément sur 8 bits est rapode: sur 2 fois 8 bits, il faut prévoir un increment des 8 bits les moins significatifs et une propagation du report (comme quand vous faites une addition “à la main”). Autant avoir les structures de données les plus petites possible.
J’employe assez souvent des types “standard”: ex; sur Arduino, les int sont sur 16 bits; sur PC ils sont sur 32 bits, mais, si je veux tester du calcul sur arduino (alors que je dispose d’un PC), pour être raisonnablement sûr de ne pas avoir à faire d’efforts de portage, je déclare un “entier” ayant pour vocation d’être une partie de code Atduino en int16_t (entier non signé, allant de -32xxx à 32xxx); si je sais qu’il sera toujours >=, j’en fais un entier sur 16 bits non signé, int16_t…

le prg fonctionne. voir copie d’écran ci jointe
je l’ai modifié pour qu’il n’incrive à l’écran que si pulse change de valeur.
le hard se compose d’un BP entre GND et D2
et d’une capa montée au bornes du BP

/* programme de lecture d'un capteur sous interrution
le but est de compter le temps entre 4 passages devant un capteur.
il n'y a pas de rebond, le signal est un état qui change.*/

// les constantes
int pin = 2;              //Broche interruption
const int ledpin = 13;    //led (pas utilisée)

//les variables
volatile int nbpulse = LOW;   // déclaration variable volatile = stockée en RAM
volatile int pulse;           // variable intermediaire
unsigned long ecart;           // contient l'ecart de temps
unsigned long preval;         // première valeur
//unsigned long finvaleur;      // dernière valeur
unsigned long premval = millis();
int memo_pulse=pulse;
void setup()
{
  Serial.begin(115200); //Configuration de vitesse de transmission
  pinMode(ledpin, OUTPUT);
  pinMode(pin, INPUT_PULLUP);//si pas de pullup externe
  attachInterrupt(0,progpri,FALLING);
  Serial.println( "debut");
   
}
void loop()
{            /*en fonction de la valeur de pulse, on execute une action,
  ici on imprime un caractère*/
 //delay(100); //antirebonds supprimé car tu dis ne pas avoir de rebonds
if(memo_pulse!=pulse)
{
switch (pulse) {
   
    case 1: {// le compteur vaut 1, on mémorise le nb de millis, et imprime "a"
                 premval = millis();
                 Serial.println("a");
                 memo_pulse=pulse;
                }
                  break;
       
    case 2: { // le compteur vaut 2,on imprime "b"
                Serial.println("b");
               memo_pulse=pulse;
               }
                break;
               
    case 3:{ // le compteur vaut 3,  on imprime "c"
                Serial.println("c");
                memo_pulse=pulse;
                }
                break;

    case 4: {// le compteur vaut 4,c'est fini on lit millis et on calcule l'écart
               //finvaleur =  millis();
               ecart =  millis() -  premval;
               Serial.print("d  ");
               Serial.print(ecart);
               Serial.println("fin");
               memo_pulse=pulse;
               }
               break;
    default:{
              Serial.println("z"); }
              break;                       
              }
}
}
void progpri()// programme prioritaire
{
   //Serial.println(nbpulse);Serial.println(pulse);
    nbpulse++;  // on incrémente nbpulse
    if (nbpulse>4){nbpulse=1;}
    pulse = nbpulse; // on met à jour pulse
}

Merci, je n'avais pas vu que tu avais changé la vitesse de com. Je ne comprends pas bien le problème.

Cette petite application est pour faire un métronome par apprentissage du battement. A bientôt