Rotary encoder in/decrement issue

A rotary encoder must increment a counter when turned clockwise; decrement a counter when turned counterclockwise.
Increments do work. Turning the rot. enc. counterclockwise decrements with one at first CCW step, the increments by one next CCW step, then decrement then increment etc...

How to get the decrements to work properly?

The serial monitor shows correct binary status of clock and data: when CW then clock = !data.
PD6 (clock) is either 0B0100000 or 0B00000000.
PD7 (data) is either 0B1000000 or 0B00000000

When CCW then clock = data.

Output serial monitor:

 while is running 
Counter: 1 clock : 1000000 data : 0
 while is running 
Counter: 2 clock : 0 data : 10000000
 while is running 
Counter: 3 clock : 1000000 data : 0
 while is running 
Counter: 4 clock : 0 data : 10000000
 while is running 
Counter: 5 clock : 1000000 data : 0
 while is running 
Counter: 6 clock : 0 data : 10000000
 while is running 
Counter: 7 clock : 1000000 data : 10000000
 while is running 
Counter: 6 clock : 0 data : 0
 while is running 
Counter: 7 clock : 1000000 data : 10000000
 while is running 
Counter: 6 clock : 0 data : 0
 while is running 
Counter: 7 clock : 1000000 data : 10000000

Code:

#define clk 6   //Clock pin connected to D6
#define data 7    //Data pin connected to D7
#define btn 5   //Push button pin connected to D5
volatile int counter;                   
volatile byte currentStateClock;              //Store the status of the clock pin (HIGH or LOW)
volatile byte stateData;                      //Store the status of the data pin (HIGH or LOW)
volatile byte lastStateClock;                 //Store the PREVIOUS status of the clock pin (HIGH or LOW)
uint16_t aState, aLastState; //GPIO #4-DT on encoder (Output B)
volatile byte once;

void setup() {
  Serial.begin(9600);
  delay(500);
  Serial.println("DCCpp_turntable_controller_v2");
  pinMode(clk, INPUT_PULLUP);
  pinMode(data, INPUT_PULLUP);
  aLastState = digitalRead(clk);

  //Here activate pin change interruptions on pin PD6 (and PD7) with PCINT22 (and PCINT23)
  PCICR |= (1 << PCIE2);  // set PCICR register bit 2 (PCIE2: PCINT[23:16], 0B00000100)
  PCMSK2 |= (1 << PCINT22); // pin 6 PCINT22, 0B01000000    clk
  // PCMSK2 |= (1 << PCINT23); // pin 7 PCINT23, 0B10000000    data
}

void loop()
{
  delay(1);
  runStepper(counter);
}

void runStepper(int ctr)
{
  while (once)
    // while (myStepper.isRunning())
  {
    Serial.println (" while is running ");
    delay(1);
    Serial.print("Counter: ");
    Serial.print(ctr);
    Serial.print(" clock : ");
    Serial.print(currentStateClock, BIN);
    Serial.print(" data : ");
    Serial.println(stateData, BIN);
    once = false;
  }
  // myStepper.disableOutputs();
}

ISR(PCINT2_vect) {
  // cli(); //We pause interrupts happening before we read pin values
  currentStateClock = (PIND & 0B01000000);       //Check pin D6 state? Clock
  stateData = (PIND & 0B10000000);              //Check pin D7 state? Data
  if (currentStateClock != lastStateClock)
  {
    // If "clock" state is different from "data" state, then encoder is rotating clockwise
    if (stateData != currentStateClock) {
      counter ++;                                //  increment
    }
    //Else, the encoder is rotating counter-clockwise
    else
    {
      counter --;                                 //  decrement
    }
    // sei(); //restart interrupts
  }
  lastStateClock = currentStateClock;         // Updates the previous state of the clock with the current state
  once = true;
}


A rotary encoder is best if you follow a state machine for decoding, like this:-

This is best implemented by using a library. Sadly there are many bad libraries around that do not use a state machine. However this is a good one.
http://www.pjrc.com/teensy/td_libs_Encoder.html

3 Likes

There is a state machine included in the code:

  currentStateClock = (PIND & 0B01000000);       //Check pin D6 state? Clock
  stateData = (PIND & 0B10000000);              //Check pin D7 state? Data
  if (currentStateClock != lastStateClock)
  {
    // If "clock" state is different from "data" state, then encoder is rotating clockwise
    if (stateData != currentStateClock) {
      counter ++;                                //  increment
    }
    //Else, the encoder is rotating counter-clockwise
    else
    {
      counter --;                                 //  decrement
    }
    // sei(); //restart interrupts
  }
  lastStateClock = currentStateClock;
  1. detect whether clk = !previousClk
  2. detect whether clk=!data (CW direction) then increment
  3. if clk=data (CCW direction) then decrement
  4. set previousClk=clk

Is your aim to write the code from scratch or would it be sufficient to use a well tested ready to use library?

another good one is this library. It uses not pin-change-interrupt it uses pin-interrupts

or a time-interrupt

I prefer to use no library: no overhead, and the challenge to get minimalistic code running.

Basically the code in my OP makes sense except that I cannot find for the time being the reason why decrementing does not yet work.

Solved it myself:

 currentStateClock = (PIND & 0B01000000);       //Check pin D6 state? Clock
  stateData = (PIND & 0B10000000);              //Check pin D7 state? Data
  if (currentStateClock != lastStateClock)

For decrementing to work currentStateClock must be equal to stateData .

Counterclockwise operation however results in PD6=clock=0B00000000 and PD7=data=0B00000000 (which indeed decreases the counter one unit...)

or...

PD6=0B01000000 and PD7=0B10000000 which does NOT decrement the counter (EDIT: but increases by one unit).

Solution:

  currentStateClock = (PIND & 0B01000000);       //Check pin D6 state? Clock
  stateData = (PIND & 0B10000000);              //Check pin D7 state? Data
  stateData = stateData >>1;
  if (currentStateClock != lastStateClock)
1 Like

I am surpised that, today on this forum, while a clear problem and symptom description and a bare-bones program are provided, I do get answers that show no interest in these and suggest -besides the point- to use libraries instead.
Maybe I should refrain from posting on Sundays in the future :smile:.

There are two reasons for this

  • lack of information that you want to not use a library
  • lack of information about your programming knowledge-level

not given information is replaced with assumptions:

  • got the code from somewhere not really understanding what it is doing

most users here have only very little experience and ask a detail-question.
This means the common reaction is: "use a library this will be easier for you".

Point taken.

Yet from my OP I thought it was clear that a detailed description and symptom analysis was given.

But yes, I assumed, and you assumed :wink:

We can only know what you tell us. Funny that.

What exactly do you think is the overhead in using a library? Unused functions are optimised out.

Yes and that is state 3 in the diagram I posted. But it seems you chose to ignore that then because it was not the answer you were expecting.

?? I do not understand this ??

Still, your answer was besides my point in OP. But granted, you were very fast with your answer, thank you for that Grumpy_Mike.

Not to bad for an old guy who had a stroke last Wednesday, and is now at home trying to do something with a right hand that will not cooperate with me. I am, or rather was, right handed.

Thanks for the appreciation.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.