4

I'm trying to change my working rotary encoder code (stolen from here) to an interrupt-based code on my Mega2560. The trouble is that the result behaves very erratically, even though the physical setup is exactly the same as the working code. The determined encoder incrementation is jumping back and forth, when monotonically turning in one direction.

The interrupt-based code currently looks like this:

const long int baudrate = 115200;

const int debounceTime = 5; // time in ms, in which the interrupt is not reacting. Starting at the first trigger of the switch.

const int clk1Pin = 19; // clkpin (signal A) const int dt1Pin = 18; // dt pin (signal B) const int sw1Pin = 4; // switchbutton pin

volatile int encoder1Val = 1500; // what i want volatile int firstSwitchTime = 0; // helper variable for debouncing volatile int cnt = 0; // counters for debugging volatile int cnt2 = 0; volatile int cnt3 = 0; volatile int cnt4 = 0;

volatile bool switchlock = false; // flags for debouncing and readout flow control volatile bool readoutflag = false;

int cycletime;

void setup() {

// Set up pins pinMode(clk1Pin, INPUT_PULLUP); pinMode(dt1Pin, INPUT_PULLUP); pinMode(sw1Pin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(clk1Pin), encoder1Decoding, CHANGE);

Serial.begin(baudrate); }

void loop() {

// only [print the data] and [release encoder1Val incrementing lock] after the debouncing period has passed cycletime = millis() - firstSwitchTime; if (cycletime > debounceTime) { readoutflag = true; }

if (readoutflag == true && switchlock == true) { switchlock = false; readoutflag = false; Serial.print(encoder1Val); Serial.print(","); Serial.print(cnt4); // Total amount of interrupt calls between readouts Serial.print(","); Serial.print(cnt); // Amount of effective interrupts Serial.print(","); Serial.print(cnt2); // Amount of "increment branch" calls Serial.print(","); Serial.print(cnt3); // Amount of "decrement branch" calls Serial.println(); detachInterrupt(digitalPinToInterrupt(clk1Pin)); //ensure atomic action cnt = 0; cnt2 = 0; cnt3 = 0; cnt4 = 0; attachInterrupt(digitalPinToInterrupt(clk1Pin), encoder1Decoding, CHANGE); } }

void encoder1Decoding() { cnt4++; // if outside of debounceTime: change encoder1Val if (switchlock == false) { switchlock = true; firstSwitchTime = millis(); // remember the first time, the interrupt changed in a bouncing group cnt++; // .. decoding encoder .. if (digitalRead(clk1Pin) == digitalRead(dt1Pin)) { encoder1Val++; cnt2++; } else { encoder1Val--; cnt3++; } } }

It is relatively complicated for debugging reasons, as will become clear in a moment. When turning the encoder counter-clockwise, the resulting output is this:

Value, Total Interrupts, Effective Interrupts, Increment Branch, Decrement Branch:

1501,2,1,1,0
1500,33,2,0,2
1499,0,0,0,0
1500,1,1,1,0
1499,19,2,0,2
1498,24,0,0,0
1499,5,2,2,0
1500,0,0,0,0
1501,15,1,1,0
1500,16,2,1,1
1501,43,0,0,0
1500,3,1,0,1
1501,1,1,1,0
1502,6,1,1,0
1503,3,1,1,0
1504,2,1,1,0
1505,1,1,1,0

The values should increase monotonically. They don't, so what's happening?

  1. The first readout makes sense: One successful interrupt call, one increment branch run, one value increase.
  2. The second readout is already strange: two decrements, but only one less value?
  3. The third is completely inexplicable: a decrement without the interrupt even being called.

I'm completely puzzled. Anybody got an idea where to start?

What I tried so far:

  • Searching the web. Seems to be a new problem.
  • Using the encoder without interrupts ("polling"). Works flawlessly, but is speed limited.
  • Explicitly analyzed the bouncing period of this rotary encoder using interrupts. Worked flawlessly. Result is: BouncingTime is mostly less than 5ms (very few outliers).
  • Plotted the data coming from clk and dt (x-ticks are 1ms, they're overlapping):

enter image description here

There is one bounce visible in the beginning, but the rest seems quite alright.

As I'm completely at a loss, any help is much appreciated!

Edit 2023-07-25:

The most important question is, why the ISR keeps on decrementing encoder1Val, even though i only turn in positive direction. This happens in all variants of the code. An absolutely stripped down version with the same behaviour can be found below:

void loop() {
  Serial.println(encoder1Val);
  delay(500);
}

void encoder1Decoding() {

if (millis() - firstSwitchTime > debounceTime) { firstSwitchTime = millis();

if (digitalRead(clk1Pin) == digitalRead(dt1Pin)) {
  encoder1Val++;

} else {
  encoder1Val--;

}

} }

Result:

1500
1494
1497
1502
1513
1515
1518
1515
1516

Malibu
  • 41
  • 2
  • 2
    remove debounce code ... each bounce should produce a +1 followed by a -1 ... think about what happens if you ignore the +1 – jsotola Jul 21 '23 at 16:02
  • Your code seems to have lost some of the basic simplicity of the original (non-interrupt version) which you linked but I guess it has grown during your debugging efforts. You haven't said if you are using a raw encoder or one with debouncing/filter circuitry such as here: https://www.aliexpress.com/item/1005002845325794.html. The one in your linked article appears only to have pullup resistors but no capacitors. Your software debouncing is handling only bounces from clk1Pin not dt1Pin. – 6v6gt Jul 21 '23 at 17:12
  • @jsotola: I'm trying to prevent jitter in the output, as the signal will be transferred to servos. – Malibu Jul 21 '23 at 18:44
  • @6v6gt: It is a simple one without debouncing afaik. I only use the pullup resistors of the Arduino. Do you suggest, I should add some capacitors? Only clk1 is debounced, as it is the only one with the interrupt attached. I will expand the code once using clk1 works. – Malibu Jul 21 '23 at 18:51
  • 1
    @Malibu I'm trying to prevent jitter ... maybe something like this might work ... respond to A state change, then ignore any other A state changes until B changes ... basically, accept only the first clock pin transition for every data pin change – jsotola Jul 22 '23 at 17:12
  • 1
    @jsotola: Thank you for the suggestion. Sadly it seems not to improve the situation. I have also added an edit to the question, because the most pressing question is: why does the code decrement the values, even though the encoder is only turned in one direction. – Malibu Jul 25 '23 at 12:23
  • volatile int encoder1Val on a Mega is a 2 byte integer which you are updating in an ISR and reading in the loop() here Serial.println(encoder1Val);. This is a non-atomic operation and you could get unlucky in that the read operation is corrupted during a simultaneous interrupt. Temporarily suspend interrupts during which time you make a copy of encoder1Val then later print the copy. – 6v6gt Jul 26 '23 at 09:35
  • Ok, thanks! I tried detachInterrupt(..); valuesafe = encoder1Val; attachInterrupt(..); Serial.println(valuesafe). The behaviour didn't change, though. – Malibu Aug 01 '23 at 12:05

0 Answers0