Routine d'interruption : qu'est-ce qui cloche ?

Bonjour,

Je découvre peu à peu l'univers Arduino. Aujourd'hui je bloque sur un nouveau soucis lié aux routines d'interruption. J'ai lu pas mal de trucs à ce sujet, mais j'avoue ne pas avoir saisi tous les détails...

Pour faire simple je veux déclencher une ISR qui gère un encodeur rotatif (sur 3 entrées, en gros droite, gauche et appui). Je ne veux donc qu'une seule fonction qui se lance lors du changement d'état d'un des 3 pins de l'encodeur.

J'ai essayé ce simple code qui déjà ne fonctionne pas, censé interrompre loop() lors du changement d'état du pin 2. Je ne comprends pas ce qui ne va pas... Quelqu'un aurait une idée, des explications ? Ou alors une autre méthode pour arriver au même résultat ?

volatile bool interrupted = 1;

void setup() {
  Serial.begin(9600);
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18);
  sei();
}

void loop() {
if(interrupted == 1) { 
  Serial.println("Interrupted !"); 
  interrupted = 0; }
}

ISR(PCINT2_vect) {
  interrupted = 1;
}

Merci d'avance !

Avec quelques modifications, ça devrait mieux fonctionner:

volatile bool interrupted = 0;

void setup() {
  Serial.begin(9600);
  while(!Serial);
  pinMode(2,INPUT);
  cli();
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18);
  sei();
}

void loop() {
if(interrupted == 1) { 
  Serial.println("Interrupted !"); 
  interrupted = 0; }
}

ISR(PCINT2_vect) {
  interrupted = 1;
}

Hello, merci d'avoir répondu !

J'avais bien déclaré ma variable volatile, c'était un oubli de copier / coller.
En revanche j'avais oublié le pinMode. Une question : À quoi sert while(!Serial); ? Attendre l'initialisation du moniteur série ?

J'ai testé ton code, il ne fonctionne pas, rien ne se passe...

J'ai téléchargé une bibliothèque "Rotary master" (ci-jointe) pour mon encodeur rotatif qui contient 2 exemples : "Polling" et "Interrupt" respectivement en scrutation et en interruption. Le code de "polling" fonctionne, mais pas celui de "interrupt" (basé sur le même principe que ce que j'essaie de faire). Voici le code :

#include <Rotary.h>

Rotary r = Rotary(2, 3);

void setup() {
  Serial.begin(9600);
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
}

void loop() {

}

ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if (result == DIR_NONE) {
    // do nothing
  }
  else if (result == DIR_CW) {
    Serial.println("ClockWise");
  }
  else if (result == DIR_CCW) {
    Serial.println("CounterClockWise");
  }
}

Je suppose qu'il est correct puisque provenant des exemples de la bibliothèque, non ? D'où peut venir le problème ?
Merci par avance !

Rotary-master.zip (5.76 KB)

Chez moi le code que j'ai transmis fonctionne (testé avec pinMode(2, INPUT_PULLUP)). Pour éviter de récupérer du bruit sur la pin 2 qui pourrait être interprété comme un signal valide, il faudra mettre une résistance de pull_down.

Le while(!Serial); est recommandé après un Serial.begin() sur un AVR.

Pour la suite, commence par appliquer les modifications transmises...

Une question : À quoi sert while(!Serial); ? Attendre l'initialisation du moniteur série ?

sur certains Arduino le port USB n'est pas géré en matériel (USB CDC) et il faut donc un peu de temps pour que la connexion s'établisse et que l'instance Serial ne soit pas un pointeur vers NULL

Si vous avez un Uno ou MEGA par exemple ça n'est pas nécessaire de le mettre (mais ça ne coûte rien puisque Serial ne sera pas NULL donc vous n'entrez même pas dans le while() mais si vous utilisez un 32u4 genre (Leonardo, Yùn etc) alors vaut mieux le faire si vous voulez être sûr de ne rien perdre de la communication

vous avez quel type d'arduino?

(il faut vous assurer que votre PIN 2 ne flotte pas)

ard_newbie, j'ai essayé ton code, mais rien ne se passe l'arduino n'exécute jamais l'ISR :frowning:
J-M-L, j'ai un Mega2560. Que le pin ne flotte pas ? Qu'est-ce, et comment le vérifier ?
Merci pour vos réponses !

OOps... PinMode(2, INPUT) c'est pour la UNO sur la pin 2 (PCINT18), pour la Mega 2560 selon le pinout diagram que tu dois avoir, ça doit être la pin A10, donc pinMode(A10, INPUT) et PCINT19 sur la pin A11 (pinMode(A11, INPUT).

Exact !! Merci, j'ai simplement déplacé mon encodeur rotatif sur les pins 10, 11 et 12 (respectivement PCINT4, 5 et 6). Voici le code corrigé (qui gère la rotation et l'appui) si quelqu'un ayant le même problème passe par là :

#include <Rotary.h>
volatile byte encodeur = 0; // 1 : GAUCHE, 2 : DROITE, 3 : APPUI
Rotary r = Rotary(10, 11);

void setup() {
  Serial.begin(9600);
  pinMode(10,INPUT);
  pinMode(11,INPUT);
  pinMode(12,INPUT_PULLUP);
  PCICR |= (1 << PCIE0);
  PCMSK0 |= (1 << PCINT4) | (1 << PCINT5) | (1 << PCINT6);
  sei();
}

void loop() {
if(encodeur != 0) {
Serial.println(encodeur);
encodeur = 0;
}
}

ISR(PCINT0_vect) { 
  unsigned char result = r.process();
  if(!digitalRead(12)) encodeur = 3;
  if (result == DIR_NONE) {
    // do nothing
  }
  else if (result == DIR_CW) {
    encodeur = 1;
  }
  else if (result == DIR_CCW) {
    encodeur = 2;
  }
}

Encore merci, ça faisait un moment que je bloquais sur ce maudit encodeur !!