Je souhaiterais que mon programme Arduino se mette en pause lors de l'appui sur un bouton, à n'importe-quel moment de l’exécution.
Le programme reprendrait lors d'un nouvel appui sur le bouton.
J'ai donc naturellement pensé aux interruptions, mais toutes mes tentatives pour faire cela ont échouées...
Voici mon dernier programme :
const int buttonPin = 2;
void setup()
{
Serial.begin(9600);
attachInterrupt(0, onRelease, FALLING); // Lance onRelease() lorsque on relache le bouton
pinMode(buttonPin, INPUT);
}
void loop()
{
for(int i=0; i<100; i++) { // Programme simple pour vérifier l’exécution du programme principal
Serial.println(i);
delay(500);
}
}
void onRelease()
{
Serial.println("\nInterruption");
state();
while(digitalRead(buttonPin) == LOW) { // On attend le nouvel appui sur le bouton
Serial.print(".");
}
state();
}
void state() // Imprime l'état actuel du bouton
{
if (digitalRead(buttonPin) == HIGH)
Serial.println("Hight");
else
Serial.println("Low");
}
{
if(state){
for(int i=0; i<100; i++) { // Programme simple pour vérifier l’exécution du programme principal
Serial.println(i);
delay(500);
}
}
}
Alors non ça ne va pas.
: Si on j'appuie sur le bouton, le programme se mettra en pause lors de la fin du for(), alors que je voudrais une pause immédiatement
Edit : En fait non après test, le programme ne se met pas en pause du tout, ça affiche "1" et reprend le comptage.
Il faut par contre modifier la 2eme ligne :
volatile boolean state = HIGH ;
: En réalité mon programme est beaucoup plus long que les quelques lignes ici, je ne peux pas me permettre de tout regrouper dans un if{}.
Mais le soucis c'est que avec ce que tu me propose, l'appui sur le bp n'a aucune action immédiate, vu que le test de state se fait en dehors de la boucle...
PS : Je suppose que pour changer l'état de la variable tu voulais écrire : state = !state ;
Je voulais garder le contrôle du moment où la mise en sommeil se produisait ce qui explique l'utilisation de goSleep et goToSleep.
Tu dois pouvoir appeler directement goToSleep par attachInterrupt.
La mise en veille et le réveil se font par appui sur un bouton connecté à l'entrée 2.
La mise en veille de l'ADC est un facteur important pour la réduction de la consommation.
Je ne sais pas comment est câblé ton BP mais j'imagine au GND avec une pull-up, en fait peu importe.
Oui, j'ai mis aussi un condo en // avec l'inter pour faire un anti-rebond, étant donné que je ne peux pas le faire dans le prgm puisque l'interruption désactive le timer.
En faisant ça, tu restes bloqué à vie dans l'ISR dès que state est à '1'
Oui, je peux confirmer
==> il faut en fait, en entrant dans l'ISR, attendre de relâcher le BP, pour ensuite attendre un nouvel appui, puis encore un nouveau relâchement ...
Pourquoi ? L’interruption est déclenchée au moment où on relâche le bouton.
Comment faire alors?
fdunews, je vais d'abord voir si j'y arrive sans librairie, je ne sais pas de toute façon si elle me sera utile car je cherche pas a mettre en veille l'Arduino, la pause n'est jamais très longue (max 1min).
fdunews, je vais d'abord voir si j'y arrive sans librairie, je ne sais pas de toute façon si elle me sera utile car je cherche pas a mettre en veille l'Arduino, la pause n'est jamais très longue (max 1min).
Si tu avais seulement pris le temps de regarder la librairie en question, tu aurais vu qu'il y a le mode IDLE qui est une simple mise en pause du processeur soit pour un temps donné soit en l'attente d'une nouvelle interruption.
Si tu avais seulement pris le temps de regarder la librairie en question, tu aurais vu qu'il y a le mode IDLE qui est une simple mise en pause du processeur soit pour un temps donné soit en l'attente d'une nouvelle interruption.
Ok, j'avoue avoir parcouru un peu trop vite la librairie.
Alors j'ai fait ceci :
#include <Sleep_n0m1.h>
Sleep sleep;
void setup()
{
Serial.begin(9600);
attachInterrupt(0, onRelease, RISING); // Lance onRelease() lorsque on relache le bouton
}
void loop()
{
for(int i=0; i<100; i++) { // Programme simple pour vérifier l’exécution du programme principal
Serial.println(i);
delay(500);
}
}
void onRelease()
{
sleep.pwrDownMode(); //set sleep mode
sleep.sleepInterrupt(0, RISING); // sleep until interrupt
}
Résultat : Le programme s’exécute,
on appuie/relâche le bouton, le programme se met en pause,
on appuie/relâche le bouton à nouveau, le programme reprends son exécution,
Et là (hélas!), quand on appuie/relâche le bouton ça ne fait rien.
J'ai essayé en mode idle mais là rien ne se passe du tout.
5_cylindre : Déjà merci pour l'explication sur l'anti-rebond, j'ai l'impression qu'il marche un peu mieux (mais pas tout a fait encore, certains appuis sont encore comptés 2 fois).
Sinon j'ai mis le changé en RISING et j'ai attendu les différents états mais on reste toujours bloqué dans la pause.
I did it !!!
Merci à fdufnews pour sa solution avec la librairie Sleep_n0m1.
En fait le pb était que à l'appel de sleep.sleepInterrupt(0, RISING); la librairie désactive l'interruption. J'ai donc fait un nouvel appel de attachInterrupt() pour la réactiver.
Voici le code :
#include <Sleep_n0m1.h>
Sleep sleep;
void setup()
{
Serial.begin(9600);
pinMode(13, OUTPUT);
attachInterrupt(0, onRelease, RISING); // Lance onRelease() lorsque on relache le bouton
digitalWrite(2, HIGH); // set internal pullup
digitalWrite(13, LOW);
}
void loop()
{
for(int i=0; i<100; i++) { // Programme simple pour vérifier l’exécution du programme principal
Serial.println(i);
delay(500);
}
}
void onRelease()
{
digitalWrite(13, HIGH);
sleep.pwrDownMode(); //set sleep mode
sleep.sleepInterrupt(0, RISING); // sleep until interrupt
attachInterrupt(0, onRelease, RISING); // Lance onRelease() lorsque on relache le bouton
digitalWrite(13, LOW);
}
Il doit y avoir plus propre, mais bon ça marche, c'est déjà ça
En fait le pb était que à l'appel de sleep.sleepInterrupt(0, RISING); la librairie désactive l'interruption. J'ai donc fait un nouvel appel de attachInterrupt() pour la réactiver.
En fait la librairie Sleep_n0m1 utilise attachInterrupt pour ces propres besoins. Elle attache sa propre fonction de gestion de l'interruption (celle qui est appelée au réveil) et du coup au retour la fonction que l'on avait attachée est perdue.
Yeaaah !
Ça marche du feu de dieu ! Merci pour ce dernier message !
Un topic, 2 solutions
Ça me donne de plus en plus envie de coder en avr pur sans passer par arduino, on comprends bien mieux ce qu'on fait
J'ai réécris en Arduino-like pour que ça compile avec l'IDE, je me permet de poster ce code pour que les prochains qui cherchent a faire un truc identique s'y retrouvent.
Edit : Il y a un moyen de mettre ce topic en évidence ou de le recenser quelque part ? Je trouve ce code très utile, je ne dois pas être le seul à avoir ce besoin.
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
void setup()
{
SREG |= 128 ; // INTERRUPTIONS GLOBALES OK
EIMSK = 1 ; // INT0 OK
EICRA = 3 ; // FRONT MONTANT INT0
// POUR UN FRONT DESCENDANT : EICRA = 2 ;
Serial.begin(9600);
digitalWrite(2, HIGH); // set internal pullup
}
void loop()
{
// Compteur pour vérifier l’exécution du programme
for(int i=0; i<100; i++) {
Serial.println(i);
delay(500);
}
}
// ROUTINE D'INTERRUPTION
ISR (INT0_vect)
{
EIMSK = 0 ; // BLOQUER INT0
while ( PIND & 4 ) { } ; // ON ATTEND TANT QUE LA BROCHE EST À '1'
while ( ! ( PIND & 4 ) ) { } ; // ON ATTEND TANT QUE LA BROCHE EST À '0'
// POUR UN FRONT DESCENDANT IL FAUT INVERSER CES 2 LIGNES
EIMSK = 1 ; // RÉAUTORISER INT0
EIFR = 1 ; // ET REMETTRE SON FLAG À '0'
};
Edit : Il y a un moyen de mettre ce topic en évidence ou de le recenser quelque part ? Je trouve ce code très utile, je ne dois pas être le seul à avoir ce besoin.