Question sur les interruptions

Bonjour à tous, je suis nouveau sur le forum, et je me suis fraîchement attaqué au arduino.
Je ne connais pas encore grand chose, si ce n'est les bases.

J'expérimente les interruptions, ça marchote mais ce n'est pas propre...

Voila ma config:

Arduino Uno + Sparkfun CAN-BUS shield

J'utilise le shield juste pour l'interrupteur dans un premier temps, le but par la suite étant de faire un CAN datalogger...

Ce que je souhaite faire avec mon bout de code:

faire changer d'état une LED quand le bouton change lui aussi d'état.

relaché => LED éteinte
Actionné => LED allumée

J'utilise pour ça une interruption sur la pin 3 (interruption 1)

Le programme ne marche pas trop mal mais de temps à autres, un changement d'état est raté et on se retrouve avec:

relaché => LED allumée
Actionné => LED éteinte

Je ne comprends pas bien pourquoi cela arrive, voici mon bout de code:

//Skarfun CAN-Shield
//Broche A4 reliée à digit 3 (Click)
//Test des interruption avec pull-up

int LED = 8; //Built-in LED du sparkfun
int Input = 3; //Pin interruption n°1
int state = LOW; // etat de la LED

void setup()
{
  pinMode(LED, OUTPUT);
  pinMode(Input, INPUT);
  digitalWrite(Input, HIGH);//Activation pullup
  attachInterrupt(1, blink, CHANGE); // attache l'interruption externe n°1 à la fonction blink
}

void loop()
{
  digitalWrite(LED, state); //Pilotage de la LED
}

void blink() // la fonction appelée par l'interruption externe n°1
{
state = !state; // inverse l'état de la variable
}

Merci pour votre aide :slight_smile:

Salut! Merci pour le pseudo :slight_smile:

Oui j'avais déjà essayé ça, le résultat est plus ou moins le même, ça marche comme on s'y attends 9 fois sur 10 et la 10ème quelquechose foire.

Si je remplace par FALLING, la LED va changer d'état uniquement à l'appui du bouton dans la plupart des cas, mais de temps en temps elle vas :

  • ou changer 2 fois d'état
  • ou ne pas changer d'état (au moins de façon perceptible par l'oeil...)

C'est peut un problème hard (rebond du BP ou je ne sais pas, ça ne semble du coup pas être du code...)

Si quelqu'un à une idée?

Bonjour,

Le probléme vient des rebonds occasionné par le bouton poussoir, quand tu appui sur le bouton la mécanique dériére fait contact plus ou moins bien ce qui crée des "micro coupure" dans le signal que l'on appellent des "rebonds".

Il faut donc mettre en place un "anti rebonds", soit matériellement avec un condensateurs soit par logiciel avec un simple delay().

const byte LED = 8;
const byte Input = 3;

byte state = LOW;
volatile byte flag = false;

void setup()
{
  pinMode(LED, OUTPUT);

  pinMode(Input, INPUT);
  digitalWrite(Input, HIGH);

  attachInterrupt(1, blink, FALLING);
}

void loop()
{
  if(flag) {
    state = !state;
    flag = false;
  }
  digitalWrite(LED, state);
  delay(50); // anti rebonds, un appui sur le bouton n'est pris en compte qu'une fois toute les 50ms
}

void blink()
{
  flag = true;
}

Salut skywodd,

Merci pour ces info, en effet j'avais bien pensé à mettre un filtre anti rebond, mais je ne sais pas trop comment le mettre en oeuvre..

La solution que tu proposes est intéressante mais ne doit pas fonctionner dans mon cas car j'utilise une interruption et la fonction delay() ne marche pas à l'intérieur des interruptions :~

Il ne reste plus que le condo je crois :slight_smile:

pi_et_rho:
La solution que tu proposes est intéressante mais ne doit pas fonctionner dans mon cas car j'utilise une interruption et la fonction delay() ne marche pas à l'intérieur des interruptions :~

A tu regardais plus en profondeur comment j'avais fait, je n'ai pas mis le delay() dans l'interruption :wink:

