Interrupts and delay to read a rotary encoder

Hi,

I am trying to read a rotary encoder. I am using the code in the attachment. I figured out that there is a problem when I use the delay() function. When the interrupts are trigged the board is no longer doing a delay in the main loop. Is there a programming error in my code?

Moreover, I can not read my encoder with this code. And I can not see the error. I am using the arduino 101.
Thanks in advance for an answer.

Greetings,
Amine

test101.ino (2.02 KB)

aothmane: Hi,

I am trying to read a rotary encoder. I am using the code in the attachment. I figured out that there is a problem when I use the delay() function. When the interrupts are trigged the board is no longer doing a delay in the main loop. Is there a programming error in my code?

Moreover, I can not read my encoder with this code. And I can not see the error. I am using the arduino 101. Thanks in advance for an answer.

Greetings, Amine

I rewrote your example, See if this works.

 #define encoder0PinA 12
 #define encoder0PinB 13

volatile unsigned int encoder0Pos = 0;
// volatile unsigned int tmp = 0; not needed here
volatile bool phaseA=false; // last value high/low for pin A
volatile bool phaseB=false; // last value high/low for pin B
void setup() {

  pinMode(encoder0PinA, INPUT);
  phaseA=digitalRead(encoder0PinA); // init current A
  pinMode(encoder0PinB, INPUT);
 phaseB=digitalRead(encoder0PinB); // init current B
 // digitalWrite(encoder0PinA,1);
//  digitalWrite(encoder0PinB,1); 
// encoder pin on interrupt 0 (pin 2)

  attachInterrupt(encoder0PinA, doEncoderA, CHANGE);

// encoder pin on interrupt 1 (pin 3)

  attachInterrupt(encoder0PinB, doEncoderB, CHANGE);  

  Serial.begin (9600);

}


void loop(){ 
static unsigned int tmp=0; // I like variable to be local if possible
//Check each changes in position
  if ((tmp != encoder0Pos)&&(Serial.availableForWrite()>60)){
 // serial output buffer is almost empty,
    // and value has changed, so send it
    Serial.println(encoder0Pos, DEC);
    tmp = encoder0Pos;
  }
//  delay(500); don't like delay
}


