Interruption non prises en compte

Bonjour à tous,

j'ai fait un petit programme qui incrémente/décrémente une variable (affichée sur un 7 segments) en fonction du sens de rotation et du nombre de pas d'un encodeur rotatif.

Ce dernier est relié (après filtre RC) aux broches 2 et 3 de l'arduino Uno.

Mais quand je tourne la roue de l'encodeur tous les pas ne sont pas pris en compte et de manière à priori aléatoire alors qu'en regardant avec l'analyseur logique les signaux sensés déclencher les interruptions sont corrects.

Est-ce que vous auriez une idée qui permette d'expliquer cela ?

Bonne journée, Phaust.

Peux-tu poster un schéma de branchement et ton code afin qu'on en sache plus ?

Il y a ici un exemple de mise en oeuvre qui pourrait ressembler à ce que tu veux faire. Un autre ici qui utilise aussi des filtres RC.

La prise en compte des interruptions est différente dans les deux cas : périodique ou sur un changement de niveau logique. C'est pourquoi il faut que tu donnes plus d'info, mais tu peux déjà voir si les valeurs que tu as choisies pour R et C sont similaires à celles du second exemple.

Bonjour,

Je suis curieux de voir la forme du signal apres le filtre RC.

Je suis curieux de voir la forme du signal apres le filtre RC.

en regardant avec l'analyseur logique les signaux sensés déclencher les interruptions sont corrects.

L'analyseur logique le trouve correct...... l'oscilloscope sans doute nettement moins... ;) ;)

De mon côté je parie sur des Serial.print() ou autres traitements bien lourds dans la routine d’interruption… :smiling_imp:

comment j’me fait trop troller ! mouahahhahaha !

Mais ouais, j’ai dû faire une bêtise quelque part.

Plus sérieusement, en suivant les tuto RC (comme celui donné en lien) ça ne marchait pas du tout et en vérifiant à l’analyseur logique, après filtre (du style 10k/100nF) le signal restait quasiment tout le temps à l’état logique 1.

En regardant l’analyseur sans filtre j’ai vu que les rebonds étaient de l’ordre de la µs et en passant sur un filtre RC (100ohms/100nF) j’ai le résultat tout propre en PJ et mon arduino répond (presque) correctement.

J’ai effectivement eu le soucis avec les Serial mais il n’y en a plus depuis longtemps.

Maintenant, quand je tourne l’encodeur, ma variable n’est pas systématiquement incrémentée et je peux même parfois faire 7 ou 8 pas sans qu’ils ne soient pris en compte.

Niveau oscillo, j’en ai juste un tout vieux, cathodique alors c’est chaud patate de visualiser le signal après filtre.

// Définition des broches de commande du décodeur BCD vers décimal (5411).
static const int A = 7;
static const int B = 6;
static const int C = 5;
static const int D = 4;

// Définition des broche de commande d'affichage des digits
static const int MIN_TEN_PIN = 8;
static const int MIN_ONE_PIN = 9;
static const int SEC_TEN_PIN = 10;
static const int SEC_ONE_PIN = 11;

// Définition des broches de l'encodeur
static const byte ENCODER_PIN_A = 2; // Interruption
static const byte ENCODER_PIN_B = 3; 

static const int MIN_TEN = 0;
static const int MIN_ONE = 1;
static const int SEC_TEN = 2;
static const int SEC_ONE = 3;

// Le bouton poussoir permettant de lancer le compte-à-rebourd est relié à une broche d'interruption.
//static const int START_PIN = 3;

// Constante de temps de rebond 
static unsigned long debounce = 200;
const static byte ENCODER_DEBOUNCE = 100;

// Dernier instant considéré de changement de valeur du bouton poussoir de compte-à-rebourd pour le temps de rebond
unsigned long previousChange = 0;

static const int DEFAULT_START_TIME = 180; // 3 minutes

int minutes = 3;
int seconds = 0;

void setup() {
  Serial.begin(9600);
  
  pinMode(A, OUTPUT);
  pinMode(B, OUTPUT);
  pinMode(C, OUTPUT);
  pinMode(D, OUTPUT);

  pinMode(MIN_TEN_PIN, OUTPUT);
  pinMode(MIN_ONE_PIN, OUTPUT);
  pinMode(SEC_TEN_PIN, OUTPUT);
  pinMode(SEC_ONE_PIN, OUTPUT);
  
  //pinMode(START_PIN, INPUT_PULLUP);
  //attachInterrupt(digitalPinToInterrupt(START_PIN), countDownStart, FALLING);

  pinMode(ENCODER_PIN_A, INPUT_PULLUP);
  pinMode(ENCODER_PIN_B, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), changeTime, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), changeTime, CHANGE);
  
  digitalWrite(A, LOW);
  digitalWrite(B, LOW);
  digitalWrite(C, LOW);
  digitalWrite(D, LOW);

  digitalWrite(MIN_TEN_PIN, LOW);
  digitalWrite(MIN_ONE_PIN, LOW);
  digitalWrite(SEC_TEN_PIN, LOW);
  digitalWrite(SEC_ONE_PIN, LOW);
  
  
}

