Skip to content

File syn_foc_encoder.c

File List > motor > syn_foc_encoder.c

Go to the documentation of this file

#include "syn_foc_encoder.h"

#include <string.h>

static const q16_t g_hall_angle_lut[8] = {
    0,                                 /* 000 Invalid */
    (q16_t)Q16_FROM_FLOAT(5.2359877f), /* 001: 300 deg */
    (q16_t)Q16_FROM_FLOAT(2.0943951f), /* 010: 120 deg */
    (q16_t)Q16_FROM_FLOAT(3.6651914f), /* 011: 210 deg */
    (q16_t)Q16_FROM_FLOAT(0.5235987f), /* 100: 30 deg  */
    (q16_t)Q16_FROM_FLOAT(5.7595865f), /* 101: 330 deg */
    (q16_t)Q16_FROM_FLOAT(1.0471975f), /* 110: 60 deg  */
    0                                  /* 111 Invalid */
};

SYN_Status syn_foc_encoder_init(SYN_FOCEncoder *enc, const SYN_FOCEncoderConfig *cfg)
{
    if (!enc || !cfg || cfg->pole_pairs == 0) {
        return SYN_INVALID_PARAM;
    }
    memset(enc, 0, sizeof(*enc));
    enc->config = *cfg;
    return SYN_OK;
}

void syn_foc_encoder_update_quadrature(SYN_FOCEncoder *enc, int32_t raw_count)
{
    if (!enc || enc->config.cpr == 0)
        return;

    int32_t delta = raw_count - enc->prev_count;
    enc->count = raw_count;
    enc->prev_count = raw_count;

    /* Mechanical angle in Q16: (count % cpr) / cpr * 2*PI */
    int32_t cpr = (int32_t)enc->config.cpr;
    int32_t mod_count = raw_count % cpr;
    if (mod_count < 0)
        mod_count += cpr;

    q16_t mech_angle = q16_div(Q16_FROM_INT(mod_count), Q16_FROM_INT(cpr));
    mech_angle = q16_mul(mech_angle, Q16_2_PI);

    /* Electrical angle = (P * mech_angle + offset) % 2*PI */
    q16_t elec_raw = q16_mul(Q16_FROM_INT(enc->config.pole_pairs), mech_angle);
    elec_raw += enc->config.zero_offset_rad;

    while (elec_raw >= Q16_2_PI)
        elec_raw -= Q16_2_PI;
    while (elec_raw < 0)
        elec_raw += Q16_2_PI;

    enc->elec_angle_rad = elec_raw;

    /* Speed calculation: (delta_count / cpr) * P * 2*PI * sample_rate_hz */
    if (enc->config.sample_rate_hz > 0) {
        q16_t revs = q16_div(Q16_FROM_INT(delta), Q16_FROM_INT(cpr));
        q16_t mech_speed = q16_mul(revs, Q16_FROM_INT((int32_t)enc->config.sample_rate_hz));
        enc->elec_speed_rad_s =
            q16_mul(q16_mul(mech_speed, Q16_FROM_INT(enc->config.pole_pairs)), Q16_2_PI);
    }
}

void syn_foc_encoder_update_hall(SYN_FOCEncoder *enc, bool hall_u, bool hall_v, bool hall_w)
{
    if (!enc)
        return;
    uint8_t state = (uint8_t)((hall_u ? 4 : 0) | (hall_v ? 2 : 0) | (hall_w ? 1 : 0));
    enc->hall_state = state;
    if (state >= 1 && state <= 6) {
        q16_t angle = g_hall_angle_lut[state] + enc->config.zero_offset_rad;
        while (angle >= Q16_2_PI)
            angle -= Q16_2_PI;
        while (angle < 0)
            angle += Q16_2_PI;
        enc->elec_angle_rad = angle;
    }
}

void syn_foc_encoder_update_absolute(SYN_FOCEncoder *enc, uint16_t raw_angle_14bit)
{
    if (!enc)
        return;
    uint16_t angle_14 = raw_angle_14bit & 0x3FFFU;
    q16_t norm_angle = q16_div(Q16_FROM_INT((int32_t)angle_14), Q16_FROM_INT(16384));
    q16_t mech_angle = q16_mul(norm_angle, Q16_2_PI);

    q16_t elec_raw =
        q16_mul(Q16_FROM_INT(enc->config.pole_pairs), mech_angle) + enc->config.zero_offset_rad;
    while (elec_raw >= Q16_2_PI)
        elec_raw -= Q16_2_PI;
    while (elec_raw < 0)
        elec_raw += Q16_2_PI;

    enc->elec_angle_rad = elec_raw;
}

q16_t syn_foc_encoder_get_elec_angle(const SYN_FOCEncoder *enc)
{
    return enc ? enc->elec_angle_rad : 0;
}

q16_t syn_foc_encoder_get_elec_speed(const SYN_FOCEncoder *enc)
{
    return enc ? enc->elec_speed_rad_s : 0;
}