void doEncoderA(){
phaseA = digitalRead(encoder0PinA); // only read it once!
  // look for a low-to-high on channel A
//  if (digitalRead(encoder0PinA) == HIGH) { 
  if(phaseA){ // is high
    // check channel B to see which way encoder is turning
//    if (digitalRead(encoder0PinB) == LOW) {  
   if(!phaseB){ // is low, use last value of phaseB
      encoder0Pos = encoder0Pos + 1;         // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }

  else   // must be a high-to-low edge on channel A            
  { 
    // check channel B to see which way encoder is turning  
//    if (digitalRead(encoder0PinB) == HIGH) {   
    if(phaseB){
      encoder0Pos = encoder0Pos + 1;          // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
//  Serial.println (encoder0Pos, DEC); too much data
  if(phaseA)Serial.write('A'); // went high
  else Serial.write('a'); // when Low
  // use for debugging - remember to comment out
}


void doEncoderB(){
phaseB=digitalRead(encoder0PinB);
  // look for a low-to-high on channel B
  if (phaseB) {   
   // check channel A to see which way encoder is turning
    if (phaseA) {  
      encoder0Pos = encoder0Pos + 1;         // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }

  // Look for a high-to-low on channel B

  else { 
    // check channel B to see which way encoder is turning  
    if (!phaseA) {   
      encoder0Pos = encoder0Pos + 1;          // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
  if(phaseB)Serial.write('B'); // went high
  else Serial.write('b'); // when Low

}

Chuck.

Hi, thanks for the answer. It is still not working. When I move the encoder in one direction he is counting upwards and downwards. I need to spend more time on it and figure out what is happening.

Amine

aothmane: Hi, thanks for the answer. It is still not working. When I move the encoder in one direction he is counting upwards and downwards. I need to spend more time on it and figure out what is happening.

Amine

What does the Serial monitor output look like?

You should be seeing clockwise: ABabABabAB

Counter clockwise: BAbaBAbaBA

You should never see AA, Aa, aA, aa or BB, Bb, bB, bb. If you see any of those sequences a pulse has been missed.

Capture the Serial monitor output and post it.

Chuck.

You might try my interrupt-less code here.

I have no idea how fast you can spin the encoder before it loses pulses though.

chucktodd: What does the Serial monitor output look like?

You should be seeing clockwise: ABabABabAB

Counter clockwise: BAbaBAbaBA

You should never see AA, Aa, aA, aa or BB, Bb, bB, bb. If you see any of those sequences a pulse has been missed.

Capture the Serial monitor output and post it.

Chuck.

Hi,

I have the problem that the output is sometimes AAAAA... or just aaa... or BBB.. or bbbbb... That ist what i don't really understand. It does not make any sens to me. I will look deeper into it.

JimboZA: You might try my interrupt-less code here.

I have no idea how fast you can spin the encoder before it loses pulses though.

Hi, Thanks for the Code! I will try it. But I definitively have a high pulse frequency. And I have several other thing going on i the project so I prefer to read the encoder using interrupts.

Well there will definitely be a rotation speed and code complexity at which my approach falls over, but you never know, it might be fast enough....

Hi, I figured out that the problem is not in the code! I am using a txb0108 bi-directional level shifter. The Arduino 101 inputs and outputs are 3.3V. However on the webpage is written 5V tolerant I/O ? I though It would be better to use a level shifter to shift the encoder voltage and the voltage to a motor driver that need both 5V. And the problem is with the level shifter. I tried to connect the encoder directly and it worked fine! What does "5V tolerant I/O" mean? can i just connect the encoder and motor drivers without a level shifter?

aothmane: Hi, I figured out that the problem is not in the code! I am using a txb0108 bi-directional level shifter. The Arduino 101 inputs and outputs are 3.3V. However on the webpage is written 5V tolerant I/O ? I though It would be better to use a level shifter to shift the encoder voltage and the voltage to a motor driver that need both 5V. And the problem is with the level shifter. I tried to connect the encoder directly and it worked fine! What does "5V tolerant I/O" mean? can i just connect the encoder and motor drivers without a level shifter?

Please post a schematic of your failed interface circuit. I am interested in understanding why it failed.

I use TXB0104's to levelshift between MEGA's and 3.3v SPI devices(MISO,MOSI,SCK). I have not had any problem. The TXB010x's cannot drive a large capacitance load, I think they are specified at 70pf max.

Does your encoder use pullups, or is it's output TTL?

Chuck.

chucktodd: Please post a schematic of your failed interface circuit. I am interested in understanding why it failed.

I use TXB0104's to levelshift between MEGA's and 3.3v SPI devices(MISO,MOSI,SCK). I have not had any problem. The TXB010x's cannot drive a large capacitance load, I think they are specified at 70pf max.

Does your encoder use pullups, or is it's output TTL?

Chuck.

Hi,

i don't have a schematic of the circuit. But it's actually very simple: I am using the txb0108 on the following board: https://www.adafruit.com/products/395 . On one side I have the encoder connected and the connection to a DC motor driver. On the other side is connected the arduino 101. The 3.3V and 5V inputs are connected to the arduino. The OE is connected to the 3.3V though a pulldown resistor. The encoder output is with Push-Pull (with inverted signal).

aothmane: i don't have a schematic of the circuit.

It's fine for our purposes to hand draw one and post a phone camera pic

JimboZA:
It’s fine for our purposes to hand draw one and post a phone camera pic

Hi,

here is the schematic of the circuit. I am using the txb0108 on the following board: https://www.adafruit.com/products/395 . So there is already the pull down resistor. The motor drivers and encoder 2 are not connected now. There is just the Encoder 1 that is connected.

Schematic in line to save others downloading it

1b0f1090ba8fa90132982f26094bfcd3d08648f7.jpg

aothmane: Hi,

i don't have a schematic of the circuit. But it's actually very simple: I am using the txb0108 on the following board: https://www.adafruit.com/products/395 . On one side I have the encoder connected and the connection to a DC motor driver. On the other side is connected the arduino 101. The 3.3V and 5V inputs are connected to the arduino. The OE is connected to the 3.3V though a pulldown resistor. The encoder output is with Push-Pull (with inverted signal).

Did you enable the Arduino's internal pullup resistors?

pinMode(inputpin,INPUTPULLUP);
//or
pinMode(inputpin,INPUT);
digitalWrite(inputpin,HIGH);

The TXB01xx are really touchy about driving any kind of load. The have really weak drivers. To support bi-directional connections, they weakly drive the signal then monitor the output. If the output is forced, they reverse direction and drive the other end to match the 'new' input, if this new 'output' is also forced, it oscillates back and forth.

They are spec'd to handle >50k pullups, Arduino's internal pullups are ~50k, sometimes lower, sometimes higher. If the pullups were enabled, that could be the source of your problem.

Chuck.

chucktodd: Did you enable the Arduino's internal pullup resistors?

pinMode(inputpin,INPUTPULLUP);
//or
pinMode(inputpin,INPUT);
digitalWrite(inputpin,HIGH);

The TXB01xx are really touchy about driving any kind of load. The have really weak drivers. To support bi-directional connections, they weakly drive the signal then monitor the output. If the output is forced, they reverse direction and drive the other end to match the 'new' input, if this new 'output' is also forced, it oscillates back and forth.

They are spec'd to handle >50k pullups, Arduino's internal pullups are ~50k, sometimes lower, sometimes higher. If the pullups were enabled, that could be the source of your problem.

Chuck.

Hi,

the pullups are already disabled. So that can not be the source of the problem...

aothmane: Hi,

the pullups are already disabled. So that can not be the source of the problem...

I hope you can figure out what was happening, I reviewed the spec's from TI, with your circuit, there should not have been any problem. :confused:

Except, Did you have a bypass cap on the TXB? at least 100nf 0.1uf? I did not see one on your schematic. It could have been as simple as the switch current causing a glitch.

Chuck.

Hi,

on the board are already two caps on both direction. However, there are some long cable between the encoer/llevelshifter/arduino 101. This could be the problem I think. I don't have an oscilloscope to check the signals :disappointed_relieved: