# Драйвер инкрементального энкодера DS_Encoder Универсальный драйвер для подсчета шагов и определения направления вращения инкрементального энкодера (например, популярного EC11). Поддерживает два независимых режима работы: **аппаратный** (через Encoder Mode таймера STM32) и **программный** (через ручной опрос пинов GPIO). ## 📌 Особенности реализации * **Двойная архитектура**: выбор между аппаратным декодированием таймером и программным сканированием пинов на лету. * **Аппаратное подавление дребезга**: при использовании таймера задействуются встроенные цифровые фильтры STM32. * **Квантование шага**: в аппаратном режиме учтена специфика энкодеров EC11 — счетчик автоматически делится на 4, возвращая ровно 1 шаг за один физический щелчок ручки. --- ## 🛠 Вариант 1: Аппаратный режим (Рекомендуемый) Данный режим использует периферию таймера микроконтроллера. Процессор не тратит ресурсы на опрос пинов. ### Настройка в STM32CubeMX: 1. Выберите таймер (например, `TIM1` или `TIM2`). 2. В поле **Combined Channels** установите значение **Encoder Mode**. 3. В параметрах таймера (`Parameter Settings`): * **Encoder Mode**: `Encoder Mode TI1 and TI2` (подсчет по обоим каналам для максимальной точности). * **Input Filter**: установите значение от `8` до `15` для аппаратного подавления дребезга контактов. 4. Настройте соответствующие пины `TIMx_CH1` и `TIMx_CH2` в режим `GPIO_AF`. ### Пример кода в `main.c`: ```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`: ```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)` Возвращает относительное изменение положения энкодера (количество шагов) с момента последнего вызова этой функции и **автоматически обнуляет** внутренний накопительный счетчик.