Skip to content

Commit 25a1bc8

Browse files
committed
Improve rotary encoder signal processing
Tested with setupRotaryEncoderWithInterrupt and setupStateMachineRotaryEncoder.
1 parent 9781a35 commit 25a1bc8

File tree

2 files changed

+95
-42
lines changed

2 files changed

+95
-42
lines changed

src/SwitchInput.cpp

Lines changed: 94 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -426,64 +426,117 @@ int AbstractHwRotaryEncoder::amountFromChange(unsigned long change) {
426426
}
427427

428428
void HardwareRotaryEncoder::encoderChanged() {
429-
bool lastSyncStatus = switches.getIoAbstraction()->sync();
430-
bitWrite(flags, LAST_SYNC_STATUS, lastSyncStatus);
429+
static uint8_t state = 0; // Current state of the encoder
430+
static uint8_t pulseCounter = 0; // Pulse counter for FULL_CYCLE and HALF_CYCLE modes
431+
432+
// Read the current states of pins A and B
433+
uint8_t a = digitalRead(pinA);
434+
uint8_t b = digitalRead(pinB);
435+
436+
/**
437+
* Calculate the new state from signals A and B.
438+
*
439+
* Signal A and B form a quadrature signal pattern like this:
440+
*
441+
* Signal A: __|¯¯|__|¯¯|__ (HIGH/LOW alternating)
442+
* Signal B: _|¯¯|__|¯¯|__|_ (90 degrees phase-shifted from A)
443+
*
444+
* Each combination of A and B represents one of four states:
445+
* - State 0: A=0, B=0 --> Binary: 00
446+
* - State 1: A=1, B=0 --> Binary: 10
447+
* - State 2: A=1, B=1 --> Binary: 11
448+
* - State 3: A=0, B=1 --> Binary: 01
449+
*
450+
* Transitions between these states determine the direction of rotation:
451+
* - Clockwise (CW): 0 -> 1 -> 3 -> 2 -> 0
452+
* - Counterclockwise (CCW): 0 -> 2 -> 3 -> 1 -> 0
453+
*
454+
* The new state is calculated by combining the values of signals A and B:
455+
* - newState = (A << 1) | B
456+
*/
457+
uint8_t newState = (a << 1) | b;
458+
459+
// Determine rotation direction (CW or CCW) based on state transitions
460+
bool directionUp = false;
461+
if ((state == 0 && newState == 1) ||
462+
(state == 1 && newState == 3) ||
463+
(state == 3 && newState == 2) ||
464+
(state == 2 && newState == 0)) {
465+
directionUp = true;
466+
}
467+
else if ((state == 0 && newState == 2) ||
468+
(state == 2 && newState == 3) ||
469+
(state == 3 && newState == 1) ||
470+
(state == 1 && newState == 0)) {
471+
directionUp = false;
472+
}
473+
else {
474+
// Invalid transition, return early
475+
return;
476+
}
431477

432-
uint8_t a = switches.getIoAbstraction()->digitalRead(pinA);
433-
uint8_t b = switches.getIoAbstraction()->digitalRead(pinB);
434-
435-
if(encoderType == QUARTER_CYCLE){
436-
if((a != aLast) || (b != cleanFromB)) {
437-
aLast = a;
438-
if((a != aLast) || (b != cleanFromB)) {
439-
cleanFromB = b;
440-
if((a || cleanFromB) || (a == 0 && b == 0)) {
441-
handleChangeRaw(a && b);
442-
}
443-
}
444-
}
445-
}
446-
else {
447-
if(a != aLast) {
448-
aLast = a;
449-
if(b != cleanFromB) {
450-
cleanFromB = b;
451-
if(a) {
452-
handleChangeRaw(b);
453-
}
454-
}
455-
}
456-
}
478+
// Logic for different modes
479+
pulseCounter++;
480+
bool validTransition = false;
481+
switch (encoderType) {
482+
case FULL_CYCLE:
483+
if (pulseCounter >= 4) { // Count 4 transitions for one cycle
484+
validTransition = true;
485+
pulseCounter = 0;
486+
}
487+
break;
488+
case HALF_CYCLE:
489+
if (pulseCounter >= 2) { // Count 2 transitions for one step
490+
validTransition = true;
491+
pulseCounter = 0;
492+
}
493+
break;
494+
case QUARTER_CYCLE:
495+
validTransition = true; // Every transition is valid
496+
pulseCounter = 0;
497+
break;
498+
}
499+
500+
// If the transition is valid, call handleChangeRaw
501+
if (validTransition) {
502+
handleChangeRaw(directionUp);
503+
}
504+
505+
// Update the current state
506+
state = newState;
457507
}
458508

