Skip to content

File encoder.cpp

File List > controls > sae_2025_ws > src > payload > src > encoder.cpp

Go to the documentation of this file

#include "payload/encoder.hpp"

#include <cmath>

// Quadrature decode lookup table (4x).
// Index = (prev_ab << 2) | curr_ab  where ab = (A<<1)|B
// +1 = forward, -1 = reverse, 0 = no change or error
static const int8_t QEM[16] = {
     0, -1, +1,  0,
    +1,  0,  0, -1,
    -1,  0,  0, +1,
     0, +1, -1,  0,
};

QuadratureEncoder::QuadratureEncoder(int pi, int pin_a, int pin_b, int cpr, MotorType motor_type)
: pi_(pi), pin_a_(pin_a), pin_b_(pin_b), cpr_(cpr), motor_type_(motor_type)
{
    set_mode(pi_, pin_a_, PI_INPUT);
    set_mode(pi_, pin_b_, PI_INPUT);

    int a = gpio_read(pi_, pin_a_);
    int b = gpio_read(pi_, pin_b_);
    prev_ab_ = (a << 1) | b;

    cbid_a_ = callback_ex(pi_, pin_a_, EITHER_EDGE, alert_cb, this);
    cbid_b_ = callback_ex(pi_, pin_b_, EITHER_EDGE, alert_cb, this);
}

QuadratureEncoder::~QuadratureEncoder()
{
    if (cbid_a_ >= 0) callback_cancel(cbid_a_);
    if (cbid_b_ >= 0) callback_cancel(cbid_b_);
}

void QuadratureEncoder::alert_cb(int /*pi*/, unsigned gpio, unsigned level, uint32_t /*tick*/, void* userdata)
{
    auto* self = static_cast<QuadratureEncoder*>(userdata);
    if (!self) return;
    self->on_edge(gpio, level);
}

void QuadratureEncoder::on_edge(unsigned gpio, unsigned level)
{
    int a = (gpio == static_cast<unsigned>(pin_a_)) ? static_cast<int>(level) : gpio_read(pi_, pin_a_);
    int b = (gpio == static_cast<unsigned>(pin_b_)) ? static_cast<int>(level) : gpio_read(pi_, pin_b_);
    int curr_ab = (a << 1) | b;

    int8_t step = QEM[(prev_ab_ << 2) | curr_ab];
    if (motor_type_ == MotorType::LEFT) step = -step;
    count_.fetch_add(step, std::memory_order_relaxed);
    prev_ab_ = curr_ab;
}

int64_t QuadratureEncoder::count() const
{
    return count_.load(std::memory_order_relaxed);
}

void QuadratureEncoder::reset()
{
    count_.store(0, std::memory_order_relaxed);
}

float QuadratureEncoder::angle_deg() const
{
    return static_cast<float>(count()) * 360.0f / static_cast<float>(cpr_);
}

float QuadratureEncoder::angle_rad() const
{
    return static_cast<float>(count()) * (2.0f * static_cast<float>(M_PI)) / static_cast<float>(cpr_);
}