Skip to content

Commit

Permalink
lib: add encoder library
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
mlecriva committed Oct 10, 2024
1 parent a745415 commit d9260cd
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/encoder/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base
2 changes: 2 additions & 0 deletions lib/encoder/Makefile.dep
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Board features required
FEATURES_REQUIRED += periph_qdec
2 changes: 2 additions & 0 deletions lib/encoder/Makefile.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
USEMODULE_INCLUDES_encoder := $(LAST_MAKEFILEDIR)/include
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_encoder)
97 changes: 97 additions & 0 deletions lib/encoder/include/Encoder.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#pragma once

#include "etl/math_constants.h"

// RIOT includes
#include "EncoderInterface.hpp"

// Module includes
#include "EncoderParams.hpp"
#include "periph/qdec.h"

namespace cogip {

namespace encoder {

class Encoder : public EncoderInterface {
public:
Encoder(EncoderParams &parameters) : _parameters(parameters) {}

int setup()
{
qdec_t qdec;
qdec_mode_t mode;

switch (_parameters.id()) {
case EncoderID::LEFT_ENCODER_ID:
qdec = QDEC_DEV(0);
break;
case EncoderID::RIGHT_ENCODER_ID:
qdec = QDEC_DEV(1);
break;
default:
printf("Invalid QDEC id (%u)\n", (uint8_t)_parameters.id());
return -1;
}

switch (_parameters.mode()) {
case EncoderMode::ENCODER_MODE_X1:
mode = QDEC_X1;
break;
case EncoderMode::ENCODER_MODE_X2:
mode = QDEC_X2;
break;
case EncoderMode::ENCODER_MODE_X4:
mode = QDEC_X4;
break;
default:
printf("Invalid QDEC mode (%u)\n", (uint8_t)_parameters.id());
return -1;
}

/* Setup qdec peripheral */
int error = qdec_init(qdec, mode, NULL, NULL);
if (error) {
printf("QDEC %u not initialized, error=%d !!!\n", (uint8_t)_parameters.id(), error);
}

return error;
}

/**
* @brief Get the distance measured by the encoder since the last call.
*
* @note This can become a speed in mm/Tech once periodically called.
*
* @return float distance in mm
*/
double get_traveled_distance()
{
int32_t counter = qdec_read_and_reset((qdec_t)_parameters.id());

return (double)counter / _parameters.pulse_per_rev() *
(etl::math::pi * _parameters.wheel_diameter());
}

/**
* @brief Get the encoder raw counter value
*
* @return double pulse since last call
*/
int32_t get_raw_counter() { return qdec_read_and_reset((qdec_t)_parameters.id()); }

/**
* @brief Reset encoder counter
*
*/
void reset() { qdec_read_and_reset((qdec_t)_parameters.id()); };

private:
EncoderParams &_parameters;
};

} // namespace encoder

} // namespace cogip

/// @}
29 changes: 29 additions & 0 deletions lib/encoder/include/EncoderInterface.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

namespace cogip {

namespace encoder {

class EncoderInterface {
public:
/**
* @brief Get the distance measured by the encoder since the last call.
*
* @note This can become a speed in mm/Tech once periodically called.
*
* @return float distance in mm
*/
virtual double get_traveled_distance() = 0;

/**
* @brief Reset encoder counter
*
*/
virtual void reset() = 0;
};

} // namespace encoder

} // namespace cogip

/// @}
101 changes: 101 additions & 0 deletions lib/encoder/include/EncoderParams.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#pragma once

namespace cogip {

namespace encoder {

enum class EncoderID : uint8_t {
LEFT_ENCODER_ID = 0,
RIGHT_ENCODER_ID = 1,
};

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 EncoderParams {
public:
/**
* @brief Construct a new Encoder Params 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 id Encoder ID
* @param mode Encoder mode
* @param wheel_diameter Wheel diameter in millimeters
* @param pulse_per_rev Encoder total pulses per revolution depending on mode choosen
*/
EncoderParams(EncoderID id, EncoderMode mode, double wheel_diameter, double pulse_per_rev)
: _id(id), _mode(mode), _wheel_diameter(wheel_diameter), _pulse_per_rev(pulse_per_rev) {};

/**
* @brief Return encoder ID
*
* @note possible values:
* - LEFT_ENCODER_ID
* - RIGHT_ENCODER_ID
*
* @return EncoderID
*/
EncoderID id() const { return _id; }

/**
* @brief Return encoder Mode
*
* @note possible values:
* - ENCODER_MODE_X1
* - ENCODER_MODE_X2
* - ENCODER_MODE_X4
*
* @return EncoderMode
*/
EncoderMode mode() const { return _mode; }

/**
* @brief Return encoder wheel diameter value in mm.
*
* @return double
*/
double wheel_diameter() const { return _wheel_diameter; }

/**
* @brief Return encoder pulse per revolution
*
* @return double
*/
double pulse_per_rev() const { return _pulse_per_rev; }

private:
EncoderID _id;
EncoderMode _mode;
double _wheel_diameter;
double _pulse_per_rev;
};

} // namespace encoder

} // namespace cogip

/// @}

0 comments on commit d9260cd

Please sign in to comment.