509+
459510
void HardwareRotaryEncoder::initialise(pinid_t pinA, pinid_t pinB, HWAccelerationMode accelerationMode, EncoderType et) {
460511
this->aLast = switches.getIoAbstraction()->digitalRead(pinA);
461512
this->cleanFromB = switches.getIoAbstraction()->digitalRead(pinB);
462513
initialiseBase(pinA, pinB, accelerationMode, et);
463514
}
464515

465516
void AbstractHwRotaryEncoder::handleChangeRaw(bool increase) {
466-
// was the last direction up?
467-
bool lastDirectionUp = bitRead(flags, LAST_ENCODER_DIRECTION_UP);
517+
// Calculate the time delta in microseconds
518+
unsigned long currentMicros = micros();
519+
unsigned long deltaMicros = currentMicros - lastChange;
520+
521+
// Ignore all pulses below the reject threshold (debounce logic)
522+
if (deltaMicros < REJECT_DIRECTION_CHANGE_THRESHOLD) {
523+
return;
524+
}
468525

469-
// get the amount of change and direction. Also keep a copy of the time until later for acceleration purposes
470-
unsigned long timeNow = micros();
471-
unsigned long deltaMillis = timeNow - lastChange;
472-
int amt = amountFromChange(deltaMillis);
526+
// Get the amount of change based on the time delta
527+
int amount = amountFromChange(deltaMicros);
473528

474-
// update the last change time now to ensure always set
475-
lastChange = timeNow;
529+
// Update the last change time
530+
lastChange = currentMicros;
476531

477-
// direction changes within the reject change threshold would not result in an encoder change, part of the debounce
478-
// logic to prevent spurious updates. Within this period direction must be the both now and previously.
479-
//serlogF4(SER_DEBUG, "delta ", deltaMillis, increase, lastDirectionUp);
480-
if(deltaMillis < REJECT_DIRECTION_CHANGE_THRESHOLD && increase != lastDirectionUp) return;
532+
// Apply the increment or decrement based on the direction
533+
increment((int8_t)(increase ? amount : -amount));
481534

482-
// now we make the change and register the last change direction (as we accepted it)
483-
increment((int8_t) (increase ? amt : -amt));
535+
// Update the last direction in the flags
484536
bitWrite(flags, LAST_ENCODER_DIRECTION_UP, increase);
485537
}
486538

539+
487540
EncoderUpDownButtons::EncoderUpDownButtons(pinid_t pinUp, pinid_t pinDown, EncoderCallbackFn callback, uint8_t speed)
488541
: RotaryEncoder(callback), upPin(pinUp), downPin(pinDown), backPin(-1), nextPin(-1), passThroughListener(nullptr),
489542
canRotate(false) {

src/SwitchInput.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
* quick to change direction in 1/10th of a second, but it is configurable in case.
6262
*/
6363
#ifndef REJECT_DIRECTION_CHANGE_THRESHOLD
64-
#define REJECT_DIRECTION_CHANGE_THRESHOLD 100000
64+
#define REJECT_DIRECTION_CHANGE_THRESHOLD 10000
6565
#endif //REJECT_DIRECTION_CHANGE_THRESHOLD
6666

6767
// END user adjustable section

0 commit comments

Comments
 (0)