Go Down

Topic: [Résolu] Une pause avec une interruption (Read 2029 times) previous topic - next topic

Sep 05, 2013, 05:45 pm Last Edit: Sep 08, 2013, 12:36 am by roipoussiere Reason: 1
Bonjour.

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 :

Code: [Select]

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");
}
N'attendez pas de savoir pour entreprendre.
Drawall, le robot qui dessine : http://drawall.cc/

Jean-François

#1
Sep 05, 2013, 06:46 pm Last Edit: Sep 06, 2013, 02:51 am by Jean-François Reason: 1
Est ce que ce ne serait pas plus simple un truc du style :

Code: [Select]
const int buttonPin = 2;
boolean state = 0 ;

void setup()
{
 Serial.begin(9600);
 attachInterrupt(0, onRelease, FALLING); // Lance onRelease() lorsque on relache le bouton
 pinMode(buttonPin, INPUT);
}

void loop()
{
Serial.println(state);
 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);
 }
 }
}

void onRelease()
{
state = !state ;
}

MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

#2
Sep 05, 2013, 06:56 pm Last Edit: Sep 05, 2013, 07:02 pm by roipoussiere Reason: 1
Merci pour ta réponse J.F.

Quote
Code: [Select]

void loop()
{
  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.
1. : 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 ;

2. : 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{}.
N'attendez pas de savoir pour entreprendre.
Drawall, le robot qui dessine : http://drawall.cc/

Jean-François

#3
Sep 05, 2013, 07:00 pm Last Edit: Sep 05, 2013, 07:03 pm by Jean-François Reason: 1
Ben non justement pas, si "state" est à 0, la boucle for n'est jamais executée.

Inverse la valeur du boléen lors de la déclaration.

et rien ne t'empèche de faire :

Code: [Select]

if (state)
{
1ère foncction()
2ème fonction()
etc()
}
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

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 ;
N'attendez pas de savoir pour entreprendre.
Drawall, le robot qui dessine : http://drawall.cc/

Jean-François

Et comme ça :

Code: [Select]
const int buttonPin = 2;
boolean state = 0 ;

void setup()
{
  Serial.begin(9600);
  attachInterrupt(0, onRelease, FALLING); // Lance onRelease() lorsque on relache le bouton
  pinMode(buttonPin, INPUT);
}

void loop()
{
Serial.println(state);

  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()
{
state = !state ;
while (state);
}
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

fdufnews

Avec la librairie Sleep_n0m1

J'ai extrait le code d'un bout de programme que j'avais utilisé pour tester cette librairie sur une carte Seeduino Film.

Code: [Select]
#include <avr/pgmspace.h>
#include <Sleep_n0m1.h>

Sleep sleep;
volatile boolean aulit=false;

void goSleep(void){
  aulit=true;
}

void goToSleep(void){
  aulit=false;
  detachInterrupt(0);
  ADCSRA &= ~_BV(ADEN);     // turns ADC off
  delay(200);
//  while(digitalRead(2)==0);
  sleep.pwrDownMode(); //set sleep mode
  sleep.sleepInterrupt(0,FALLING); //sleep for: sleepTime
  ADCSRA |=_BV(ADEN);      // turns ADC on
  delay(200);
//  while(digitalRead(2)==0);
  attachInterrupt(0,goSleep,FALLING);
}

void setup(void) {
  pinMode(2, INPUT);  // bouton poussoir
  // autres initialisation
  attachInterrupt(0,goSleep,FALLING);
}

void loop(void) {
// picture loop 
//  u8g.firstPage();
  if (aulit) goToSleep();
 
  // L'application est là
 
}


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.

Jean-François




Code: [Select]

[...]
void onRelease()
{
state = !state ;
while (state);
}



En faisant ça, tu restes bloqué à vie dans l'ISR dès que state est à '1'


C'est également ce qu'il me semblait, mais sans en avoir la certitude  :smiley-mr-green:
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

#8
Sep 06, 2013, 06:35 pm Last Edit: Sep 06, 2013, 06:41 pm by roipoussiere Reason: 1
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).
N'attendez pas de savoir pour entreprendre.
Drawall, le robot qui dessine : http://drawall.cc/

fdufnews

Quote
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.

#10
Sep 07, 2013, 02:37 am Last Edit: Sep 07, 2013, 02:47 am by roipoussiere Reason: 1
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 :
Code: [Select]
#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.
N'attendez pas de savoir pour entreprendre.
Drawall, le robot qui dessine : http://drawall.cc/

J'ai réalisé ceci :
Code: [Select]

void onRelease()
{
  EIMSK = 0;
  state = !state;
  digitalWrite(13, state);
  Serial.println("début 1er while");
  while(digitalRead(buttonPin) == 1)
  { // On attend le nouvel appui sur le bouton
    Serial.print(".");
  }
  Serial.println("fin 1er while");
  while(digitalRead(buttonPin) == 0)
  { // On attend le relachement
    Serial.print("-");
  }
  Serial.println("fin 2eme while");
  EIMSK = 1;
}


C'est étrange, Dès que j'appuie/relâche le BP, le programme est bloqué et il n'y a aucune des phrases ci-dessus qui s'affichent.
N'attendez pas de savoir pour entreprendre.
Drawall, le robot qui dessine : http://drawall.cc/

#12
Sep 07, 2013, 02:02 pm Last Edit: Sep 07, 2013, 02:06 pm by roipoussiere Reason: 1
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 :

Code: [Select]
#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 :)
N'attendez pas de savoir pour entreprendre.
Drawall, le robot qui dessine : http://drawall.cc/

fdufnews

Quote
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.

Code: [Select]
#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'
};
N'attendez pas de savoir pour entreprendre.
Drawall, le robot qui dessine : http://drawall.cc/

Go Up