Missing data using rotatory encoder (interrupts).

Hi

I’m missing time after time data from my arduino and rotatory encoder. Just one line at the time and only when rotatory encoder spins slower. I’m using interrupts. Don’t know is my code wrong (can it be made better)?
There is code:

/*
-* This sketch uses interrupts
*/
long count=0;
long countPrev=0;
unsigned long timep, time, etime;
boolean A,B;
byte state, statep;

void setup()
{
Serial.begin(128000);
pinMode(2, INPUT); //connect Channel A to pin 2
pinMode(3, INPUT); //connect Channel B to pin 3
attachInterrupt(0,Achange,CHANGE);
attachInterrupt(1,Bchange,CHANGE);
timep = micros(); //set the initial time
//read the initial value of A & B
A = digitalRead(2);
B = digitalRead(3);
//set initial state value
if ((A==HIGH)&&(B==HIGH)) statep = 1;
if ((A==HIGH)&&(B==LOW)) statep = 2;
if ((A==LOW)&&(B==LOW)) statep = 3;
if ((A==LOW)&&(B==HIGH)) statep = 4;
}

void loop()
{
time = micros();
etime = time - timep;
if (etime > 33000)
{
if (countPrev != count)
{
Serial.print ("DATA, TIMER , ");
Serial.println(count);

timep = time;
countPrev = count;
}
}
}
void Achange()
{
A = digitalRead(2);
B = digitalRead(3);
//determine state value
if ((A==HIGH)&&(B==HIGH)) state = 1;
if ((A==HIGH)&&(B==LOW)) state = 2;
if ((A==LOW)&&(B==LOW)) state = 3;
if ((A==LOW)&&(B==HIGH)) state = 4;
switch (state)
{
case 1:
{
if (statep == 2) count–;
if (statep == 4) count++;
break;
}
case 2:
{
if (statep == 1) count++;
if (statep == 3) count–;
break;
}
case 3:
{
if (statep == 2) count++;
if (statep == 4) count–;
break;
}
default:
{
if (statep == 1) count–;
if (statep == 3) count++;
}
}
statep = state;
}
void Bchange()
{
A = digitalRead(2);
B = digitalRead(3);
//determine state value
if ((A==HIGH)&&(B==HIGH)) state = 1;
if ((A==HIGH)&&(B==LOW)) state = 2;
if ((A==LOW)&&(B==LOW)) state = 3;
if ((A==LOW)&&(B==HIGH)) state = 4;
switch (state)
{
case 1:
{
if (statep == 2) count–;
if (statep == 4) count++;
break;
}
case 2:
{
if (statep == 1) count++;
if (statep == 3) count–;
break;
}
case 3:
{
if (statep == 2) count++;
if (statep == 4) count–;
break;
}
default:
{
if (statep == 1) count–;
if (statep == 3) count++;
}
}
statep = state;
}

I attached screen chot of missing data (you can see its printing tim instead of normal time. Sometimes its printing error or just blank line and so on. Don’t know whats wrong. Is my serial comunication (usb and paralax excel) failing or ??

Anyone know how to fix this?
Should i simplify code or upgrade board ??

code.ino (2.26 KB)

Be careful with single letter names for variables, especially with capital letters. Many of those are already defined in the core. Shadowing them can cause undefined issues.

There are much simpler ways to read an encoder. I'm not quite sure what the statep variable is doing but it isn't needed. In each interrupt handler read both pins. In one if they are equal you're going one way and if they aren't you're going the other way. The other interrupt handler will be the opposite. Look at an example of gray code and you'll see what I mean.

The other thing is to be careful with digitalRead in there. Others have had success, but every time I try to use it I end up missing some steps. digitalRead is slow and sometimes the pulses are too fast. Direct port reads are better IMHO for encoder stuff.

Here's an example of how I handle encoders on an UNO.

// Rotary Encoders Done Proper
// Using interrupts and direct port reads for maximum speed and resolution

volatile int8_t counter = 0;

void setup(void){
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);  
  attachInterrupt(0, encoderPinA_ISR, CHANGE);
  attachInterrupt(1, encoderPinB_ISR, CHANGE);
  
  // Let us know what program we're in
  Serial.println("Starting Encoder Example");
}


void loop(void){
  int8_t thisCount = readEncoder();
  if(thisCount != 0){
    Serial.print("The encoder turned ");
    Serial.print(abs(thisCount));
    Serial.print(" clicks in the ");
    Serial.print(thisCount > 0 ? "clockwise" : "counter-clockwise");
    Serial.println(" direction");    
  }  
  else {
    Serial.println("Encoder is in the same place it was last time you read it");
  }
  delay(1000);  // Spin your wheels for a second and pretend you're a program that does something
}


uint8_t readEncoder(){  
  //  Hold interrupts so we can't miss a beat between these two statements
  cli();
  int8_t thisCount = counter;
  counter = 0;
  sei();
  return thisCount;
}


// NOTE:  This is hardcoded for the UNO and other 328P based devices
//  Rewrite this part for use on other boards or rewrite to use
//  some macros to figure out which board you have.
void encoderPinA_ISR(){
  uint8_t portVal = PIND;
   if(((portVal >> 1) & 0x04) == (portVal & 0x04)){
     counter++;
   }
   else {
     counter--;
   }
}

// The opposite pin has to be setup the opposite direction
// Look at the waveform to see why
// Which one is clockwise or counterclockwise all depends on
// how you wire up your encoder.  If it goes backwards of what
// you want then just reverse the wires or the logic in these
// two functions. 
void encoderPinB_ISR(){
  uint8_t portVal = PIND;
   if(((portVal >> 1) & 0x04) == (portVal & 0x04)){
     counter--;
   }
   else {
     counter++;
   }
}

The output you attached does not come from the code you posted.

raimiss8:
/*
-* This sketch uses interrupts
*/

And your interrupt programming skills are a mess (or non existing).

When doing interrupt programming these should be the first two things you will have to learn:

  1. Use 'volatile' variable declaration with variables that are used in "normal code" as well as in "interrupt code"

  2. When reading volatile (changed in interrrupt handling) multi-byte variables (i.e. int or long variables) in normal code, you have to disable interrupts for a very short time while reading the variable, or you might get strange results from time to time.

Fix your code to meet these two requirements!

128000 is a strange baudrate:
Serial.begin(128000);

Are you sure that the PC program you are using supports this baudrate correctly?

Why don't you test your code with a standard baudrate and the serial monitor first?

Delta-G
Statep is previous state. So it is to see if any changes (that what i think, I will try your version). Basically what I'm doing is similar devise as Tendo unit. I'm attaching wire to barbell. When I'm lifting barbell It will pull wire and spin rotatory encoder then arduino will send click count (+ or - ) to paralax (excel) it will print time and count (+ or - ) then I can count my barbell speed and distance, Power generated and so on.

Thanks for replay :slight_smile:

cattledog:
The output you attached does not come from the code you posted.

Could you please explain what you mean.
I have attached pin 2 and 3 as in my code is written. Did I missing something?

jurs:
And your interrupt programming skills are a mess (or non existing).

When doing interrupt programming these should be the first two things you will have to learn:

  1. Use 'volatile' variable declaration with variables that are used in "normal code" as well as in "interrupt code"

  2. When reading volatile (changed in interrrupt handling) multi-byte variables (i.e. int or long variables) in normal code, you have to disable interrupts for a very short time while reading the variable, or you might get strange results from time to time.

Fix your code to meet these two requirements!

128000 is a strange baudrate:
Serial.begin(128000);

Are you sure that the PC program you are using supports this baudrate correctly?

Why don't you test your code with a standard baudrate and the serial monitor first?

I'm using Paralax Excel program to log my data from arduino and Paralax port suports this rate I need this rate to work. Is another like 56000 baud rate could use this instead but tough higher rate will give better result (more accurate and less missing date). I could be wrong (I'm still new to all this) :slight_smile: