/** ************************************************************************** * @file general_encoder.c * @brief Модуль для работы с квадратурными энкодерами через GPIO. ************************************************************************** * @details Инициализация входов энкодера и получение готовых шагов вращения. *************************************************************************/ #include "general_encoder.h" /** * @brief Прочитать фазы через HAL в едином формате A/B. * @details Порты могут различаться; A занимает старший бит снимка. */ static uint8_t read_phases(const Encoder_HandleTypeDef *encoder) { return (uint8_t)(((HAL_GPIO_ReadPin(encoder->port_a, encoder->pin_a) == GPIO_PIN_SET) << 1U) | (HAL_GPIO_ReadPin(encoder->port_b, encoder->pin_b) == GPIO_PIN_SET)); } /** * @brief Подготовить независимый экземпляр энкодера к опросу. * @details Входы предварительно настраивает CubeMX или вызывающий код. * Начальный снимок предотвращает событие при включении питания. * Число переходов задаётся механикой конкретного энкодера. * @return HAL_OK при успехе, HAL_ERROR при неверной конфигурации. */ HAL_StatusTypeDef Encoder_Init(Encoder_HandleTypeDef *encoder, GPIO_TypeDef *port_a, uint16_t pin_a, GPIO_TypeDef *port_b, uint16_t pin_b, unsigned steps_per_detent) { if (!encoder || !port_a || !port_b || !pin_a || !pin_b || !steps_per_detent || steps_per_detent > 4U) { return HAL_ERROR; } encoder->port_a = port_a; encoder->port_b = port_b; encoder->pin_a = pin_a; encoder->pin_b = pin_b; encoder->state = read_phases(encoder); encoder->steps_per_detent = (uint8_t)steps_per_detent; encoder->partial_steps = 0; encoder->invalid_transitions = 0U; return HAL_OK; } /** * @brief Превратить последовательность фаз в законченный шаг вращения. * @details Обратные переходы компенсируют дребезг. Одновременная смена обеих * фаз обрывает незавершённый детент и учитывается как пропуск опроса. * Функция сама читает оба GPIO; вызывающий код задаёт частоту опроса. */ int Encoder_Update(Encoder_HandleTypeDef *encoder) { static const int8_t direction[16] = {0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0}; uint8_t previous, state; if (!encoder || !encoder->port_a || !encoder->port_b || !encoder->pin_a || !encoder->pin_b || !encoder->steps_per_detent) { return 0; } state = read_phases(encoder); previous = encoder->state; encoder->state = state; if ((previous ^ state) == 3U) { ++encoder->invalid_transitions; encoder->partial_steps = 0; return 0; } encoder->partial_steps += direction[previous * 4U + state]; if (encoder->partial_steps >= (int)encoder->steps_per_detent) { encoder->partial_steps -= (int8_t)encoder->steps_per_detent; return 1; } if (encoder->partial_steps <= -(int)encoder->steps_per_detent) { encoder->partial_steps += (int8_t)encoder->steps_per_detent; return -1; } return 0; }