From e00184195720bc2887ae8f90e6a2db6ff63b529f Mon Sep 17 00:00:00 2001 From: audiobird Date: Fri, 11 Apr 2025 17:02:43 -0700 Subject: [PATCH 1/4] Add quadrature encoder decoder --- util/quadrature_encoder.hh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 util/quadrature_encoder.hh diff --git a/util/quadrature_encoder.hh b/util/quadrature_encoder.hh new file mode 100644 index 0000000..7fb2e9f --- /dev/null +++ b/util/quadrature_encoder.hh @@ -0,0 +1,28 @@ +#pragma once + +#include + +template +class QuadratureEncoder { + static constexpr uint8_t valid_ccw = 0b10000111 ^ invert ? 0xff : 0x00; + static constexpr uint8_t valid_cw = 0b01001011 ^ invert ? 0xff : 0x00; + + uint8_t prev_state{}; + +public: + enum class State { Undefined, Right, Left }; + + State update(bool state_a, bool state_b) { + const auto cur_state = state_a | (state_b << 1u); + prev_state <<= 2; + prev_state |= cur_state; + switch (prev_state) { + case valid_cw: + return State::Right; + case valid_ccw: + return State::Left; + default: + return State::Undefined; + } + } +}; From a5cc30d9c48d4af495c8d4ae6ae546fd591d3710 Mon Sep 17 00:00:00 2001 From: audiobird Date: Fri, 11 Apr 2025 18:03:26 -0700 Subject: [PATCH 2/4] Fix xor --- util/quadrature_encoder.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/quadrature_encoder.hh b/util/quadrature_encoder.hh index 7fb2e9f..26d2643 100644 --- a/util/quadrature_encoder.hh +++ b/util/quadrature_encoder.hh @@ -4,8 +4,8 @@ template class QuadratureEncoder { - static constexpr uint8_t valid_ccw = 0b10000111 ^ invert ? 0xff : 0x00; - static constexpr uint8_t valid_cw = 0b01001011 ^ invert ? 0xff : 0x00; + static constexpr uint8_t valid_ccw = 0b10000111 ^ (invert ? 0xff : 0x00); + static constexpr uint8_t valid_cw = 0b01001011 ^ (invert ? 0xff : 0x00); uint8_t prev_state{}; From 3566d1c4f6216e9e5125128e4c0c5e943ca60afa Mon Sep 17 00:00:00 2001 From: Dan Green Date: Tue, 15 Apr 2025 10:21:21 -0700 Subject: [PATCH 3/4] Refactor to check for changed pins, and return enum with signed int values. --- util/quadrature_encoder.hh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/util/quadrature_encoder.hh b/util/quadrature_encoder.hh index 26d2643..fc28be0 100644 --- a/util/quadrature_encoder.hh +++ b/util/quadrature_encoder.hh @@ -10,19 +10,24 @@ class QuadratureEncoder { uint8_t prev_state{}; public: - enum class State { Undefined, Right, Left }; + enum Direction { None = 0, CW = 1, CCW = -1 }; - State update(bool state_a, bool state_b) { + Direction get_motion(bool state_a, bool state_b) { const auto cur_state = state_a | (state_b << 1u); + + if ((prev_state & 0b11) == cur_state) + return None; + prev_state <<= 2; prev_state |= cur_state; + switch (prev_state) { case valid_cw: - return State::Right; + return CW; case valid_ccw: - return State::Left; + return CCW; default: - return State::Undefined; + return None; } } }; From d82a4d8cc9e83f06e31592541a7bddb995fdfdc8 Mon Sep 17 00:00:00 2001 From: audiobird Date: Tue, 15 Apr 2025 11:00:28 -0700 Subject: [PATCH 4/4] Add quadrature encoder test --- tests/quadrature_encoder_tests.cc | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/quadrature_encoder_tests.cc diff --git a/tests/quadrature_encoder_tests.cc b/tests/quadrature_encoder_tests.cc new file mode 100644 index 0000000..097e1a6 --- /dev/null +++ b/tests/quadrature_encoder_tests.cc @@ -0,0 +1,55 @@ +#include "doctest.h" +#include "util/quadrature_encoder.hh" + +TEST_CASE("Quadrature Encoder") { + + using Enc = QuadratureEncoder<>; + + Enc encoder{}; + + // check clockwise + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(true, true) == Enc::CW); + + // check clockwise no state change + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(true, false) == Enc::None); + + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(false, true) == Enc::None); + + CHECK(encoder.get_motion(true, true) == Enc::CW); + CHECK(encoder.get_motion(true, true) == Enc::None); + CHECK(encoder.get_motion(true, true) == Enc::None); + CHECK(encoder.get_motion(true, true) == Enc::None); + + // check counter-clockwise + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(true, true) == Enc::CCW); + + // check with no state change + CHECK(encoder.get_motion(false, true) == Enc::None); + CHECK(encoder.get_motion(false, true) == Enc::None); + + CHECK(encoder.get_motion(false, false) == Enc::None); + CHECK(encoder.get_motion(false, false) == Enc::None); + + CHECK(encoder.get_motion(true, false) == Enc::None); + CHECK(encoder.get_motion(true, false) == Enc::None); + + CHECK(encoder.get_motion(true, true) == Enc::CCW); + CHECK(encoder.get_motion(true, true) == Enc::None); +}