J'aimerais utiliser un attachInterrupt pour lire rapidement mon encodeur

Bonjour,

J'aimerais lire mon encodeur lorsque l'axe qui le supporte tourne dans un sens ou dans l'autre et ainsi incrémeter ou décrémenter un compteur.

Pour cela j'utilise une déclaration attachinterrupt(pin,fonction,CHANGE).

Pour cela j'ai un capteur 2 voies et un moteur à courant continu.
Je fais tourner le moteur dans un sens puis dans l'autre grâce à une boucle que j'effectue 5 fois par exemple. Et pour ce faire, j'utilise les classiques : high et low sur les broches du moteur, ça fonctionne bien et ça s'arrête après la 5ème fois.

Malheureusement, le port série n'affiche que le numéro de la boucle et rien d'autre alors que j'ai bien créé mes attachinterrupt() et mes fonctions qui appellent une fonction d'affichage lorsqu'il y a un changement sur les bornes du capteur.

Pourriez-vous m'indiquer où est mon erreur s'il vous plait ?

#include <Encoder.h>
#define ENCODER_USE_INTERRUPTS
#include <digitalWriteFast.h>



// sensorInPin = 2   fil bleu du capteur sur l'Arduino UNO sur ligne d'interruption
// sensorRedPin = 3  fil rouge du capteur sur l'Arduino UNO sur ligne d'interruption
Encoder myenc(2,3);

const int sensA=6;        //borne sens horaire du moteur sur pin 6 du shield moteur
const int sensB=7;        //borne sens anti-horaire du moteur sur pin 7 du shield moteur
const int L298N_enA=4;    //contrôle de la vitesse du moteur sur pin 4 du shield moteur
int pwmMoteur=5;          //borne du capteur

unsigned char i = 0;

volatile long newPosition = 0;



void setup()
{
Serial.begin(115200); //connection série initialisée à 115200 bauds/s
 
 
pinMode(pwmMoteur,OUTPUT) ;
pinMode(sensA,OUTPUT) ; 
pinMode(sensB,OUTPUT) ;
pinMode(pwmMoteur, OUTPUT);
 
 
 attachInterrupt(sensA, gestioninterruption2, CHANGE);
 attachInterrupt(sensB, gestioninterruption3, CHANGE);
}

void loop()
{

  while(i<5) {

Serial.println("");
Serial.print(i+1);

//rotation sens horaire
digitalWrite(sensA,HIGH) ;
digitalWrite(sensB,LOW) ;
analogWrite(pwmMoteur,255) ;
delay(500) ;

//pour l'arret
digitalWrite(sensA,HIGH) ;
digitalWrite(sensB,HIGH) ;
delay(500) ;

//rotation sens anti-horaire
digitalWrite(sensA,LOW) ;
digitalWrite(sensB,HIGH) ;
delay(500) ; 

//pour l'arret
digitalWrite(sensA,HIGH) ;
digitalWrite(sensB,HIGH) ;
delay(500) ; 

i++;

 }

 }

 

void gestioninterruption2()
{
if (digitalReadFast2(2)== digitalReadFast2(3)) {
  newPosition--;
  affiche();
  }
else {
  newPosition++;
  affiche();
  }
}

void gestioninterruption3()
{
if (digitalReadFast2(3)== digitalReadFast2(2)) {
  affiche();
  newPosition++;
  }
else {
  newPosition--;
  affiche();
  }
}

void affiche()
{
newPosition = myenc.read();
Serial.print("Encoder is at: "); // print the position
Serial.println(newPosition);
  }

J'ajoute que la diode LD3 de l'arduino clignote lorsque je tourne à la main l'axe du moteur tout doucement, j'ai bien une interruption qui semble se faire...

Il y a plusieurs problèmes dans ton code

Tu utilises les bornes 6 et 7 pour le contrôle du sens de rotation de ton moteur
et les bornes 2 et 3 pour le capteur incremental

Ensuite tu associe une interruption avec changement d'état aux bornes 6 et 7 (qui sont les bornes de contrôle du moteur)

attachInterrupt(sensA, gestioninterruption2, CHANGE);
 attachInterrupt(sensB, gestioninterruption3, CHANGE);

Si tu utilise une carte Uno ou Mega, ces broches ne fonctionnent pas pour générer une interruption sur changement d'état

voir Arduino attachInterrupt

D'autre part serial.print utilise les interruptions pour transférer les données.
Pendant le déroulement d'une ISR, les autres interruptions sont bloquées
Autrement dit, tu ne peu pas appeler serial.print à l'intérieur d'une ISR

Bonjour,

La réponse de Alain46 est tout a fait pertinente, mais en plus tu utilises une librairie Encoder et tu essaies de faire la même chose en parallèle.

  • Soit tu utilises la librairie et tu laisses la librairie gérer les interruptions
  • Soit tu n'utilises pas de librairie et tu gères le interruptions toi même.

Mais pas les deux en même temps !!

Merci pour vos réponses.

J'ai revu mon code encore plus simplement pour partir sur de meilleures bases avec le code qui suit.

Mon interrogation maintenant : est-il possible de faire tourner le moteur pendant x seconde tout en utilisant la fonction attachinterrupt(), car j'ai l'impression que le moteur va continure à tourner du fait de la fonction.

#define EncoderInterrupt 0


volatile long _EncoderTicks = 0;


void setup() 
{
  Serial.begin(9600);
  attachInterrupt(EncoderInterrupt, InterruptA, RISING);
}


void loop()
{
  Serial.print(_EncoderTicks);
  Serial.print("\n");
  
  delay(20);
}


void InterruptA() 
{
  if (_EncoderTicks > 32766)
  {
    _EncoderTicks = 0;
    
  }
  _EncoderTicks++;
 
}

Je ne vois pas pourquoi le moteur continuerait à tourner du fait de l'interruption. Il va s'arrêter quand tu lui demande de s’arrêter.

Par contre ta gestion d'it est trop simpliste, elle ne prend pas en compte le sens.
Aussi je ne vois pas l'intérêt de ça: if (_EncoderTicks > 32766) ...

Ok, je vais tester de faire tourner le moteur et d'avoir les incrémentations qui s'affichent.

Le sens n'est pas pris en compte, c'est pour simplifier pour l'instant et n'avoir que les instructions simples.

Le if c'est pour tester une remise à 0.

Franck34:
Le if c'est pour tester une remise à 0.

Si tu déclares _EncoderTicks en unsigned, il va se remettre tout seul à 0 quand il va déborder.

Ok merci pour le conseil concernant le débordement, je ne savais pas.

J'essaye d'imprimer sur le port COM le nombre d'interruptions pendant la rotation du moteur mais impossible pour l'instant.

Je ne sais pas où placer :

  Serial.println("");
  Serial.print(_EncoderTicks);
  Serial.print("\n");

  delay(20);

Voici mon code qui ne m'affiche le nombre qu'une fois l'itération de la boucle faite :

#include <digitalWriteFast.h> 
#define EncoderInterruptA 0
#define EncoderInterruptB 1


const int sensA=6;        //borne sens horaire du moteur sur pin 6 du shield moteur
const int sensB=7;        //borne sens anti-horaire du moteur sur pin 7 du shield moteur
//const int L298N_enA=4;    //contrôle de la vitesse du moteur sur pin 4 du shield moteur
int pwmMoteur=5;          //borne du capteur

unsigned char i = 0;
volatile long _EncoderTicks = 0;



void setup()
{
Serial.begin(9600); //connection série initialisée à 9600 bauds/s
  attachInterrupt(EncoderInterruptA, InterruptA, CHANGE);
  attachInterrupt(EncoderInterruptB, InterruptB, CHANGE);
  
pinMode(pwmMoteur,OUTPUT) ; 
pinMode(sensA,OUTPUT) ;  
pinMode(sensB,OUTPUT) ;

 
}

void loop()
{
  
  
  while(i<5) {

  Serial.println("");
  Serial.print(_EncoderTicks);
  Serial.print("\n");

  delay(20);

Serial.println("");
Serial.print(i+1);

//rotation sens horaire
digitalWrite(sensA,HIGH) ;
digitalWrite(sensB,LOW) ;
analogWrite(pwmMoteur,255) ;
delay(500) ; 

//pour l'arret
digitalWrite(sensA,HIGH) ;
digitalWrite(sensB,HIGH) ;
delay(500) ; 

//rotation sens anti-horaire
digitalWrite(sensA,LOW) ;
digitalWrite(sensB,HIGH) ;
delay(500) ;  

//pour l'arret
digitalWrite(sensA,HIGH) ;
digitalWrite(sensB,HIGH) ;
delay(500) ;  

i++;

 }

 }

 
// Routine de service d'interruption attachée à la voie A du codeur incrémental
void InterruptA()
{
  if (digitalReadFast2(EncoderInterruptA) == digitalReadFast2(EncoderInterruptB)) {
    _EncoderTicks--;
  
  }
  else {
    _EncoderTicks++;
   
  }
}
 
// Routine de service d'interruption attachée à la voie B du codeur incrémental
void InterruptB()
{
  if (digitalReadFast2(EncoderInterruptB) == digitalReadFast2(EncoderInterruptA)) {
    _EncoderTicks++;
    
  }
  else {
    _EncoderTicks--;
    
  }
}

Pour faire les choses bien, il faudrait réorganiser ton programme sans utiliser delay() en faisant un automate d'état.

Pour faire un essai tu peux afficher le compteur pendant l'attente de marche moteur
Par exmple comme ça

    //rotation sens horaire
    digitalWrite(sensA, HIGH) ;
    digitalWrite(sensB, LOW) ;
    analogWrite(pwmMoteur, 255) ;
    unsigned long millisStart=millis();
    while (millis()-millisStart<500)
    {
      Serial.println("");
      Serial.print(_EncoderTicks);
      Serial.print("\n");
      delay(10);
    }

Ok je vais tester ça merci.
Concernant la lecture des voies de l'encodeur, se fera-t-elle assez rapidement pour ne rater aucun pas ?
Merci.

Il faudrait connaitre la fréquence des interruptions (qui dépend de la vitesse et de la résolution du codeur) pour répondre précisément, mais il y a toutes les chances pour que ce soit assez rapide.

Salut Kamil,

En fait je ne comprends pas l'instruction :

while (millis()-millisStart<1500)

Le moteur ne s'arrête jamais en fait ?

Si, il s'arrêtera au bout de 1500 ms

Bhen justement il ne s'arrête jamais...
Je crois qu'il faudrait une instruction pour qu'il s'arrête non ? ou l'arrêt de la boucle stop le HIGH ?

On va forcément sortir du while au bout de 500ms

Après ce qui se passe dépend de ton code. Montre la version ou le moteur ne s'arrête pas.