AVR. Учебный Курс. Инкрементальный энкодер
Наверняка, каждый сталкивался, в повседневной жизни, с энкодером. Например, в автомобильных магнитолах их используют для управления громкостью. Или в компьютерных мышках, колесо прокрутки.
С их помощью очень удобно организовывать меню. Мне известны случаи, когда на очень серьезном и дорогом устройстве, все управление организовано, при помощи всего одного энкодера. Аналогично, в давние времена попадалась модель телефона, где все управление, также было организовано всего одним колесиком.
Прежде всего энкодеры бывают нескольких типов, рассматриваемый в данной статье — механический инкрементальный. В качестве испытуемого, был использован pec12-4220f-s0024. Внешне он похож на переменный резистор, но, в отличие от резистора, он не имеет ограничителей, т.е. может крутиться бесконечно в любую сторону.
Результат работы такого устройства — двоичный код Грея. Получить его можно анализируя состояние ножек, на которые приходят импульсы от энкодера.
Теперь рассмотрим все более детально. Электрически он представляет собой 2 кнопки без фиксации, когда мы начинаем крутить они по очереди срабатывают — сначала одна, затем вторая. В зависимости от того, в какую сторону мы вращаем, одна из кнопок срабатывает раньше или позднее. Для того чтобы узнать, в каком состоянии находятся эти кнопки, ножки порта (к которому подсоединен энкодер) должны быть подтянуты к «+» питания.
На разобранном энкодере 1/3 площадки относится к 1 контакту, 1/3 к 2 контакту, сплошной участок — общий. Когда скользящие контакты попадают на изолированные участки (черные), слышны щелчки. В этот момент энкодер, находится в устойчивом состоянии, когда обе кнопки разомкнуты. На ножках порта будут лог единицы(состояние 11).
Как только мы начинаем вращать в какую либо сторону, один из контактов замыкается на землю. На этой ножеке появится лог 0, на второй ножке по прежнему будет лог1 (состояние 01). Если мы продолжаем вращать, на второй ножке появится лог0(состояние 00). Далее, на первой ножке пропадает контакт (состояние 10), в конце концов энкодер возвращается в устойчивое состояние (11). Т.е. на один щелчок приходится 4 изменения состояния. Временная диаграмма выглядит так:
При вращении в противоположную сторону, идея остается прежней, только сначала будет замыкаться, вторая ножка.
Если выписать эти состояния в двоичной системе и перевести их в десятичную, то получится следующий порядок(для вращения в одну сторону):
11=3
01=1
00=0
10=2
При вращении в противоположную сторону:
11=3
10=2
00=0
01=1
Теперь осталось понять, как эти значение обрабатывать. Допустим, энкодер подключен к ножкам порта В0 и В1. Нам нужно прочитать эти ножки. Есть довольно хитрый способ, но для начала нам нужно понять операцию «логического и» (&).
Результат будет равен единице, только если оба числа равны 1, т.е. результат операции 1&1, будет равен 1. Следовательно 1&0=0, 0&0=0, 0&1=0.
Логическое & поможет нам вычленить из целого порта, только интересующие нас ножки. Т.е. операция x=0b00000011 & PINB; позволит нам прочитать в переменную «х» состояние первых двух ножек, независимо от того, что находится на остальных ножках. Число 0b00000011 можно перевести в шестнадцатеричную систему 0х3.
Теперь все необходимые знания для написания прошивки у нас есть. Задача: увеличивать/уменьшать переменную Vol, при помощи энкодера, результат вывести на lcd дисплей.
#include |
#include
В качестве пояснений: таймер 1 настроен на срабатывание 1000 раз в секунду, строкой NewState=PINB & 0b00000011; считываем состояние ножек 0 и 1 портаВ. if(NewState!=OldState) если состояние не изменилось, значит вращения нет.
Если состояние изменилось конструкция switch определяет в какую сторону было произведено вращение, в зависимости от этого увеличивается значение переменной downState(влево) или upState(вправо).
От щелчка до следующего щелчка 4 изменения состояния, поэтому 1 раз за 4 импульса изменяем переменную Vol. Ее же и выводим на дисплей. Прошивка доступна
Из этой статьи вы узнаете, что такое энкодер, зачем он нужен, и как его подружить с микроконтроллером. Если вы пользовались современной стиральной машиной, микроволновой печью или аудио системой то, скорее всего вы уже имели дело с энкодером, сами того не подозревая. Например, в большинстве современных домашних и автомобильных стерео систем энкодеры используются для регулировки громкости звука.
Энкодер или датчик угла поворота – это электромеханическое устройство, предназначенное для преобразования углового положения вала или оси в электрические сигналы. Существует два основных типа энкодеров - инкрементные и абсолютные.
Инкрементный энкодер при вращении формирует импульсы, число которых пропорционально углу поворота. Подсчет числа этих импульсов даст нам величину угла поворота вала энкодера относительно его начального положения. Этот тип энкодеров не формирует выходные импульсы, когда его вал находится в покое. Инкрементные энкодеры находят широкое применение в индустриальных средствах управления, бытовой и музыкальной технике.
Абсолютный энкодер для каждой позиции своего вала выдает уникальный код. Ему, в отличии от инкрементного энкодера, счетчик не нужен, угол вращения всегда известен. Абсолютный энкодер формирует сигнал и когда вал вращается, и когда он находится в покое. Абсолютный энкодер не теряет информацию о своем положении при потере питания и не требует возврата в начальную позицию. Этот тип энкодеров применяется в промышленно оборудовании - робототехнике, станках, конвейерных линиях.
Я хотел бы рассказать о сопряжении инкрементного механического энкодера с микроконтроллером. Для этого я приобрел инкрементный энкодер фирмы Bourns - PEC12-4220F-S0024. Вот расшифровка его названия согласно datasheet: PEC12 – модель, 4 – вертикальное положение выводов, 2 – 24 стопора, 20 – длина вала в мм, S – наличие кнопки, 0024 – 24 импульса за оборот.
У него 5 выводов. 2 вывода на фотографии слева – выводы кнопки, 3 вывода на фотографии справа – выводы энкодера. Из них - 2 сигнальных и 1 общий. Он посередине. Схема подключения энкодера ничем не отличается от подключения обычных кнопок. Сигнальные выводы энкодера подключаем к любому порту ввода вывода микроконтроллера. Общий вывод энкодера сажаем на землю. Для защиты от дребезга контактов не лишним будет добавить еще пару керамических конденсаторов номиналом в несколько нанофарад. Выводы микроконтроллера в программе конфигурируем как входы и включаем подтягивающие резисторы. Можно использовать внешние.
Когда ручка энкодера стоит неподвижно – на входах микроконтроллера присутствуют логические единицы. Когда ручку энкодера поворачивают, на выводах микроконтроллера появляются два прямоугольных сигнала сдвинутых друг относительно друга. От направления вращения вала энкодера зависит, какой из сигналов будет опережать другой. На рисунке ниже представлены возможные варианты сигналов для идеального случая.
Внутри энкодера имеются контакты, которые при вращении то замыкаются, то размыкаются. Этот процесс естественно сопровождается дребезгом, поэтому реальные сигналы могут выглядеть вот так.
Сигналы сняты со старого энкодера, включенного без фильтрующих конденсаторов.
Алгоритм обработки сигналов энкодера выглядит следующим образом. В обработчике прерывания таймера запускается функция опроса энкодера. Она считывает логические уровни, присутствующие на выводах микроконтроллера к которым подключен энкодер и записывает их во временную переменную. Внутри функции есть статическая переменная (переменная, которая сохраняет свое значение при выходе из функции) хранящая последовательность предыдущих состояний. С помощью битовой маски микроконтроллер выделяет из этой переменной последнее состояние и сравнивает его с текущим, чтобы определить произошли ли изменения. Если состояния равны – функция завершает работу, если отличны – значение статической переменной сдвигается влево на 2 разряда и на «освободившееся» место записывается текущее состояние. Таким образом, если вал энкодера вращается, функция будет постоянно сохранять некую повторяющуюся кодовую последовательность. При вращении вправо – это будет 11100001. При вращении влево – 11010010. По этим последовательностям микроконтроллер и будет понимать, в какую сторону происходит вращение.
Исходник для работы с энкодером можно скачать . Архив содержит два файла: encoder.h и encoder.c. В хедере задаются порт и номера выводов, к которым подключен энкодер, константы LEFT_SPIN и RIGHT_SPIN. Также там описаны прототипы функций. Сишный файл содержит реализацию функций.
void InitEncoder(void) – инициализирует выводы порта.
void PollEncoder(void) – однократно опрашивает энкодер. Если зафиксировано вращение, записывает в буфер одну из констант, если нет, просто завершает работу.
unsigned char GetStateEncoder(void) – возвращает содержимое буфера и очищает его.
Опрос энкодера я обычно произвожу с частотой ~ 4 кГц. Если опрашивать медленней, микроконтроллер будет пропускать импульсы при быстрых поворотах ручки энкодера. Если энкодер используется для установки линейно меняющейся величины, например для установки времени в часах, то в качестве констант LEFT_SPIN и RIGHT_SPIN удобно использовать числа 255 и 1 соответственно. В обработчике сигналов энкодера эти числа просто складываются с устанавливаемой величиной. При сложении с 1 величина увеличивается на 1, при сложении с 255 уменьшается на 1. Конечно это актуально если эта величина однобайтная. Ну а в принципе константы LEFT_SPIN и RIGHT_SPIN можно выбирать произвольно, главное правильно написать обработчик. На этом все.
Исходник для работы с энкодером .
Принцип действия, схема включения и исходник библиотеки для работы с инкрементным энкодером уже рассматривался мной в одной из статей. Сегодня мы поговорим о практическом применении энкодера. В качестве примера я выбрал программу генератора прямоугольного сигнала с диапазоном рабочих частот 1 – 100 Гц. Первоначальный замысел предполагал диапазон 1 - 1000 Гц, но на практике выяснилось, что перебирать тысячу значений утомительно даже с энкодером.
Подготовка
Создаем в пустом workspace`е новый проектProject > Create New Project…
Тип шаблона C > main
Копируем в папку проекта файлы исходника библиотеки для работы с энкодером
encoder.h и encoder.c
Подключаем к нашему проекту файл encoder.c
Правая кнопка мышки в окне workspace и в открывшемся меню Add > Add Files…
Копируем файл bits_macros.h в папку проекта.
Подключаем заголовочные файлы
В начале файла main.c забиваем следующие строки
#include
#include
#include
"encoder.h"
#include
"bits_macros.h"
Задаем настройки проекта
Project > Options
Тип микроконтроллера
General Options > Target > Processor Configuration > ATMega8535
Разрешение использования имен битов определенных в заголовочных файлах
General Options > System > Enable bit defenitions...
Оптимизация кода по размеру
C/C++ Compiler > Optimisations >Size High
Тип выходного файла
Linker > Output File галочка Override default и поменять расширение на hex
Linker > Format > Other выбрать Intel Standart
Жмем Ок. Сохраняем проект и workspace.
Теперь у нас есть пустой проект с подключенной либой и заданными настройками.
Задача
Заставить микроконтроллер генерировать меандр с частотой от 1 до 100 Гц. Значение частоты должно задаваться с помощью энкодера. Поворот энкодера на одну позицию должен соответствовать изменению частоты генератора на 1 Гц.
Схема для нашего примера
К выводу, на котором будет генерироваться меандр, подключен светодиод, чтобы хоть как-то видеть результат работы программы. Вряд ли у многих под рукой осциллограф.
Алгоритм программы
Прямоугольный сигнал генерируется с помощью 16 разрядного таймера Т1, который работает в режиме СТС – сброс при совпадении. Во флэш памяти микроконтроллера храниться массив, содержащий для каждого значения требуемой частоты константу. Для доступа к элементам массива используется переменная pTimerValue. В прерываниях таймера Т1 значение константы считывается и записывается в регистр сравнения.
Для генерации сигнала используется вывод PD5 (OC1A). У него есть альтернативные функции – он может менять свое состояние на противоположное при равенстве счетного регистра и регистра сравнения.
В основной программе в бесконечном цикле while микроконтроллер опрашивает буфер энкодера и в зависимости от его значения уменьшает или увеличивает переменную pTimerValue.
В самом начале main`а располагается код инициализации периферии и необходимых переменных.
Структура программы
Для наглядности я изобразил структуру программы в виде диаграммы.Это типовая структура построения простых программ. Прерывания естественно происходят в произвольном месте цикла.
- Инициализация.
- Бесконечный цикл (так называемый superloop), в котором происходит ожидание события, обычно в виде опроса флагов или какого-нибудь буфера.
- Параллельная работа периферийных устройств, вызывающих прерывания. В них выполняется какой-то код (желательно короткий) и выставляются флаги.
Для простых задач такого подхода хватает за глаза. Для сложных существуют другие способы организации программ. Наберитесь терпения, скоро и до них дойдет дело.
Расчет констант для таймера Т1
Рассчитаем значение константы для частоты 1 Гц. Подобный расчет я уже приводил, но будет не лишним его вспомнить
Тактовая частота микроконтроллера 16 МГц (смотрите схему). Коэффициент предделителя таймера - 256. Он позволяет получить прерывания с любой частотой из нашего диапазона.
Период одного тика таймера будет равен 1/(16 МГц/ 256) = 16 мкс
На выводе PD5 нам нужно получить сигнал частотой 1 Гц. Вывод меняет свое состояние на каждое прерывание таймера и значит, частота прерываний должна быть в 2 раза больше. Для нашего случая - 2 Гц.
Сколько тиков таймера уложится в 2 Герца? (1/2 Гц)/16 мкс = 31250
Это и есть искомая константа.
Остальные значения рассчитываются аналогично. Я для этого обычно использую Exel.
Полученные значения мы помещаем в массив
__flash
unsigned int
timerValue =
{
сохраняем его в отдельном файле – timer_value.h и подлючаем его к файлу main.c
#include "timer_value.h"
Да, еще нужно добавить парочку констант в этот файл
#define
MAX_TIM_VALUE 99
#define
MIN_TIM_VALUE 0
Убедимся, что правильно рассчитали константы для таймера. Запустим его. Код программы будет такой.
//программирование AVR на Си
//сайт 17.10.09
#include
#include
#include
"encoder.h"
#include
"bits_macros.h"
#include
"timer_value.h"
//индекс для доступа к элементам массива
volatile unsigned char
pTimerValue = 0;
int
main(void
)
{
//инициализация таймера Т1
TCNT1 = 0;
TCCR1A = (0<
//настройка вывода PD5 на выход
SetBit(PORTD, PD5);
SetBit(DDRD, PD5);
//ничего не делаем в бесконечном цикле
while
(1);
return
0;
}
Думаю, пояснения требует только кусок инициализации таймера.
Обнуление счетного регистра
TCNT1 = 0;
Инициализация конфигурационных регистров таймера Т1. Где биты WGM13, WGM12, WGM11, WGM10 задают режим работы таймера – СТС, COM1A1, COM1A0 – определяют поведение вывода PD5(OC1F) – в данном случае по сигналу таймера он будет менять свое состояние на противоположное Компилируем программу и грузим в микроконтроллер. Светодиод должен моргать с частотой 1 Гц. Теперь нужно “прикрутить” к этой программе энкодер. Зададим настройки в хедер файле encoder.h – порт и выводы, к которым подключен энкодер, значения констант. #define
RIGHT_SPIN 0x01 Хедер содержит прототипы трех функций. Вспомним их назначение. void ENC_InitEncoder(void)
настраивает выводы микроконтроллера, к которым подключен энкодер на вход. Эту функцию нужно вызвать в начале main`а. void ENC_PollEncoder(void)
– однократно опрашивает энкодер, анализирует текущее и предыдущее состояния и записывает в буфер соответствующие константы (RIGHT_SPIN и LEFT_SPIN). Эта функция будет сидеть в прерывании таймера Т0. unsigned char ENC_GetStateEncoder(void)
– возвращает содержимое буфера энкодера. Если поворот на одну позицию не был зафиксирован – функция вернет 0, если поворот был зафиксирован функция вернет значение соответствующей константы. При этом значение буфера очистится. Эта функция будет вызываться в основном программе – в цикле while. //программирование AVR на Си #include
#define
TCNT0_const 253 volatile unsigned char
pTimerValue = 0; int
main(void
) //инициализация таймера т0
//инициализация таймера т1
//настраиваем PD5 на выход
__enable_interrupt
(); //если не пустой
//опрос энкодера
#pragma
vector=TIMER1_COMPA_vect Вроде все должно быть понятно. if
(stateEnc != 0) { При вращении энкодера вправо pTimerValue складывается с 1, то есть инкрементируется. При вращении энкодера влево pTimerValue складывается с 0хff, что равносильно вычитанию 1. Одна и та же операция, а результат прямо противоположный. Для реализации демонстрационного проекта нам понадобятся: Энкодер - современный и оригинальный элемент управления цифровыми устройствами, и по внешнему виду похож на переменный резистор (см. рисунок ниже). Другое название этого элемента управления - датчик угла, датчик поворота. Вращение вала сопровождается щелчками, например 24 щелчка на один оборот. Энкодер имеет 3 вывода - A, B, C и применяется для быстрого ввода данных в цифровые устройства. Некоторые модели имеют встроенную кнопку, которая срабатывает по нажатию на вал энкодера (добавляется еще один вывод). Принцип работы энкодера
При повороте на один щелчок, например, вправо, сначала замыкается контакт А+С, затем В+С. Когда в этом щелчке вал доворачивается, в той же последовательности контакты размыкаются. При повороте вала в другую сторону, последовательность замыкания с контактом С меняется, т.е. при повороте влево замыкаются сначала В+С, затем А+С. Используя энкодер в проектах на микроконтроллерах, возможно, при помощи одного и того же энкодера, реализовать несколько различных типов ввода данных, однако, это требует некоторой обратной связи и визуализации, чтобы пользователь знал, какую информацию он вводит и в какой позиции энкодер. Принципиальная схема
Выводы энкодера A и B подключаются к портам микроконтроллера RB4 и RB5, вывод С энкодера подключается к «земле». Стоит заметить, что на сигнальные линии выводов A и B должны быть подключены подтягивающие резисторы. Энкодер не случайно подключен к указанным линиям ввода/вывода микроконтроллера: во-первых, порт B имеет встроенные подтягивающие резисторы и нам не придется подключать внешние, во-вторых, порт B микроконтроллера имеет очень полезную функцию - «interrupt-on-change» - прерывание по изменению уровня, что позволит нам отслеживать состояние энкодера. 16 обычных 3 мм светодиодов используются для визуализации вводимых данных и расположены они будут на печатной плате вокруг установленного энкодера. Светодиоды подключены к микросхеме A6276. Микросхема A6276 представляет собой драйвер светодиодов с 16-битным последовательным вводом информации. Драйвер содержит 16-битный КМОП сдвиговый регистр, соответствующие защелки и драйверы для управления светодиодами и может управлять большим количеством светодиодов, чем это позволяет микроконтроллер. Кроме того, драйвером можно управлять по интерфейсу SPI, что дополнительно сокращает количество используемых линий ввода/вывода и делает проект масштабируемым. Программное обеспечение микроконтроллера для решения нашей задачи относительно простое. Реализуется 3 режима работы (ввод информации) и обратная связь: Демонстрация работы проекта
Загрузки
ZIP-архив с проектом в среде MPLAB и исходным кодом на Hitech C, а также, принципиальная схема и топология печатной платы находятся . Узнайте, как использовать инкрементальный поворотный энкодер в проекте на Arduino.
Поворотный энкодер представляет собой электромеханическое устройство, которое преобразует вращательное движение в цифровую или аналоговую информацию. Он очень похож на потенциометр, но может вращаться бесконечно как по часовой стрелке, так и против часовой стрелки. Существует несколько типов поворотных энкодеров. Двумя основными типами являются абсолютные и относительные (инкрементальные) энкодеры. В то время как абсолютный энкодер выдает значение, пропорциональное текущему углу вала, инкрементальный энкодер выдает шаг движения вала и его направление. Поворотные энкодеры становятся всё более и более популярными в потребительской электронике, особенно в качестве ручек управления, в дополнение к приложениям во многих других областях. Они заменяют собой потенциометры и кнопки навигации, где требуются быстрая навигация, настройка, ввод данных и выбор пункта меню. Некоторые энкодеры также включают в себя встроенную кнопку, которая создает дополнительный вход для процессора, который может использоваться в качестве другой пользовательской команды в интерфейсе управления. На рисунке ниже вы можете увидеть типовой инкрементальный поворотный энкодер с кнопкой включения. В данной статье мы покажем вам, как использовать инкрементальный поворотный энкодер в проекте на Arduino. Мы объясним, как бороться с дребезгом контактов и интерпретировать сигналы энкодера в программе микроконтроллера, используя прерывания. Инкрементальный поворотный энкодер во время поворота вала генерирует два выходных сигнала, что также называется квадратурным выходом. В зависимости от направления один сигнал опережает другой. Ниже вы можете увидеть форму выходного сигнала инкрементального поворотного энкодера и ожидаемую последовательность битов. Как видно из рисунка, оба выхода в изначально находятся в состоянии логической единицы. Когда вал энкодера начинает вращаться в направлении по часовой стрелке, первым падает до логического нуля состояние на выходе A, а затем с отставанием за ним следует и выход B. При вращении против часовой стрелки всё происходит наоборот. Временные интервалы на диаграмме сигнала зависят от скорости вращения, но отставание сигналов гарантируется в любом случае. На основе этой характеристики инкрементального поворотного энкодера мы напишем программу для Arduino. Механические энкодеры имеют встроенные переключатели, которые формируют сигнал на квадратурном выходе во время вращения. Когда имеем дело с сигналами энкодера, основной проблемой является дребезг контактов. Он вызывает ошибочное определение направления вращения и величины поворота вала энкодера и делает использование энкодеров проблематичным. Мы можем избавиться от дребезга контактов, отфильтровывая его в программе или используя дополнительные схемы фильтрации. Фильтрация шума в программном обеспечении микроконтроллера является одним из вариантов фильтрации, но она обладает некоторыми недостатками. Вам необходимо написать более сложный код для обработки шума. Фильтрация займет время обработки и внесет задержки в основной поток программы. Вам может потребоваться установить таймеры, чтобы игнорировать интервалы дребезга контактов. В конце концов, возможно, у вас не получится получить удовлетворительный и надежный результат. Фильтрация шума с помощью дополнительных аппаратных средств проще, и она останавливает шум еще в его источнике. Вам понадобится RC фильтр первого порядка. На рисунке ниже вы можете увидеть, как выглядит сигнал после использования RC фильтра. RC-фильтр замедляет время спада и время нарастания и обеспечивает аппаратное удаление дребезга контактов. При выборе пары резистор-конденсатор вы должны учитывать максимальную частоту вращения. Иначе будет отфильтрован и ожидаемый отклик энкодера. Мы создадим приложение, демонстрирующее, как использовать поворотный энкодер в проекте на Arduino. Мы будем использовать энкодер для навигации, ввода данных и выбора. Ниже приведена принципиальная схема приложения. Схема построена на базе платы Arduino Uno. Для графического интерфейса используется LCD дисплей Nokia 5110. В качестве средств управления добален механический поворотный энкодер с кнопкой и RC-фильтрами. Мы разработаем простое программное меню, в котором и продемонстрируем работу поворотного энкодера. Сигналы энкодера должны быть обнаружены и интерпретированы в программе как можно быстрее, чтобы не блокировать основной поток программы. Мы можем детектировать сигналы путем опроса в основном цикле, или используя прерывания. Опрос не эффективен, так как вам необходимо зарезервировать время и ресурсы в основном цикле, что приводит к дополнительным задержкам. Использование прерываний - это более быстрое и экономичное решение. Мы покажем вам, как использовать прерывания для обработки сигналов энкодера. В Atmega328 есть два типа прерываний, которые можно использовать для этих целей; внешнее прерывание и прерывание по изменению состояния вывода. Выводы INT0 и INT1 назначены на внешнее прерывание, а PCINT0 - PCIN15 назначены на прерывание по изменению состояния вывода. Внешнее прерывание может определить, произошел ли спад или нарастание входного сигнала, и может быть запущено при одном из следующих состояний: нарастание, спад или переключение. Для прерывания по изменению состояния выводов существует гораздо больше аппаратных ресурсов, но оно не может обнаруживать нарастающий и спадающий фронты, и оно вызывается, когда происходит любое изменение логического состояния (переключение) на выводе. Чтобы использовать прерывание по изменению состояния выводов, подключите выходы поворота энкодера A и B к выводам A1 и A2 , а выход кнопки - к выводу A0 платы Arduino, как показано на принципиальной схеме. Установите выводы A0 , A1 и A2 в режим входа и включите их внутренние подтягивающие резисторы. Включите прерывание по изменению состояния выводов в регистре PCICR и включите прерывания для выводов A0 , A1 и A2 в регистре PCMS1 . При обнаружении любого изменения логического состояния на одном из этих входов будет вызовано ISR(PCINT1_vect) (прерывание по изменению состояния выводов). Поскольку прерывание по изменению состояния выводов вызывается для любого логического изменения, нам необходимо отслеживать оба сигнала (и A, и B) и обнаруживать вращение при получение ожидаемой последовательности. Как видно из диаграммы сигналов, движение по часовой стрелке генерирует A = …0011… и B = …1001… . Когда мы записываем оба сигналы в байты seqA и seqB , сдвигая последнее чтение вправо, мы можем сравнить эти значения и определить новый шаг вращения. Вы можете увидеть часть кода, включающую инициализацию и функцию обработки прерывания по изменению состояния выводов. Void setup()
{
pinMode(A0, INPUT);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
// Включить внутренние подтягивающие резисторы
digitalWrite(A0, HIGH);
digitalWrite(A1, HIGH);
digitalWrite(A2, HIGH);
PCICR = 0b00000010; // 1. PCIE1: Включить прерывание 1 по изменению состояния
PCMSK1 = 0b00000111; // Включить прерывание по изменению состояния для A0, A1, A2
}
void loop()
{
// Основной цикл
}
ISR (PCINT1_vect)
{
// Если прерывание вызвано кнопкой
if (!digitalRead(A0))
{
button = true;
}
// Если прерывание вызвано сигналами энкодера
else
{
// Прочитать сигналы A и B
boolean A_val = digitalRead(A1);
boolean B_val = digitalRead(A2);
// Записать сигналы A и B в отдельные последовательности
seqA <<= 1;
seqA |= A_val;
seqB <<= 1;
seqB |= B_val;
// Маскировать четыре старших бита
seqA &= 0b00001111;
seqB &= 0b00001111;
// Сравнить запсанную последовательность с ожидаемой последовательностью
if (seqA == 0b00001001 && seqB == 0b00000011)
{
cnt1++;
left = true;
}
if (seqA == 0b00000011 && seqB == 0b00001001)
{
cnt2++;
right = true;
}
}
}
Использование внешнего прерывания делает процесс более простым, но поскольку для этого прерывания назначено только два вывода, то вы не сможете использовать его для других целей, если займете его энкодером. Чтобы использовать внешнее прерывание, вы должны установить выводы 2 (INT0) и 3 (INT1) в режим входа и включить их внутренние подтягивающие резисторы. Затем выберите вариант спадающего фронта для вызова обоих прерываний в регистре EICRA . Включите внешние прерывания в регистре EIMSK . Когда начнется вращение вала энкодера, сначала ведущий сигнал падает до логического нуля, а второй сигнал некоторое время остается на уровне логической единицы. Поэтому нам нужно определить, какой из сигналов во время прерывания находится в состоянии логической единицы. После того, как ведущий сигнал упал до логического нуля, через некоторое время второй сигнал также упадет до логического нуля, что вызовет другое прерывание. Но этот раз и другой (ведущий) сигнал будет на низком логическом уровне, что означает, что это не начало вращения, поэтому мы игнорируем его. Ниже вы можете увидеть часть кода, включающую в себя инициализацию и функцию обработки внешнего прерывания. Void setup()
{
pinMode(2, INPUT);
pinMode(3, INPUT);
// Включить внутренние подтягивающие резисторы
digitalWrite(2, HIGH);
digitalWrite(3, HIGH);
EICRA = 0b00001010; // Выбрать вызов по спадающему фронту
EIMSK = 0b00000011; // Включить внешнее прерывание
}
void loop()
{
// Основной цикл
}
ISR (INT0_vect)
{
// Если второй сигнал находится в состоянии логической единицы, то это новое вращение
if (digitalRead(3) == HIGH)
{
left = true;
}
}
ISR (INT1_vect)
{
// Если второй сигнал находится в состоянии логической единицы, то это новое вращение
if (digitalRead(2) == HIGH)
{
right = true;
}
}
Полный код скетча Arduino, включающий основной цикл приведен ниже: #include Энкодер в действии вы можете увидеть на видео, приведенном ниже.
TCCR1A = (0<
CS12, CS11, CS10 – определяют коэффициент предделителя таймера –256,
Инициализация регистра совпадения начальным значением.
OCR1A = timerValue;
В программе нет никаких прерываний. Нет никаких манипуляций с выводом PD5. Однако светодиод моргает! Программа
#define
PORT_Enc PORTA
#define
PIN_Enc PINA
#define
DDR_Enc DDRA
#define
Pin1_Enc 2
#define
Pin2_Enc 1
#define
LEFT_SPIN 0xff
Дополняем нашу программу. Можете попробовать сделать это самостоятельно.
//пример использования энкодера
//сайт 17.10.09
#include
#include
"encoder.h"
#include
"bits_macros.h"
#include
"timer_value.h"
#define
TCCR0_const 5
{
ENC_InitEncoder();
TCNT0 = TCNT0_const;
TCCR0 = TCCR0_const;
TCNT1 = 0;
TCCR1A = (0<
//разрешение прерываний от таймеров
//т0 - по переполнению, т1 - по совпадению
TIMSK = (1<
SetBit(PORTD, PD5);
SetBit(DDRD, PD5);
while
(1){
//считываем содержимое буфера энкодера
//после считывания он очищается
unsigned char
stateEnc = ENC_GetStateEncoder();
if
(stateEnc != 0){
//определяем направление вращения и изменяем переменную timerValue
if
(stateEnc == RIGHT_SPIN){
if
(pTimerValue == MAX_TIM_VALUE) pTimerValue = MIN_TIM_VALUE;
else
pTimerValue++;
}
if
(stateEnc == LEFT_SPIN) {
if
(pTimerValue == MIN_TIM_VALUE) pTimerValue = MAX_TIM_VALUE;
else
pTimerValue--;
}
}
}
return
0;
}
#pragma
vector=TIMER0_OVF_vect
__interrupt
void
timer0_ovf_my(void
)
{
TCNT0 = TCNT0_const;
ENC_PollEncoder();
}
__interrupt
void
timer1_compa_my(void
)
{
//обновляем значение регистра стравнения
OCR1A = timerValue;
}
Кусок кода, в котором изменяется значение pTimerValue, можно было бы написать еще так:
pTimerValue = pTimerValue + stateEnc;
if
(pTimerValue == (MAX_TIM_VALUE + 1)) pTimerValue = MIN_TIM_VALUE;
else if
(pTimerValue == (MIN_TIM_VALUE - 1)) pTimerValue = MAX_TIM_VALUE;
}Сигнал квадратурного выхода инкрементального энкодера
Фильтрация дребезга контактов механического энкодера
Простое приложение
Обработка сигналов энкодера с помощью прерываний