Добрый день.
На предыдущем занятии мы обрабатывали событие поворота вала энкодера программно.
Но во многих микроконтроллерах STM32 есть специальный блок, основанный на таймере, который умеет обрабатывать эти события, устранять дребезг контактов, и выдавать нам уже готовый результат куда и на сколько повернулся энкодер. При чем он делает это на много быстрее, чем мы могли позволить себе при программной реализации. Плюсом к этому - при обработке не задействуется ядро микроконтроллера, что может быть очень полезно в нагруженных задачах.
Но в этой бочке мёда есть большая такая ложка дёгтя - аппаратный режим работы с энкодером доступен не на всех выводах.
Перед началом более глубокого погружения - обозначим задачу на сегодняшнее занятие.
Она звучит практически так же, как и на предыдущее занятие, но с небольшим дополнением.
К микроконтроллеру подключен энкодер, и индикатор на основе MAX7219. Необходимо при повороте энкодера по часовой стрелке увеличивать значение выводимое на индикатор, а при повороте против часовой - уменьшать. Чтение состояния энкодера необходимо осуществить при помощи таймера.
Ну и раз у нас задача практически повторяет то, что было на предыдущем занятии, то к нему мы и вернёмся.
Давайте запустим CubeMX, и в нём посмотрим куда же нам можно подключить энкодер, что бы работать с ним при помощи таймера.
Откроем первый таймер, и в режиме комбинированных каналов найдем Encoder Mode. Активируем его. Как видим - активировались выводы BP0 и PB3.
Запомним эту информацию, и отключим обратно режим энкодера. Хорошо, теперь заглянем в таймер2. Там так же попытаемся активировать Encoder mode. Но тут оно подсвечено красным. И во всплывающем окне написано какие именно выводы конфликтуют. Это выводы PA6 и PA7 - отлично, именно туда у нас и подключен был энкодер на предыдущем занятии!
Для того, что бы всё таки воспользоваться этими выводами - сбросим их состояние, после чего - активируем режим энкодера на втором таймере.
Теперь необходимо настроить сам таймер, что бы корректно работать в этом режиме.
Многие из параметров нам уже знакомы, по этому не буду на них подробно останавливаться.
Prescaler - Делитель частоты. В режиме энкодера он должен быть установлен в ноль. Что CubeMX нам и сделал.
Counter Mode - здесь всё таки необходимо сделать небольшую вставку. Когда таймер работает в режиме энкодера - он делает вид, что работает в режиме счетчика внешних импульсов. Вот небольшой пример, как это выглядит в документации.
Таким образом, нам необходимо настроить направление счета. Что и задается в этом параметре.
Выберем счет в верх.
Counter Period - до какого значения считать. Оставим 65565.
Internal Clock Division - Если внимательно присмотреться к примеру из документации, то видно что на 1 оборот энкодера счетчик изменится на 4. Таким образом нам необходим делитель 4.
auto-reload Preload - Мы не планируем менять Counter Period, по этому оставим отключенным.
Далее пара пунктов, которые появились в новом микроконтроллере. Они касаются триггерного входа, по этому пока что их не будем трогать.
Ну и наконец то настройки режима энкодера.
Encoder Mode - За какой линией энкодера будет следить таймер. Т1 - за первой линией, Т2 - за второй. Т1 и Т2 - за обоими. Выберем последний вариант.
Polarity - Восходящий или нисходящий сигнал будет являться триггером для этого канала. Оставим восходящий, т.к. у нас энкодер коммутирует положительный вывод источника питания.
IC Selection - у нас тут выбора нет, по этому не будем даже смотреть что это такое.
Prescaler Division Ratio - Делитель входящего сигнала. нам он не требуется, по этому оставим без деления.
Input Filter - это и есть как раз аппаратный фильтр борьбы с дребезгом. Не будем его пока то активировать.
Аналогичный набор параметров имеется так же и для второго канала. И там мы менять ничего не будем, так же, как и в первом.
После настройки таймера - необходимо включить подтяжку на выводах, подключенных к энкодеру. В нашем случае - к земле.
На этом настройка завершена, и можно перегенерировать проект.
После генерации идём в VS Code, и в первую очередь возвращаем удалённые записи из CMakeList.
Далее - удаляем из main.c функцию инициализации энкодера, и его обновления.
В заголовочном файле создадим новый прототип функции инициализации EncoderInitTim, у которой будет 2 параметра, энкодер и таймер.
Так же в структуру добавим новую переменную для хранения таймера.
Вроде как этого должно быть достаточно.
Приступаем к описанию функции инициализации.
Сохраним указатель на таймер в структуру, инициализируем счетчик, после чего - запустим таймер.
В функции инициализации, написанной на предыдущем занятии - так же инициализируем указатель на таймер, но нулём.
Осталось немножко дописать функцию возвращающую состояние энкодера.
Если таймер не равен нулю, тогда получим чему равен счетчик этого таймера, вычтем из него предыдущее состояние счетчика, сохраним новое состояние, после чего - вернём эту дельту вызывающей программе, иначе оставим предыдущий код.
Давайте проверять что получилось.
В main.c удалим инициализацию для программной работы с энкодером, и инициализируем его для работы с таймером.
Так же из бесконечного цикла уберём вызов обновления, так как этим теперь занимается сам таймер.
Больше менять то и нечего. Запускаем программу, и проверяем получившийся результат.
Необычные ощущения, вроде бы и работает, но как то странно.
На каждый "щелчок" энкодера значение на индикаторе увеличивается на четыре. При этом если крутить очень медленно, то можно видеть промежуточные состояние, отражающее все переходные процессы внутри одного шага.
К сожалению я так и не смог понять то ли это баг, то ли так и задумано, но решение смог найти только одно - делить полученное от счетчика значение на 4. Почему не работает делитель, который мы настраивали в CubeMX - так же не понятно. Хорошо, давайте поделим, и посмотрим что получится в этом случае. После такой доработки - всё заработало как и требовалось, но доля непонимания осталась. Но на этом на сегодня мы закончим, спасибо за внимание.