@@ -426,64 +426,117 @@ int AbstractHwRotaryEncoder::amountFromChange(unsigned long change) {
426
426
}
427
427
428
428
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
+ }
431
477
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;
457
507
}
458
508
509
+
459
510
void HardwareRotaryEncoder::initialise (pinid_t pinA, pinid_t pinB, HWAccelerationMode accelerationMode, EncoderType et) {
460
511
this ->aLast = switches.getIoAbstraction ()->digitalRead (pinA);
461
512
this ->cleanFromB = switches.getIoAbstraction ()->digitalRead (pinB);
462
513
initialiseBase (pinA, pinB, accelerationMode, et);
463
514
}
464
515
465
516
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
+ }
468
525
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);
473
528
474
- // update the last change time now to ensure always set
475
- lastChange = timeNow ;
529
+ // Update the last change time
530
+ lastChange = currentMicros ;
476
531
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));
481
534
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
484
536
bitWrite (flags, LAST_ENCODER_DIRECTION_UP, increase);
485
537
}
486
538
539
+
487
540
EncoderUpDownButtons::EncoderUpDownButtons (pinid_t pinUp, pinid_t pinDown, EncoderCallbackFn callback, uint8_t speed)
488
541
: RotaryEncoder(callback), upPin(pinUp), downPin(pinDown), backPin(-1 ), nextPin(-1 ), passThroughListener(nullptr ),
489
542
canRotate(false ) {
0 commit comments