Go Down

Topic: Rotary Encoder KY40 with Interrupt (Read 2016 times) previous topic - next topic

zhomeslice

this is not true:


the shifting works like this:

_prevValueAB 00000001 <<2 changes to 00000100

see official manual
ok then when you read again 
_prevValueAB 00000100 <<2 changes to 00010010
then again:
_prevValueAB 00010010 <<2 changes to 01001011
How does your "switch case" handle the subsequent shifts in the uint8_t (byte)
z
HC

enjoyneering

#16
Jun 19, 2019, 05:03 pm Last Edit: Jun 19, 2019, 05:13 pm by enjoyneering
this is how my code works:

prevAB=0b00001000

read A = 1 = 0b00000001

A << 1 = 0b00000010

read B = 1 = 0b00000001

currAB = A | B = 0b00000011

switch(prevAB | currAB ) = 0b00001011 //the result is not assigned to prevAB or currAB, it is a third temporary variable in the memory

prevAB = currAB << 2 = 0b00000011 << 2 = 0b00001100

zhomeslice

this is how my code works:

prevAB=0b00001000

read A = 1 = 0b00000001

A << 1 = 0b00000010

read B = 1 = 0b00000001

currAB = A | B = 0b00000011

switch(prevAB | currAB ) = 0b00001011 //the result is not assigned to prevAB or currAB, it is a third temporary variable in the memory

prevAB = currAB << 2 = 0b00000011 << 2 = 0b00001100
I see my error in reading the code I interpreted. this line as prevAB |= currAB << 2 = 0b00000011 << 2 = 0b00001100
I don't know what I was thinking lol


Z
HC

zhomeslice

you are doing it wrong:
- The encoder uses the Manchester code. You must read A & B at the same time, otherwise you will have wrong counts due to noise, debounce...
- pins and ports are hardcoded and can only be used with atmega328 / 168


The only reason you get a decent result with your code is because the atmega328 & arduino framework is so slow and cannot catch the noise yet. :) But the encoder is aging & debounce will increase....

Try your code on fast Cortex MCU, for example, STM32F103xxxx or ESP8266 - you will be surprised. Ohh but you can't - pins and ports are hardcoded
So Heres an example using both ways. I believe the additional 2 Step and 4 Step will work also but I haven't tested them with your method yet. I'm currently working on another project.
I also switched to digitalRead to provide flexibility for the faster ESP8266.
My thoughts are this. 
when using interrupts with RISING we already know the prior state of the pin being interrupted LOW and it just transitioned to HIGH. The other pin Is not transitioning and so it is stable no bounce If they are == then we are moving forward if they are != then we are moving backward.  

I'm still trying to understand how your code differs from my code in final results.
Here is the test program I made. Will this meet the needs to prove/disprove my thery.
Code: [Select]
#define ClockPin 2 // Must be pin 2
#define DataPin 3 // Must be pin 3
//#define readA bitRead(PIND,2)//faster than digitalRead()  (((value) >> (bit)) & 0x01)
//#define readB bitRead(PIND,3)//faster than digitalRead()  (((value) >> (bit)) & 0x01)

#define readA digitalRead(ClockPin)// Not Hard Coded
#define readB digitalRead(DataPin)// Not Hard Coded

volatile long count1 = 0;
volatile long count2 = 0;
volatile byte PreviousAB;

// Note digitalPinToInterrupt(PIN#) Should always be High (RISING) with the interrupt for 1 Setp
// We will read both pins
void Encoder(bool A,bool B) {
  // @enjoyneering Manchester code way
  byte CurrentAB;
  CurrentAB = A << 1 | B;
  switch ((PreviousAB | CurrentAB))
  {
    case 0b0001: case 0b1110:
      count1++;
      break;
    case 0b0100: case 0b1011:
      count1--;
      break;
  }
  PreviousAB = CurrentAB << 2;
  // @ZHomeslice Other Way.
  (A == B)  ? count2++ : count2--;
}

void setup() {
  Serial.begin(115200); //115200
  pinMode(ClockPin, INPUT);
  pinMode(DataPin, INPUT);
  PreviousAB = (readB << 1 | readB) << 2; // initialize PreviousAB state
  /* 1 Step */
  attachInterrupt(digitalPinToInterrupt(ClockPin), [] {Encoder( readA,readB);}, RISING);
  /**/

  /*  2 step count
    attachInterrupt(digitalPinToInterrupt(ClockPin), [] {Encoder( readA,readB);}, CHANGE);
  */

  /* Full 4 Step Count
    attachInterrupt(digitalPinToInterrupt(ClockPin), [] {Encoder( readA,readB);}, CHANGE);
    attachInterrupt(digitalPinToInterrupt(DataPin ), [] {Encoder( !readA,readB);}, CHANGE);
  */
}

void loop() {
  static long lastCtr1;
  static long lastCtr2;
  long Counter1;
  long Counter2;
  noInterrupts ();
  Counter1 = count1;
  Counter2 = count2;
  interrupts ();
  if ((lastCtr1 != Counter1) || (lastCtr2 != Counter2)) {
    Serial.print("Counter 1 =");
    Serial.print(Counter1);
    Serial.print("Counter 2 =");
    Serial.println(Counter2);
    SpareCycles = 0;
    lastCtr1 = Counter1;
    lastCtr2 = Counter2;
   
  }
 
}


Z
HC

enjoyneering

#19
Jun 19, 2019, 10:05 pm Last Edit: Jun 19, 2019, 11:27 pm by enjoyneering
did you read attachInterrupt()?

- the ISR function must take no parameters & return nothing


Quote
when using interrupts with RISING
There are two common methods to read the encoder - internal (attached to the pin) interrupt or interrupt by the timer. With your assumptions you won't be able to use second method (see ticker example for esp8266 and TimerOne for AVR).

zhomeslice

#20
Jun 19, 2019, 10:53 pm Last Edit: Jun 19, 2019, 10:59 pm by zhomeslice
did you read attachInterrupt()?

- the ISR function must take no parameters & return nothing


There are two common methods to read the encoder - internal (attached to the pin) interrupt or interrupt by the timer. With your assumptions you want be able to use second method (see ticker example for esp8266 and TimerOne for AVR).
Yes
[  ]  { Serial.print("Hello world") ; }
This is an lambda anonymous function that takes no parameters . I'm passing it to the  interrupt call back. Inside the lambda function acts like any other function where globals are accessible.
Try it out.
HC

enjoyneering

#21
Jun 19, 2019, 11:26 pm Last Edit: Jun 19, 2019, 11:28 pm by enjoyneering
Ooo. Never use it. I've learned something new today. Thank you zhomeslice.

zhomeslice

Ooo. Never use it. I've learned something new today. Thank you zhomeslice.
Welcome  :)
HC

Go Up