Files
STM32_General/Src/general_encoder.c
Razvalyaev d6cc99a25b Добавлены GPIO-энкодеры
Encoder_Init привязывает порты и пины A/B. Encoder_Update самостоятельно читает GPIO, обрабатывает направление и дребезг и возвращает готовые шаги. Число переходов на детент задаётся при инициализации, экземпляры независимы.

Добавлены только general_encoder.h и general_encoder.c в стиле существующих модулей. Готовые файлы, документация и структура библиотеки сохранены.
2026-10-09 02:32:31 +03:00

86 lines
3.6 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
**************************************************************************
* @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;
}