5.7 KiB
Драйвер инкрементального энкодера DS_Encoder
Универсальный драйвер для подсчета шагов и определения направления вращения инкрементального энкодера (например, популярного EC11). Поддерживает два независимых режима работы: аппаратный (через Encoder Mode таймера STM32) и программный (через ручной опрос пинов GPIO).
📌 Особенности реализации
- Двойная архитектура: выбор между аппаратным декодированием таймером и программным сканированием пинов на лету.
- Аппаратное подавление дребезга: при использовании таймера задействуются встроенные цифровые фильтры STM32.
- Квантование шага: в аппаратном режиме учтена специфика энкодеров EC11 — счетчик автоматически делится на 4, возвращая ровно 1 шаг за один физический щелчок ручки.
🛠 Вариант 1: Аппаратный режим (Рекомендуемый)
Данный режим использует периферию таймера микроконтроллера. Процессор не тратит ресурсы на опрос пинов.
Настройка в STM32CubeMX:
- Выберите таймер (например,
TIM1илиTIM2). - В поле Combined Channels установите значение Encoder Mode.
- В параметрах таймера (
Parameter Settings):- Encoder Mode:
Encoder Mode TI1 and TI2(подсчет по обоим каналам для максимальной точности). - Input Filter: установите значение от
8до15для аппаратного подавления дребезга контактов.
- Encoder Mode:
- Настройте соответствующие пины
TIMx_CH1иTIMx_CH2в режимGPIO_AF.
Пример кода в main.c:
#include "Encoder.h"
extern TIM_HandleTypeDef htim1;
DS_Encoder volume_encoder;
int main(void) {
// ... Инициализация HAL, GPIO, TIM ...
/* Инициализируем энкодер в режиме таймера */
DS_EncoderInitTim(&volume_encoder, &htim1);
while (1) {
/* Получаем изменение положения с момента предыдущего опроса */
int8_t diff = DS_EncoderGetState(&volume_encoder);
if (diff != 0) {
// Ручку повернули! diff может быть равен +1, -1, +2 и т.д.
// Применяем изменение, например: volume += diff;
}
HAL_Delay(50); // Опрос можно делать с любой редкой периодичностью
}
}
🕹 Вариант 2: Программный режим (Polling)
Используется, если на плате нет свободных каналов таймера с поддержкой Encoder Mode. Пин 1 и Пин 2 настраиваются как обычные GPIO_Input с подтяжкой Pull-up.
Пример кода в main.c:
#include "Encoder.h"
DS_Encoder manual_encoder;
int main(void) {
// ... Инициализация ...
/* Инициализация структуры программными пинами */
DS_EncoderInit(&manual_encoder, GPIOB, GPIO_PIN_4, GPIOB, GPIO_PIN_5);
while (1) {
/* ВАЖНО: Функция обновления состояния должна вызываться как можно чаще
(в основном цикле без больших задержек или в быстром таймере) */
DS_EncoderUpdate(&manual_encoder);
/* Проверяем, накопились ли шаги вращения */
int8_t steps = DS_EncoderGetState(&manual_encoder);
if (steps != 0) {
// Обработка программного поворота ручки
}
}
}
📋 Справочник API функций
void DS_EncoderInitTim(DS_Encoder *Encoder, TIM_HandleTypeDef *hTim)
Инициализирует и запускает таймер в режиме аппаратного декодера энкодера.
hTim: Указатель на структуру таймера HAL (например,&htim1).
void DS_EncoderInit(DS_Encoder *Encoder, GPIO_TypeDef *PortEncSignal1, uint16_t PinEncSignal1, GPIO_TypeDef *PortEncSignal2, uint16_t PinEncSignal2)
Конфигурирует структуру для программного отслеживания уровней на указанных GPIO портах и пинах.
void DS_EncoderUpdate(DS_Encoder *Encoder)
Конечный автомат программного парсинга квадратурного сигнала. Опрашивает физическое состояние пинов. Требует высокой частоты вызова.
int8_t DS_EncoderGetState(DS_Encoder *Encoder)
Возвращает относительное изменение положения энкодера (количество шагов) с момента последнего вызова этой функции и автоматически обнуляет внутренний накопительный счетчик.