volatile int startTime = DEFAULT_START_TIME;
bool isCountdownStarted = false;

int count = 0;
int digit = MIN_TEN;
unsigned long previous = 0;
unsigned long previousCountDown = 0;

unsigned long previousEncoderMillis = 0;

void loop() {

  if( (millis() - previous) > 3) {
    // SEC_ONE case
    int actualDigitPin = SEC_ONE_PIN;
    int previousDigitPin = SEC_TEN_PIN;
    int digitValue = 15; // ne peut être affiché

    // Rappel : si le résultat de l'opération est un nombre décimal, arduino conserve uniquement
    //          la partie entière sans approximation.
    int minTenValue = (startTime / 60) / 10;
    int minOneValue = startTime / 60 - minTenValue * 10;
    int secTenValue = (startTime - (minTenValue*10 + minOneValue) * 60) / 10;
    int secOneValue = startTime - (minTenValue*10 + minOneValue) * 60 - secTenValue * 10;
    
    switch(digit) {
      case MIN_TEN:
        actualDigitPin = MIN_TEN_PIN;
        previousDigitPin = SEC_ONE_PIN;
        digitValue = minTenValue;
        break;
      case MIN_ONE:
        actualDigitPin = MIN_ONE_PIN;
        previousDigitPin = MIN_TEN_PIN;
        digitValue = minOneValue;
        break;
      case SEC_TEN:
        actualDigitPin = SEC_TEN_PIN;
        previousDigitPin = MIN_ONE_PIN;
        digitValue = secTenValue;
        break;
      default: // SEC_ONE
        digitValue = secOneValue;
    }
    digitalWrite(previousDigitPin, LOW); 
    setDigitValue(digitValue);
    digitalWrite(actualDigitPin, HIGH); 

    digit++;
    if(digit > 3) digit = MIN_TEN;
    previous = millis();
  }
/*
  if(isCountdownStarted && (millis() - previousCountDown > 1000)) {
    startTime--;
    if(startTime == 0) isCountdownStarted = false;
    previousCountDown = millis();
  }
 */
}

volatile byte seqA = 0;
volatile byte seqB = 0;

void changeTime() {
  //Ne pas utiliser de serial ici ça ralenti le système
  byte a = digitalRead(ENCODER_PIN_A);
  byte b = digitalRead(ENCODER_PIN_B);

  seqA = ((seqA << 1) | a) & 0b00001111;
  seqB = ((seqB << 1) | b) & 0b00001111;
 
 if (seqA == 0b00001001 && seqB == 0b00000011){
     startTime--;
  } else if ( seqA == 0b00000011 && seqB == 0b00001001 ){
    startTime++;
  }
}

void countDownStart() {
  Serial.println();
  previousCountDown = millis();
  startTime = DEFAULT_START_TIME;
  isCountdownStarted = true;
}
void setDigitValue(int valueToDisplay) {
    int a0 = bitRead(valueToDisplay, 0);
    int a1 = bitRead(valueToDisplay, 1);
    int a2 = bitRead(valueToDisplay, 2);
    int a3 = bitRead(valueToDisplay, 3);

    digitalWrite(A, a0);
    digitalWrite(B, a1);
    digitalWrite(C, a2);
    digitalWrite(D, a3);
}

void plusOne() {
  if ((millis() - previousChange) > debounce) {
    count++;
    if (count > 9) count = 0;
    previousChange = millis();
  }
}

signaux après filtrage.png

signaux avant filtrage.png

Bonsoir,

La résistance de PULL-UP est mal placée. Il ne faut pas utiliser celle qui est interne mais en placer une juste avant le filtre RC.

|500x250

OK, je vais essayer. Est-ce que tu peux m'expliquer pourquoi il ne faut pas utiliser la pullup interne ?

PhaustSceptic:
Est-ce que tu peux m’expliquer pourquoi il ne faut pas utiliser la pullup interne ?

Si tu utilises la pull-up interne la constante RC que tu as calculé n’est valide que dans un cas.

Pendant un état “bas” de l’encodeur le condensateur se déchargera dans la résistance de 100 Ohms.

Pendant un état 'haut" de l’encodeur le condensateur se rechargera au travers de la résistance de pull up interne dont sa valeur se situe sur une plage de 20k à 50k.

Le signal mettra bien plus longtemps pour se stabiliser. Ce n’est peut-être pas la cause racine du problème, mais c’est un détail qui a son importance.

OK, je pense avoir compris le principe mais, du coup, dans le schéma que tu donnes ça ne va pas non plus car quand l’interrupteur est ouvert le condensateur est chargé au travers des deux résistance mais se décharge à travers une seule.

Cela ne devrait pas plutôt être comme celui-ci (ne pas regarder les valeurs) ?

J’ai testé avec mon schéma (R=10k C=1nF) et ça ne marche plus du tout. A l’oscillo, la différence de tension entre les min est max du signal en entrée de l’arduino (plus de INPUT_PULLUP du coup) est bien plus petite que les 3.3V attendus.

