Добрый день.
Сегодня будем знакомиться с принципом работы и способом подключения инкрементального энкодера.
Для начала - немного теории.
Энкодер - это электро-механическое устройство, преобразующее угол поворота в электрический сигнал.
При повороте ручки по часовой стрелке - сигнал сначала появляется на первой линии, а затем, с небольшой задержкой - на второй. При повороте против часовой - в первую очередь сигнал появляется сначала на второй линии, и только затем на первой.
Давайте рассмотрим это более подробно:
В начальный период времени - состояние на обоих линиях высокое. При повороте по часовой стрелки - сначала зелёная линия подтягивается к нулю, фиолетовая при этом остаётся в единице. Затем фиолетовая линия так же подтягивается к земле. После чего - зелёная линия возвращается к единице. Спустя еще какое то время, на фиолетовой линии так же появляется сигнал. После чего цикл повторяется.
При повороте же против часовой стрелки, последовательность меняется. Теперь первой к земле притягивается фиолетовая линия, и только потом - зелёная. Анализируя полученные диаграммы, можно сделать вывод, что без вращения энкодера невозможно определить в каком положении в произвольный период времени находится вал энкодера. Для решения этой проблемы - существуют абсолютные энкодеры. Но про них как ни будь в другой раз.
Намного более информативно - смотреть на наложение графиков. Как мы видим, можно не анализировать все стадии поворота, а достаточно анализировать какая линия была первая притянута к минусу питания. И как только все линии вернутся в исходное состояние - можно считать что шаг завершился. На основании того, какая линия была притянута первой - засчитываем шаг в данном направлении.
Так как мы уже разобрались в принципах работы, и устройства инкрементантальных энкодеров, можно подключать его к микроконтроллеру, и использовать в будущих проектах.
На сегодняшнее занятие поставим себе следующую задачу:
К микроконтроллеру подключен энкодер и индикатор на основе MAX7219. При вращении энкодера по часовой стрелке, необходимо увеличивать число на индикаторе, при вращении против часовой - уменьшать. При нажатии кнопки - сбрасывать число в ноль.
Исходя из поставленной задачи, была собрана данная схема. В качестве микроконтроллера использован STM32G030F6P6.
Ну и как обычно, для тестирования работы нового блока - создаём новый репозиторий. Клонируем его в VS Code. Запускаем Cube MX, и начинаем настраивать. Сначала - включим отладку. Далее - активируем SPI, и настроим на работу с 16-битным размером данных. В дополнение к SPI активируем PA3 на выход, и дадим ему имя CS. С индикатором закончили, переходим непосредственно к энкодеру. Его кнопка - подключена к выводу PA5, и при нажатии - соединяет этот вывод с плюсом источника питания. А при вращении - происходит коммутация выводов PA6 и PA7 так же с плюсом источника питания. Сделаем подтяжку этих выводов к земле. Вот собственно и всё. задаём имя проекту, указываем правильный тулчейн, и генерируем проект. После генерации - импорт проекта, и можно приступать к попытке оживить наше творение.
В первую очередь - предлагаю разобраться с кнопкой, так как мы уже с этим имели дело. Для этого - импортируем в наш проект субмодуль работы с кнопкой. Создадим директорию для библиотек, и выполним команду добавления кнопки, а затем и индикатора ...... git submodule add http://domstudent.ru:3000/Library/DS_Button.git
Пропишем пути до С файлов в CMakeLists.
И для проверки того, что всё собрано и подключено корректно - предлагаю реализовать пока что простенький алгоритм - при нажатии кнопки - увеличивать число на индикаторе.
Для этого - добавим наши заголовочные файлы в main.c. Создадим и инициализируем кнопку на энкодере. Создадим и инициализируем индикатор.
создадим переменную для хранения числа, выводимого на индикатор. Далее в бесконечном цикле - опрашиваем кнопку, и при нажатии - увеличиваем значение, выводим его на индикатор, и обновляем дисплей. Заливаем эту программу в микроконтроллер, и убеждаемся что всё работает как задумано. Немножко не то, что хотелось бы видеть. Забыли инициализировать переменную. Исправляем, и заново запускаем отладку. Отлично, ошибок в сборке схемы не допущено, и при нажатии кнопки у нас происходит увеличение счетчика. Можно заняться самым интересным - контролем направления вращения энкодера.
Как обычно, мы будем это делать в отдельном подключаемом файле, создадим заголовочный файл, и файл для исходных кодов. Так же добавим С файл в CMakeLists.
Далее - обычная обертка для заголовочного файла, что бы он не включался в исходники более одного раза.
И можно приступать.
Нам точно понадобятся:
Нам точно понадобятся:
Функция инициализации
Функция обновления состояния
Функция, возвращающая изменение состояния
Структура, хранящая всё, что будет касаться энкодера.
Добавим заголовочный файл ХАЛа, для работы с различными типами значений.
В структуре будут храниться порты подключения энкодера. Что бы заработало автодополнение в новых файлах - включим их в main.c
продолжаем со структурой. Добавим в неё пины энкодера, его состояние, направление вращения, и по аналогии с кнопкой - будем следить, что бы не обновлять состояние слишком часто, по этому потребуется хранить время предыдущего обновления.
На первый взгляд этого достаточно, можно приступать к реализации функций. Правда сначала необходимо дописать прототипы.
В первую очередь - инициализация. Тут всё просто. Просто инициализируем все поля нашей структуры.
Далее - очередь функции обновления. Она немного сложнее. Необходимо описать алгоритмом то, что мы видели на анимации в начале видео.
Сначала сделаем проверку, что функция вызывается не чаще чем 1 раз в 10 миллисекунд.
Далее - получаем состояние первой сигнальной линии.
Если на ней высокий уровень сигнала, то необходимо сохранить информацию о том, что уровень сигнала высокий. Добавим в нашу структуру ещё пару переменных, инициализируем их, и сохраним информацию о том, что мы поймали высокий уровень сигнала на первой линии. Если же уровень сигнала низкий - обнулим значение переменной.
Аналогично поступаем со второй линией. Считываем её состояние, и если там высокий уровень сигнала - сохраняем эту информацию.
После этого - по значению состояния наших линий анализируем в каком состоянии мы сейчас находимся. Если на первой линии высокий уровень, а на второй линии - низкий, то вращаем в одну сторону. Если на первой линии низкий уровень, а на второй линии - высокий, то вращаем в другую сторону. Как определились с направлением - ждём когда оба сигнала поднимутся в высокий уровень, фиксируем шаг, и сбрасываем флаг направления. Вот собственно и всё, мы полностью описали полный шаг инкрементального энкодера.
Осталось последнее - описать функцию, возвращающую количество сделанных энкодером шагов. Она очень простая. Сохраним во временную переменную значение, обнулим его, и вернём вызывающей функции.
Давайте теперь проверять что получилось.
Для этого вернёмся к файлу main.c.
Создадим экземпляр энкодера.
Немножко не по шаблону создал структуру, переименую её во всех файлах.
Создаём экземпляр структуры. Инициализируем энкодер.
Далее - в бесконечном цикле - вызываем обновление состояния энкодера. Считываем его состояние.
Если полученное состояние отличное от нуля - изменяем значение переменной для отображения на индикаторе на полученное значение. После чего выводим его на дисплей и обновляем его.
Так же не забываем что при нажатии кнопки - необходимо обнулить отображаемое на дисплее значение.
Вот и всё, прошиваем и проверяем.
Если неспеша вращать вал энкодера - то значение отображаемое на индикаторе - отлично отражает и направление вращения, и количество пройденных шагов. Но если повернуть энкодер быстро, то счетчик практически не меняется, хотя на самом деле он должен быть значительно измениться. Дело в том, что выбранное нами значение в 10 миллисекунд - это очень медленно. Давайте изменим его на 1 миллисекунду, и посмотрим на результат.
Так на много лучше - при быстром повороте, значение так же быстро меняется. Так что первое знакомство с энкодером можно считать оконченным.
На этом на сегодня всё, спасибо за внимание.