Files
2026-06-27 00:39:04 +03:00

5.7 KiB
Raw Permalink Blame History

Драйвер инкрементального энкодера 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:

#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)

Возвращает относительное изменение положения энкодера (количество шагов) с момента последнего вызова этой функции и автоматически обнуляет внутренний накопительный счетчик.