J’ai testé le filtre RC à côté et il a l’air de bien fonctionner.

Du coup, je vais revenir à mon premier montage qui marchait presque bien à par les valeurs ratées parfois.

Quelqu’un a-t-il une idée de pourquoi certains pas de l’encodeur ne sont pas pris en compte par l’arduino ?

PhaustSceptic: J'ai testé avec mon schéma (R=10k C=1nF) et ça ne marche plus du tout. A l'oscillo, la différence de tension entre les min est max du signal en entrée de l'arduino (plus de INPUT_PULLUP du coup) est bien plus petite que les 3.3V attendus.

J'ai testé le filtre RC à côté et il a l'air de bien fonctionner.

Du coup, je vais revenir à mon premier montage qui marchait presque bien à par les valeurs ratées parfois.

Quelqu'un a-t-il une idée de pourquoi certains pas de l'encodeur ne sont pas pris en compte par l'arduino ?

Bonsoir C'est quoi exactement ton encodeur ? Teste la lib encoder de PJRC

tu ne dois pas appeler la routine d'interruption pour les deux signaux. testes en mettant la ligne 58 en commentaire et en changeant le "CHANGE" de la ligne 57 par un "FALLING"

@Artouste j'ai acheté ça dans une boutique d'électronique, c'est un encodeur avec bouton poussoir. Je ne vais pas utiliser de librairie parce que le but c'est de comprendre comment ça marche :D

@dfgh oui, j'ai vu des tutos qui présentaient cette méthode mais maintenant que j'ai compris la séquence complète ça m'embête de tout casser pour en implémenter une autre.

@-Standby me disait que le problème avec mon montage était que je me retrouvais avec deux constantes de temps différentes mais le problème est le même avec celui qu'il m'a proposé. J'ai trouvé pourquoi celui que je proposais ensuite ne fonctionne pas mieux : il y a un pont diviseur de tension (divisé par 2) qui empêche le condensateur de se décharger complètement. C'était d'ailleur vrai dans mon premier montage mais ma petite résistance de 100 ohm faisait un rapport de 1/100 ce qui permettait de décharger le condensateur.

Là, je me dit qu'il n'y a pas de montage "propre" pour assurer une charge et décharge identiques.

Et le problème initial que tous les pas ne sont pas pris en compte n'a pas de piste de résolution pour le moment.

Mais je ne lâche rien ! ahahahahah !!!! :D

Je suis revenu à mon montage initial qui marche très bien (ou presque).

J'utilise d'un côté l'analyseur logique et de l'autre, dans le loop j'affiche les séquences A et B de l'encodeur rotatif chaque fois que la séquence A change.

 if(seqA != pSeqA) {
    Serial.println(seqA, BIN);
    Serial.println(seqB, BIN);  
    pSeqA = seqA;
  }

Et là, il y a un truc bizarre.

L'analyseur montre un signal normal sur un changement de pas de l'encodeur. Mais dans le moniteur série j'ai la séquence suivante :

(front descendant de A on ajoute 0 à A et 1 à B)
A : 1110
B : 1111

(front descendant de B on ajoute 0 à A et 0 à B)
A : 1100
B : 1110

(front montant de A on ajoute 1 à A et 0 à B)
A : 1001
B : 1100

(Et là c'est le drame, on devrait normalement 
avoir le front montant de B et ajouter 1 à A et 1 à B)
A : 111
B : 1

Z'auriez une idée ?

Bonne fin de week-end, Phaust

hello je pense que lorsque tu as le problème, le moniteur t'affiche A : 111 B : 1 et qu'il faut lire A : 0111 B : 0001 tu devrais avoir A : 0011 B : 1001

en partant de l'état précédent: (front montant de A on ajoute 1 à A et 0 à B) A : 1001 B : 1100

front montant de B on ajoute 1 à B et 1 à A A : 0011 B : 1001

histoire d’être sur que l'affichage du B est complet(qu'il ne reste rien dans le tuyau) rajoute le serial.flush dans ton affichage

if(seqA != pSeqA) { Serial.println(seqA, BIN); Serial.println(seqB, BIN); Serial.flush(); pSeqA = seqA; }

Bon, en mettant des compteurs dans mon code ça m'a permis de voir que mon système anti-rebond laisse passer des rebonds qui ne se voient pas sur mon petit analyseur logique.

J'ai modifié mon filtre RC en supprimant ma résistance et en mettant uniquement le condensateur en parallèle de l'encodeur rotatif (j'exploite uniquement la résistance de pullup de l'arduino).

J'ai modifié la détection du sens de rotation en branchant une seule patte de l'encodeur à une broche d'interruption de l'arduino et je me contente, sur un front descendant de A, de lire la valeur de B.

Cela simplifie le programme et ça filtre naturellement une partie des rebonds.

Maintenant j'ai un système qui marche vachement mieux. Il y a encore quelques ratés et je les analyserai quand je me serais trouvé un oscillo suffisant pour pas trop cher.

Merci à tous pour votre aide !

Phaust.