(si je commençai à faire le contraire de ce que je dit de faire dans mes tuto ce serais pas le top ;))

J'ai regardé un peu trop vite tu as raison... :sweat_smile:

J'ai modifié ton code pour ne pas switcher l'état avant les 50ms, sans quoi un rebond provoque une nouvelle interruption remet le flag à 1 ce qui switch a nouveau l'état à la prochaine itération.

void loop()
{
  if(flag) {
    delay(50);// anti rebonds, un appui sur le bouton n'est pris en compte qu'une fois toute les 50ms
    state = !state;
    flag = false;
  }
  digitalWrite(LED, state);
 
}

Y'a du mieux, mais avec un peu de volonté j'arrive encore à le planter :wink:

pi_et_rho:
Y'a du mieux, mais avec un peu de volonté j'arrive encore à le planter :wink:

Si tu fait tout pour le planter ça va pas le faire :grin:

Bonne chance pour planter celui ci :wink:

#define LED 8 // Défini la broche sur la quelle est câblé la led
#define BOUTON 3 // Défini la broche sur la quelle est câblé le bouton

volatile byte state = LOW; // État de la led, par défaut LOW

// Fonction setup()
void setup()
{
  pinMode(LED, OUTPUT); // Place la broche LED en sorti
  pinMode(BOUTON, INPUT); // Place la broche BOUTON en entrée
  digitalWrite(BOUTON, HIGH); // Active la résistance de tirage au 5v sur la broche BOUTON
  attachInterrupt(1, blink, FALLING); // Active l'interruption INT1 sur un front descendant, avec comme fonction d'appel blink()
}

// Fonction loop()
void loop()
{
  digitalWrite(LED, state); // "écrit" l'état de la led sur la broche LED
}

// Fonction d'interruption blink()
void blink()
{
  static unsigned long last_t = 0; // Déclare une variable last_t, initialisé à 0 uniquement lors du 1er appel à la fonction blink(), et gardant sa valeur précédante au prochain appel à la fonction (-> static)
  unsigned long now_t = millis(); // Déclare une variable now_t, contenant le temps en ms depuis l'allumage de l'arduino
  if (now_t - last_t > 200) state = !state; // Si le delta entre last_t et now_t est supérieur à 200ms l'état de la led est inversé
  last_t = now_t; // Le temps actuelle est stocké dans last_t pour la prochaine interruption
}

Skywodd, tu pourrais commenter le code que tu viens de mettre?

Si tu fait tout pour le planter ça va pas le faire

Ah bah, je veux que ça soit robuste... :smiley:

Bonne chance pour planter celui ci

On y arrive encore ]:smiley:

Mais je ne comprends pas tout ton code
Comment marche millis(), il doit être croissant non? Est-il remis à l'activation d'une interruption?

john_lenfr:
Skywodd, tu pourrais commenter le code que tu viens de mettre?

C'est fait :slight_smile:

pi_et_rho:

Bonne chance pour planter celui ci

On y arrive encore ]:smiley:

:stuck_out_tongue_closed_eyes: c'est des boutons en carton !? 200ms c'est énorme comme durée de rebonds :stuck_out_tongue:

pi_et_rho:
Mais je ne comprends pas tout ton code
Comment marche millis(), il doit être croissant non? Est-il remis à l'activation d'une interruption?

millis() renvoi le temps en millisecondes depuis l'allumage de l'arduino, en fait j'ai juste fait un delay() non bloquant :wink:
Ps: j'ai commenté tout particulièrement la partie avec la variable static, je pense que c'est ça qui te perturbe :wink:

c'est des boutons en carton !? 200ms c'est énorme comme durée de rebonds

Je crois bien que oui :smiley:

Ps: j'ai commenté tout particulièrement la partie avec la variable static, je pense que c'est ça qui te perturbe

Oui parlons des choses qui me perturbent XD, je pensais que static était plus "une variable globale".
Tu me dis que l'attribution de la valeur 0 est vrai que si la variable n'a pas encore de valeur. Quel est l'intérêt de placer la ligne suivante dans l'interruption plutôt que dans le corps void setup()?
static unsigned long last_t = 0;

