From 33b4dd450e5e0f31f746dd0cd9886528756211b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathis=20L=C3=A9crivain?= Date: Wed, 11 Sep 2024 11:25:44 +0200 Subject: [PATCH] lib: add encoder library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add encoder interface that provide method to reset and get traveled distance since last call. Add encoder implementation for RIOT qdec driver. Signed-off-by: Mathis Lécrivain --- lib/encoder/Makefile | 1 + lib/encoder/Makefile.dep | 2 + lib/encoder/Makefile.include | 2 + .../include/encoder/EncoderInterface.hpp | 71 +++++++++++++++ lib/encoder/include/encoder/EncoderQDEC.hpp | 86 +++++++++++++++++++ 5 files changed, 162 insertions(+) create mode 100644 lib/encoder/Makefile create mode 100644 lib/encoder/Makefile.dep create mode 100644 lib/encoder/Makefile.include create mode 100644 lib/encoder/include/encoder/EncoderInterface.hpp create mode 100644 lib/encoder/include/encoder/EncoderQDEC.hpp diff --git a/lib/encoder/Makefile b/lib/encoder/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/lib/encoder/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/lib/encoder/Makefile.dep b/lib/encoder/Makefile.dep new file mode 100644 index 0000000000..9a940162bc --- /dev/null +++ b/lib/encoder/Makefile.dep @@ -0,0 +1,2 @@ +# Board features required +FEATURES_REQUIRED += periph_qdec diff --git a/lib/encoder/Makefile.include b/lib/encoder/Makefile.include new file mode 100644 index 0000000000..cfc45503db --- /dev/null +++ b/lib/encoder/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES_encoder := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_encoder) diff --git a/lib/encoder/include/encoder/EncoderInterface.hpp b/lib/encoder/include/encoder/EncoderInterface.hpp new file mode 100644 index 0000000000..d9c15af87d --- /dev/null +++ b/lib/encoder/include/encoder/EncoderInterface.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include + +#include "etl/math_constants.h" + +namespace cogip { + +namespace encoder { + +enum class EncoderMode : uint8_t { + /// With X1 encoding, either the rising (aka leading) or the falling (aka following) edge of + /// channel A is counted. If channel A leads channel B, the rising edge is counted, and the + /// movement is forward, or clockwise. Conversely, if channel B leads channel A, the falling edge + /// is counted, and the movement is backwards, or counterclockwise. + ENCODER_MODE_X1 = 0, + + /// When X2 encoding is used, both the rising and falling edges of channel A are counted. This + /// doubles the number of pulses that are counted for each rotation or linear distance, which in + /// turn doubles the encoder’s resolution. + ENCODER_MODE_X2 = 1, + + /// X4 encoding goes one step further, to count both the rising and falling edges of both + /// channels A and B, which quadruples the number of pulses and increases resolution by four + /// times. + ENCODER_MODE_X4 = 2, +}; + +class EncoderInterface { +public: + /// + /// @brief Construct a new Encoder Interface object + /// @note Encoder pulse per revolution must be set according to chosen mode + /// eg. For a 2500 PPR encoder: + /// - ENCODER_MODE_X1 -> pulse_per_rev = 2500 + /// - ENCODER_MODE_X2 -> pulse_per_rev = 5000 + /// - ENCODER_MODE_X4 -> pulse_per_rev = 10000 + /// @param mode + /// @param pulse_per_rev + /// + EncoderInterface(EncoderMode mode, int32_t pulse_per_rev): mode_(mode), pulse_per_rev_(pulse_per_rev) {} + + /// + /// @brief Get the pulses counted by the encoder since the last call. + /// + /// @return int32_t int32_t counter value in ticks + /// + virtual int32_t read_and_reset() = 0; + + /// + /// @brief Reset encoder counter + /// + virtual void reset() = 0; + + /// + /// @brief Get the angle measured by the encoder since the last call. + /// + /// @return double traveled angle since last call (rad). + /// + double get_angle_and_reset() { return ((double)read_and_reset() / (double)pulse_per_rev_) * etl::math::pi; } + +protected: + const EncoderMode mode_; + const int32_t pulse_per_rev_; +}; + +} /// namespace encoder + +} /// namespace cogip + +/// @} diff --git a/lib/encoder/include/encoder/EncoderQDEC.hpp b/lib/encoder/include/encoder/EncoderQDEC.hpp new file mode 100644 index 0000000000..0bb4f3a8f1 --- /dev/null +++ b/lib/encoder/include/encoder/EncoderQDEC.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include "etl/math_constants.h" + +// RIOT includes +#include "periph/qdec.h" + +// Module includes +#include "EncoderInterface.hpp" +namespace cogip { + +namespace encoder { + +class EncoderQDEC : public EncoderInterface { +public: + + /// + /// @brief Construct a new Encoder object + /// @note Encoder pulse per revolution must be set according to chosen mode + /// eg. For a 2500 PPR encoder: + /// - ENCODER_MODE_X1 -> pulse_per_rev = 2500 + /// - ENCODER_MODE_X2 -> pulse_per_rev = 5000 + /// - ENCODER_MODE_X4 -> pulse_per_rev = 10000 + /// @param mode + /// @param pulse_per_rev + /// + explicit EncoderQDEC(uint8_t id, EncoderMode mode, int32_t pulse_per_rev): EncoderInterface(mode, pulse_per_rev), id_(id) {} + + /// + /// @brief Setup encoder + /// + /// @return int negative on error. 0 on succes + /// + int setup() + { + qdec_t qdec; + qdec_mode_t qdec_mode; + + qdec = QDEC_DEV(id_); + + switch (mode_) { + case EncoderMode::ENCODER_MODE_X1: + qdec_mode = QDEC_X1; + break; + case EncoderMode::ENCODER_MODE_X2: + qdec_mode = QDEC_X2; + break; + case EncoderMode::ENCODER_MODE_X4: + qdec_mode = QDEC_X4; + break; + default: + printf("Invalid QDEC mode (%u)\n", id_); + return -1; + } + + /* Setup QDEC peripheral */ + int error = qdec_init(qdec, qdec_mode, NULL, NULL); + if (error) { + printf("QDEC %u not initialized, error=%d !!!\n", id_, error); + } + + return error; + } + + /// + /// @brief Get the pulses counted by the encoder since the last call. + /// + /// @return int32_t counter value in ticks + /// + int32_t read_and_reset() override { return qdec_read_and_reset(id_); } + + /// + /// @brief Reset encoder counter + /// + /// + void reset() override { (void)read_and_reset(); }; + +private: + uint8_t id_; +}; + +} /// namespace encoder + +} /// namespace cogip + +/// @}