Добавлены GPIO-энкодеры
Encoder_Init привязывает порты и пины A/B. Encoder_Update самостоятельно читает GPIO, обрабатывает направление и дребезг и возвращает готовые шаги. Число переходов на детент задаётся при инициализации, экземпляры независимы. Добавлены только general_encoder.h и general_encoder.c в стиле существующих модулей. Готовые файлы, документация и структура библиотеки сохранены.
This commit is contained in:
39
Inc/general_encoder.h
Normal file
39
Inc/general_encoder.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @file general_encoder.h
|
||||
* @brief Заголовочный файл для работы с квадратурными энкодерами через GPIO.
|
||||
**************************************************************************
|
||||
* @defgroup GENERAL_ENCODER Encoder Tools
|
||||
* @ingroup STM32_GENERAL
|
||||
* @brief Опрос GPIO и обработка шагов механического энкодера.
|
||||
* @details
|
||||
Порты и пины задаются при инициализации. Обработчик самостоятельно читает
|
||||
фазы A/B, учитывает направление, дребезг и число переходов на детент.
|
||||
*************************************************************************/
|
||||
#ifndef __ENCODER_GENERAL_H_
|
||||
#define __ENCODER_GENERAL_H_
|
||||
|
||||
#include "mylibs_config.h"
|
||||
|
||||
/** Структура энкодера; каждый экземпляр имеет собственное состояние. */
|
||||
typedef struct
|
||||
{
|
||||
GPIO_TypeDef *port_a;
|
||||
GPIO_TypeDef *port_b;
|
||||
uint16_t pin_a;
|
||||
uint16_t pin_b;
|
||||
uint8_t state;
|
||||
uint8_t steps_per_detent;
|
||||
int8_t partial_steps;
|
||||
uint32_t invalid_transitions;
|
||||
} Encoder_HandleTypeDef;
|
||||
|
||||
/** Привязать настроенные GPIO A/B; steps_per_detent — от 1 до 4. */
|
||||
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);
|
||||
/** Самостоятельно опросить GPIO, обработать фазы и вернуть -1, 0 или +1 детент. */
|
||||
int Encoder_Update(Encoder_HandleTypeDef *encoder);
|
||||
|
||||
#endif
|
||||
85
Src/general_encoder.c
Normal file
85
Src/general_encoder.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @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;
|
||||
}
|
||||
Reference in New Issue
Block a user