Une autre chose qui me perturbe

if (last_t - now_t > 200) state = !state;

C'est moi où il y a une erreur? last_t - now_t est forcément négatif non?

pi_et_rho:
Oui parlons des choses qui me perturbent XD, je pensais que static était plus "une variable globale".
Tu me dis que l'attribution de la valeur 0 est vrai que si la variable n'a pas encore de valeur. Quel est l'intérêt de placer la ligne suivante dans l'interruption plutôt que dans le corps void setup()?
static unsigned long last_t = 0;

Une variable static c'est pas variable global, c'est une variable local dont la valeur est conservé en mémoire lors de chaque appel à la fonction.
L'initialisation de la variable ce fait lors du premier appel à la fonction, ensuite c'est juste la valeur précédente qui est utilisé.

byte init = false;
int variable;

fonction test() {
  if(!init) {
    variable = 0;
    init = true;
  }
  // ...
}

équivalent :

fonction test() {
  static int variable = 0;
  // ...
}

Ce balader des variables global dans un petit programme ça pose pas de probléme, mais quand il s'agit d'un programme sur plusieurs fichier c'est tout de suite beaucoup plus ch*ant :wink:

pi_et_rho:
Une autre chose qui me perturbe

if (last_t - now_t > 200) state = !state;

C'est moi où il y a une erreur? last_t - now_t est forcément négatif non?

Oups! :. petite erreur c'est bien now_t - last_t ...

Voilà qui me rassure.

Ca marche beaucoup mieux :smiley:
Merci pour tes explications!!!

skywodd:

john_lenfr:
Skywodd, tu pourrais commenter le code que tu viens de mettre?

C'est fait :slight_smile:

Merci bien j'ai enregistré le code dans un coin :smiley:

De tous vos codes, c'est un peu inutile, car si loop() doit vérifier une variable, autant oublier l'interruption et faire :

loop(){
  digitalWrite(LED, digitalRead(input));
}

Moi, je propose ceci, en reprenant le premier code :

//Skarfun CAN-Shield
//Broche A4 reliée à digit 3 (Click)
//Test des interruption avec pull-up

// déclaration des pins en const, ou #define, comme on veut

const byte LED = 8; //Built-in LED du sparkfun
const byte Input = 3; //Pin interruption n°1

void setup()
{
  pinMode(LED, OUTPUT);
  pinMode(Input, INPUT);
  digitalWrite(Input, HIGH);//Activation pullup
  attachInterrupt(1, blink, CHANGE); // attache l'interruption externe n°1 à la fonction blink
}

void loop()    // et oui, il n'y a plus rien dans loop!
{

}

void blink() // la fonction appelée par l'interruption externe n°1
{
  digitalWrite(LED, digitalRead(Input)); //Pilotage de la LED : led = état bouton
}

Je n'ai rien changé à part blink() : loop() est maintenant complètement déchargée de la gestion led. Dans ton ancien code, si tu rentres dans une boucle un peu longue, un changement de bouton ne sera pas affecté à la led, alors que là, c'est directement dans l'interruption que ça se passe. C'est le principe même d'une interruption : on déclenche un évènement sur une condition qu'il n'y a pas besoin de vérifier, ça se fait tout seul.

Super_Cinci:
Je n'ai rien changé à part blink() : loop() est maintenant complètement déchargée de la gestion led. Dans ton ancien code, si tu rentres dans une boucle un peu longue, un changement de bouton ne sera pas affecté à la led, alors que là, c'est directement dans l'interruption que ça se passe. C'est le principe même d'une interruption : on déclenche un évènement sur une condition qu'il n'y a pas besoin de vérifier, ça se fait tout seul.

C'est vrai que tu peut mettre le digitalWrite directement dans l'interruption, mais je suppose que pi_et_rho ne va pas rester à un simple digitalWrite très longtemps donc autant qu'il est à disposition toute les méthodes possible :slight_smile:

