Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions include/rotary_decoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#pragma once
#include <Arduino.h>

static const int8_t ROTARY_DECODER_ENC_TABLE[16] = {
0, +1, -1, 0, -1, 0, 0, +1, +1, 0, 0, -1, 0, -1, +1, 0,
};

class RotaryDecoder {
public:
void begin(uint8_t pinA, uint8_t pinB, uint8_t stepsPerDetent = 2) {
_pinA = pinA;
_pinB = pinB;
_stepsPerDetent = stepsPerDetent;
bool a = digitalRead(_pinA);
bool b = digitalRead(_pinB);
_abState = (a << 1) | b;
_rawPosition = 0;
_position = 0;
}

void poll() {
bool a = digitalRead(_pinA);
bool b = digitalRead(_pinB);
uint8_t newAb = (a << 1) | b;
if (newAb == _abState) return;

int8_t delta = ROTARY_DECODER_ENC_TABLE[(_abState << 2) | newAb];
_abState = newAb;
if (delta == 0) return;

_rawPosition += delta;
_position = floorDiv(_rawPosition, (int32_t)_stepsPerDetent);
}

int32_t getPosition() { return _position; }

private:
static int32_t floorDiv(int32_t a, int32_t b) {
int32_t q = a / b;
if ((a % b != 0) && ((a < 0) != (b < 0))) q--;
return q;
}

uint8_t _pinA = 0;
uint8_t _pinB = 0;
uint8_t _stepsPerDetent = 2;
uint8_t _abState = 0;
int32_t _rawPosition = 0;
volatile int32_t _position = 0;
};
43 changes: 23 additions & 20 deletions src/hal/inputs/encoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,24 @@
#include "globals.h"

#include "core/powerSave.h"
// RotaryEncoder isn't in every board's lib_deps -- only boards that set
// HAS_ENCODER pull it in, so PlatformIO's LDF doesn't need to find it for
// everyone else.
#if defined(HAS_ENCODER)
#include <RotaryEncoder.h>
#include <rotary_decoder.h>

static RotaryEncoder *halEncoder = nullptr;
static RotaryDecoder halDecoder;
static TaskHandle_t halEncoderPollTaskHandle = nullptr;

static IRAM_ATTR void halEncoderTick() { halEncoder->tick(); }

static RotaryEncoder::LatchMode toLibMode(EncoderLatchMode mode) {
static int stepsPerDetent(EncoderLatchMode mode) {
switch (mode) {
case EncoderLatchMode::FOUR3: return RotaryEncoder::LatchMode::FOUR3;
case EncoderLatchMode::FOUR0: return RotaryEncoder::LatchMode::FOUR0;
default: return RotaryEncoder::LatchMode::TWO03;
case EncoderLatchMode::FOUR3:
case EncoderLatchMode::FOUR0: return 4;
default: return 2;
}
}

static void halEncoderPollTask(void *parameter) {
while (true) {
halDecoder.poll();
vTaskDelay(pdMS_TO_TICKS(4));
}
}

Expand All @@ -31,29 +34,29 @@ void hal_encoder_init(const DeviceEncoder &cfg, EncoderLatchMode mode) {
else pinMode(cfg.pin_esc, INPUT);
}

halEncoder = new RotaryEncoder(cfg.pin_a, cfg.pin_b, toLibMode(mode));
attachInterrupt(digitalPinToInterrupt(cfg.pin_a), halEncoderTick, CHANGE);
attachInterrupt(digitalPinToInterrupt(cfg.pin_b), halEncoderTick, CHANGE);
pinMode(cfg.pin_a, INPUT_PULLUP);
pinMode(cfg.pin_b, INPUT_PULLUP);
halDecoder.begin(cfg.pin_a, cfg.pin_b, stepsPerDetent(mode));

if (!halEncoderPollTaskHandle) {
xTaskCreate(halEncoderPollTask, "EncoderPoll", 2048, NULL, 3, &halEncoderPollTaskHandle);
}
}

void hal_encoder_poll(const DeviceEncoder &cfg) {
static unsigned long tm = 0;
static unsigned long tm2 = 0; // delay between an encoder step and Select (avoid missclick)
static unsigned long tm2 = 0;
static unsigned long lastMoveMs = 0;
static int posDifference = 0;
static long lastPos = 0;

long newPos = halEncoder->getPosition();
long newPos = halDecoder.getPosition();
if (newPos != lastPos) {
posDifference += (newPos - lastPos);
// Independent running total for consumers that apply the whole
// backlog in one pass (see drainRotarySteps() in globals.h). Never
// cleared by the stale-drop below -- it's drained exactly.
RotaryNetSteps += (newPos - lastPos);
lastPos = newPos;
lastMoveMs = millis();
} else if (posDifference != 0 && millis() - lastMoveMs > 30) {
// Drop any stale queued steps once the encoder has stopped moving.
posDifference = 0;
}

Expand Down
Loading