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
}
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;
}
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 :~
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
(si je commençai à faire le contraire de ce que je dit de faire dans mes tuto ce serais pas le top ;))
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
pi_et_rho:
Y'a du mieux, mais avec un peu de volonté j'arrive encore à le planter
Si tu fait tout pour le planter ça va pas le faire
Bonne chance pour planter celui ci
#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
}
john_lenfr:
Skywodd, tu pourrais commenter le code que tu viens de mettre?
C'est fait
pi_et_rho:
Bonne chance pour planter celui ci
On y arrive encore ]
c'est des boutons en carton !? 200ms c'est énorme comme durée de rebonds
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
Ps: j'ai commenté tout particulièrement la partie avec la variable static, je pense que c'est ça qui te perturbe
c'est des boutons en carton !? 200ms c'est énorme comme durée de rebonds
Je crois bien que oui
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é.
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
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 ...
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
Mais c'est vrai que dans le cas d'un digitalWrite seul ta méthode est la meilleur
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...
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.
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;
}
/*==================================*/