Mais c'est vrai que dans le cas d'un digitalWrite seul ta méthode est la meilleur :wink:

De tous vos codes, c'est un peu inutile, car si loop() doit vérifier une variable, autant oublier l'interruption et faire :
Code:
loop(){
digitalWrite(LED, digitalRead(input));
}

Oui l'exemple est bidon, et si le but ultime du programme est d'allumer une LED lorsqu'on appui sur le bouton, alors les interruption n'ont pas vraiment intérêt... :smiley:

Mais sur un programme un peu plus compliqué qui gère l'enregistrement de grandeur physiques (ouverture de fichier, lecture des différentes entrée analogiques, écriture sur la SD etc...) on risque de rater l'appuis push qui demande à stopper l'enregistrement, et la la mise à 1 d'un flag proposée par skywodd est très intéressante pour permettre de terminer les actions en cours proprement.

effectivement, tu peux avoir dans ton code

loop()
{
  while (!flag)  // tant que flag = 0
  {
    enregistre_données();
  }
  flag = 0; // remise à zéro de flag
  // autres traitements;
}

Interruption() // appelée par INT1 en falling
{
  flag = 1;
}

C'est sûr que dans ce cas, on ne ratera pas l'appui sur le BP. les interruptions sont super puissantes car elles permettent de faire une sorte de multitâche (loop() tourne en continu et les INT permettent de faire d'autres choses "en même temps"). A creuser selon ce que tu recherches...

C'est sûr que dans ce cas, on ne ratera pas l'appui sur le BP. les interruptions sont super puissantes car elles permettent de faire une sorte de multitâche (loop() tourne en continu et les INT permettent de faire d'autres choses "en même temps"). A creuser selon ce que tu recherches...

C'est exactement ça. Comme je l'ai dit au début du post je débute, donc peut-être qu'une interruption pour ça c'est du gâchi, mais pour le moment c'est le plus efficace que j'ai trouvé.

A ce propos, j'ai un bout de code qui tourne mais qui est désespérément lent, j'enregistre à peine plus d'une valeurs à la seconde... =(

L'écriture sur une SD est-elle si lentes? Y'a sûrement des choses a améliorer dans mon code...

/*Date: 14/01/2012  ***********************************/
/*HW: ArduinoUNO + Sparkfun CAN-BUS & SD shield       */
/*Capteur: Potentiomètre sur PIN analogique 0     */


#include <SD.h>

#define Ready_LED 7 // LED 7 du Sparkfun indique Carte prête
#define Rec_State_LED 8 //Built-in LED du sparkfun
#define Input 3 //Pin interruption n°1
#define chipSelect 9 //SD_CS sur pin digital 9
#define analogPin 0 //Capteur n°1
byte state = LOW; // etat de la LED
int SensorValue = 0; //Valeur utile à enregistrer
byte ChangeState = false;
byte RecState = false; 


void setup(){
  
  /*======== Init I/O ==========*/
  pinMode(Ready_LED, OUTPUT);// LED "système" configurée en sortie
  digitalWrite(Ready_LED, LOW);// LED "système" éteinte
  pinMode(Rec_State_LED, OUTPUT); //LED "Rec" configuré en sortie
  digitalWrite (Rec_State_LED, LOW);//LED "Rec" éteinte
  pinMode(Input, INPUT); //Pin BP configurée en entrée
  digitalWrite(Input, HIGH);//Activation pull-up (Push par mise à la masse)
  
  
 /*======== Init interruption ============*/
  attachInterrupt(1, Push, FALLING); //Détection d'un front montant de l'appuis BP (Mise à la masse)

/*==============Variables=============*/


/*=========== Init SD ================*/

 pinMode(chipSelect, OUTPUT); // CS configuré en sortie
  
  if (!SD.begin(chipSelect)) {
   BlinkError();
  }//Verification présence SDCard
  else
  digitalWrite(Ready_LED, HIGH); //SDCard OK
  
}
  
  
/*============ Main Loop ==============*/
void loop () {
    int Value; //init de la variable lue (Capteur n°1)
    static byte RecState = 0;
    const int WriteOnFile = 2;
    
  if (ChangeState == true){//Interruption memorisée et non traitée
    RecState = RecMgt();//Traitement de l'interruption
    ChangeState = false;//RAZ du flag
  }
  
  if (RecState != false){//Enregistrement en cours
    Value = ReadSensor();//Lecture des capteurs
    SDMgt(WriteOnFile , Value);//Enregistrement de la valeur lue
  }    
}
/*==================================*/

/*=========Fonction Gestion SD===========*/
void SDMgt (int action, int data){
  File dataFile; //Definition de la variable dataFile
  static unsigned long StartTime;//Definition de la variable StartTime
  static unsigned long CurrentTime;//Definition de la variable CurrentTime
  /*int m_display;*/
  byte s_display;//Definition de la variable s_display
  byte ms_display;//Definition de la variable ms_display
  switch (action){
    
    case 1:// Demande d'ouverture du fichier (Début d'enregistrement)
    {
    File dataFile = SD.open("datalog.txt", FILE_WRITE);
    StartTime = millis();
    dataFile.println("***********************************");
    dataFile.println("***  debut de l'enregistrement  ***");
    dataFile.println("***********************************");
    break;
    }
    
    case 2://Demande d'écriture du fichier (Enregistrement en cours)
    {
    String dataString = "";
    const String TimeInfo = "Time : ";// definition chaine
    const String Separator = ".";// Init chaine 
    const String Space = "   ";// Init chaine 
    String msDisplay = "";// Init chaine 
    String sDisplay = "";// Init chaine 
    CurrentTime = millis() - StartTime;//Mesure temps
    /*m_display = CurrentTime/60000;*/
    sDisplay = ((CurrentTime%60000)/1000);//Mise en forme des secondes
    msDisplay = ((CurrentTime%60000)%1000)/10;//Mise en forme des millisecondes
        
    dataString += String(data); //dataString += String(data);
    dataFile.print(TimeInfo);
    dataFile.print(sDisplay);
    dataFile.print(Separator);
    dataFile.print(msDisplay);
    dataFile.print(Space);
    dataFile.println(dataString);  
    break;
    }
    
    
    case 3://Demande de fermeture di fichier (Fin d'enregistrement)
    {
    dataFile.println("***********************************");
    dataFile.println("***   fin de l'enregistrement   ***");
    dataFile.println("***********************************"); 
    dataFile.close();
    break;
    }
  }
}
/*===================================*



/*============Fonction lecture capteur==============*/
int ReadSensor(void){
    int sensorValue = analogRead(analogPin);//Lecture du capteur
    return sensorValue;  
}
/*================================================*/


/*=====Fonction Erreur SD==========*/
void BlinkError(){//Erreur d'init SD (Carte absente ou non valide)
  while(1){
    digitalWrite(Ready_LED, HIGH);
    delay (500);
    digitalWrite(Ready_LED, LOW);
    delay(500);
  }
}
/*==================================*/

/*=====Fonction Gestion de l'etat d'enregistrement=====*/
byte RecMgt(){
  static byte RecStatus = false;
  byte OpenFile = 1;
  byte CloseFile = 3;
  int Data = 0;  //Data vide pour SDMgt()
  if (RecStatus == false){
  SDMgt(OpenFile, Data);
  RecStatus = true;
  digitalWrite (Rec_State_LED, HIGH);
  }
  else{
    SDMgt(CloseFile, Data);
    RecStatus = false;
    digitalWrite (Rec_State_LED, LOW);
  }
  return RecStatus;
  
}
/*==================================================*/


/*=====Fonction dinterruption=======*/
void Push (){
  static unsigned long last_t = 0;
  unsigned long now_t = millis();
  if (now_t - last_t > 200){
  ChangeState = true;
  }
  last_t = now_t;
}
/*==================================*/

:astonished: damned !

Ton code est horriblement lent pour la simple et bonne raison que tu ouvre/écrit/ferme le fichier à chaque appel de la fonction SDmgt ...

Edit: heu ... ya un pas un probléme avec l'ouverture du fichier ...