Подключение N-кодера(энкодер) к микроконтроллеру AVR
При конструировании всяких подвижных агрегатов, будь то робот или станок на производстве, встает вопрос о контроле положения движущихся частей. Для таких целей используют валкодеры (их еще называют энкодеры, датчики положения). Валкодеры бывают механические и оптические. Типичный механический валкодер представляет собой диск с прорезями и две группы контактов:
При вращении диск замыкает и размыкает контакты, образуя синус и косинус сигналы. Направление кручения ручки определить легко. На нижние контакты подадим низкий уровень, а верхние «подтянем» резисторами к +5В и с них будем снимать сигнал. Условимся, что с Контактной Пары №1 будем снимать синус сигнал, а с Контактной Пары №2 – косинус сигнал. При кручении по часовой стрелке имеем такие сигналы:
То есть, косинус сигнал смещен относительно синуса «вперед» на 90 градусов. А против часовой:
Видно, что косинус смещен относительно синуса «назад» на 90 градусов.
Оптический энкодер состоит из прозрачного диска (зубчатого колесика), с темными черточками и двух разнесенных пар фотоизлучателя и фотоприемника, которые с помощью операционного усилителя формируют такие же TTL косинус и синус сигналы, как и у механического валкодера. Также встречаются валкодеры в виде длинной прозрачной «линейки» с черточками, по этой линейке ездит каретка с фотоизлучателями/фотоприемниками, такие часто встречаются в струйных принтерах и отвечают за положение печатающей каретки по горизонтали.
Перейдем к практической части. Соорудим из шариковой мышки валкодер для экспериментов. Выпотрошим всю мышиную начинку, и на освободившейся плате спаяем схему:
Схема состоит из двух идентичных каналов. Конденсаторы С1,С2 – блокировочные по 0.1мкФ. Резисторы R1,R2,R5,R6 по 10 кОм, R4,R7,R10,R11 – 5,1 кОм, R9 – ограничительный на 1 кОм, R3,R8 – 1 МОм. D1-J1-J2 – оптопара, используемая в мышке, иногда она бывает с общим катодом, а не с общим анодом, как изображено на схеме. Подключение такой оптопары не составит труда, достаточно поменять местами входа операционного усилителя.
Проверим с помощью тестера правильность косинус-синус сигналов при движении мышки. Если все как задумывалось – то припаяем энкодер к микроконтроллеру:
Косинус сигнал мы повесим на внешнее прерывание INT0, а синус – на PD0. Здесь я немного по-другому подключил ЖКИ, сигнал RS повесил на вывод PD1, а не на PD2, как я обычно делаю.
Перейдем к написанию программы, демонстрирующую работу с валкодером. Условимся, что вертикальным движением мышки мы будем изменять значение некоторой переменной position в границах от -9999 до 9999 и выводить его на ЖКИ.. Алгоритм работы программы очень прост. Нам необходимо включить внешнее прерывание INT0 по фронту косинус сигнала.
Пин PD0 выставить как вход. А в подпрограмме обработки прерывания сделать опрос PD0, если на нем низкий уровень – инкрементировать нашу переменную position, а если высокий – декрементировать (и не забыть про наши ограничения в |position|<=9999). В теле главной программы запишем вечный цикл, в котором будем выводить значение переменной position на ЖКИ.
#include <avr/io.h>//библиотека ввода/вывода #include <avr/interrupt.h>//библиотека прерываний #define RS 1 //RS=PD1 #define E 3 //E=PD3 #define TIME 10 //Константа временной задержки для ЖКИ //Частота тактирование МК - 4Мгц int position=0; //Функция задержки(паузы) void pause (unsigned int a) { unsigned int i; for (i=a;i>0;i--); } void lcd_com (unsigned char lcd) //Передача команды ЖКИ { unsigned char temp; temp=(lcd&~(1<<RS))|(1<<E); //RS=0 – это команда PORTD=temp; //Выводим на portD старшую тетраду команды, сигналы RS, E asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации PORTD=temp&~(1<<E); //Сигнал записи команды temp=((lcd*16)&~(1<<RS))|(1<<E); //RS=0 – это команда PORTD=temp; //Выводим на portD младшую тетраду команды, сигналы RS, E asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации PORTD=temp&~(1<<E); //Сигнал записи команды pause (10*TIME); //Пауза для выполнения команды } void lcd_dat (unsigned char lcd) //Запись данных в ЖКИ { unsigned char temp; temp=(lcd|(1<<RS))|(1<<E); //RS=1 – это данные PORTD=temp; //Выводим на portD старшую тетраду данных, сигналы RS, E asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации PORTD=temp&~(1<<E); //Сигнал записи данных temp=((lcd*16)|(1<<RS))|(1<<E); //RS=1 – это данные PORTD=temp; //Выводим на portD младшую тетраду данных, сигналы RS, E asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации PORTD=temp&~(1<<E); //Сигнал записи данных pause(TIME); //Пауза для вывода данных } void lcd_init (void) //Иниализация ЖКИ { lcd_com(0x2c); //4-проводный интерфейс, 5x8 размер символа pause(100*TIME); lcd_com(0x0c); //Показать изображение, курсор не показывать pause(100*TIME); lcd_com(0x01); //Очистить DDRAM и установить курсор на 0x00 pause (100*TIME); } //Вывод на ЖКИ переменной position void write_position(void) { unsigned char i; long int temp; temp=position; lcd_com(0x80); if (temp<0) { //проверяем знак переменной lcd_dat('-'); temp=-temp; } else lcd_dat(' '); i=temp/1000; //пишем тысячи temp=temp-i*1000; lcd_dat(0x30+i); i=temp/100; //сотни temp=temp-i*100; lcd_dat(0x30+i); i=temp/10; //десятки temp=temp-i*10; lcd_dat(0x30+i); i=temp/1; //единицы temp=temp-i*1; lcd_dat(0x30+i); } ISR (INT0_vect) { if (((PIND&0x01)==0x00)&&(position<9999)) position=position+1; //инкремент position if (((PIND&0x01)==0x01)&&(position>-9999))position=position-1; //декремент position } int main(void) { DDRD=0xfa; //PD0,PD2 как входа, остальные - выхода PORTD=0x05; pause(3000); //Задержка для включения ЖКИ lcd_init(); //Инициализация ЖКИ MCUCR=(1<<ISC01)|(1<<ISC00); //Включаем прерывание INT0 по фронту на PD2 GICR=(1<<INT0); write_position(); //Пишем position sei(); //Разрешаем прерывания while(1) write_position(); return 1; }
Выводить значение переменной position можно сразу же после ее изменения. Но тогда обработка прерывания будет занимать много времени, а в это время, при быстром вращении валкодера можно пропустить дискрету. Так что не стоит забывать делать обработку прерывания как можно более короткой.
Скачать прошивку под ATmega8 в виде проекта для AVR Studio 4
Что у меня получилось из этой затеи можно увидеть на фото:
И видео:
приветствую.скачал прошивку,
приветствую.скачал прошивку, требует пароль. подскажите его пожалуйста. спасибо.
http://avrlab.com/node/2376
http://avrlab.com/node/2376 внизу страницы
Коротко и красиво.... Но
Коротко и красиво.... Но позвольте сделать пару замечаний.
1. Хороший тон программирования просит указывать модификатор volatile для переменных, изменяемых в прерываниях и используемых в основном цикле. В вашем случае, для переменной "position" это не существенно т.к. она читается один раз и тут оптимизировать компилятору нечего.
2. А вот тут 100500 есть косяк
...
long int temp;
temp=position;
...
Копирование long int на самом деле реализовано как (см. листинг N-koder.lss)
lds r24, 0x0060
lds r25, 0x0061
lds r26, 0x0062
lds r27, 0x0063
std Y+1, r24
std Y+2, r25
std Y+3, r26
std Y+4, r27
Сами прикинте что произойдет если между командами LDS rхx,xxx произойдет прерывание и значение position изменится...
Не знаю, есть ли в avr-gcc атомарные операции (в IAR С их точно нет), но решение в любом случае простое - запрет прерываний на время чтения.
......
__disable_interrupt();
temp=position;
__enable_interrupt();
......
Косяк для этого проекта некритичный. Изредка промелькнут "левые" значения на табло, может глазом и незаметно. В следующем цикле обновления ЖКИ отобразятся истинные значения.
Я просто предостерегаю на будущее, ведь глюки от подобных косяков трудно отлавливаемые. :-)
CVAVR
А в codevision этот код можно преобразовать ?
буду очень признателен !!!!
Преобразовать то можно,
Преобразовать то можно, только скоро не обещаю, так как CV в своей практике не применяю.
Encoder
By Walward
Я сначала тоже думал повесить на прерывания, не это не везде целесообразно.
Для механического энкодера актуальна проблема фильтрации дребезга контактов.
Сделал опрос по таймеру, 5кГц.
Буффер на 4 выборки - 1 регистр по 2 бита на выборку.
Потом просто сравниваем буффер с двумя константами: СW и ССW. Так сразу и дребезг фильтруем, и событие поворота определяем. Всего 2 условных перехода.
А я сразу стал играться с
А я сразу стал играться с оптическим, потому не столкнулся с дребезгом контактов в энкодере. Если буду что-то делать с механическим - то однозначно буду проверять по таймеру.
А идея с буфером - это хорошо, мне очень понравилась :)
а в оптическом дребезга
а в оптическом дребезга контактов ведь нету? сразу пороги?
где почитать как подключать/считать?
у меня вот такой KTIR0311S - допустим, для светодиода как подобрть сопротивление выяснили (хотя в этом конкретном случае - опять не ясно, на какую строчку в даташите смотреть), а вотс фотоэлементом как быть (как оно вообще устроено?)
Если крутить вал очень
Если крутить вал очень медленно - то может вылезти дребезг.
Учимся пользоваться документацией!
1) Шаг один - качаем
2) Шаг два - обычно в даташитах есть типовые схемы включения девайса, находим оную:
3) Шаг три - переделаем ее под себя:
Input - подключаем к +5В
Расчитываем Rd = примерно 470-560 Ом
Rl = 1кОм примерно.
Когда что-то будет мешать лучам от диода к транзистору - на выходе будет лог. 1, если же лучам ничего не мешает - транзистор открывается и на выходе лог. 0. Только этой штукой не получится понять куда крутится вал с шестеренкой.
не работает отчего-то эта
не работает отчего-то эта конструкция
во-первых светодиод не светит (наверное, там спектр какой-нибудь инфракрасный)
во-вторых, если щель не загораживать - на выходе 5,12В, если загораживать - 5,16
хотя дожно быть соответственно 5 и 0
брак?
коллектор подтянут через 960Ом к +5В, эмиттер и катод - к земле, анод - через 500Ом к плюсу.
а мне не важно, куда он
а мне не важно, куда он крутится. мне нужно по нажатию кнопки делать условно десять оборотов лебедки либо в одну сторону, либо в другую (если в одну уже отработал)
Input ведь можно и не к +5 подключать, а к ноге микросхемы, которую запрограммировать на порт вывода и подавать туда логическую единицу лишь когда нужно считать обороты (когда срабатывает реле или там что), чтобы не расходовать попусту энергию на свечение диода? Или надо опять делать ключ на транзисторе, так как нагрузка на светодиоде слишком высокая и можно выжечь порт микросхемы (поправьте, плиз, если я ошибаюсь)
я немного не понял: без света сопротивление транзистора резко увеличивается? (если нет света - то на выходе будет 1) и почему если свет есть - то будет 0? (коллектор с эмиттером замыкаются, сопротивление на этом участке 0, весь ток уходит на землю?)
и с третьим шагом все же не очень ясно, как подбираются эти номиналы. если с Rd еще куда ни шло, то с Rl - не понимаю.
еще вопрос технического плана: на какое расстояние можно безопасно разносить кнопки? если мне надо на один пин повесить параллельно две кнопки?
какое может быть энергопотребление у такого устройства? (как-то рассчитывается вообще?) как долго оно может работать от трех пальчиковых элементов? оно будет должно запоминать направление последнего перемещения (это либо в EEPROM писать, либо держать в памяти программы?) и уходить в слип моде до нажатия кнопки (потом включается оптрон, включается управляющее реле,начинается счетчик)
а типовой датчик холла - это такой же транзистор, только открывается магнитом, я ведь правильно понимаю?
Всякие нагрузки, кроме
Всякие нагрузки, кроме маломощных (меньше 10мА) лучше включать через ключ.
Там юзается фототранзистор, когда на него "светит" ИК диод - сопротивление его принимаем за 0, и ток течет через фототранзистор. Чтобы не дать спалить транз. ставят ограничительный резистор Rl=1кОм, то есть, в открытом состоянии через транзистор течет ток 5/1000=5мА при этом output закорочен на землю и на нем лог. 0. Rl подбирается из расчета, чтобы не спалить транз. Когда на транзистор не светят - картина противоположна, транз имеет сопротивление +бесконечность и ток не течет. Output подтянут к +5В через резистор Rl и на нем лог. 1.
"на какое расстояние можно безопасно разносить кнопки? если мне надо на один пин повесить параллельно две кнопки?"
Не понял. В смысле какие длинные провода могут быть от кнопок к МК?
Если грамотно спроектировать - энергопотребление в спящем режиме может быть на уровне 1мкА. Рассчитывеаться энергопотребление как сумма потребляемого тока всем девайсом (немножко не грамотно, потребление меряется в ваттах*сек). На сколько хватит элемента питания - возьми емкость в А*ч и подели на потребляемый ток, получил кол-во часов работы.
Для запоминания лучше юзать EEPROM, хотя можно и обойтись спящим режимом + выход из него по внешнему прерыванию.
Да, что-то похоже, только там часто внутри усилитель стоит и на выходе получаем уже лог. 1, лог. 0 или напряжение.
угу. спасибо за разъяснения!
угу. спасибо за разъяснения! более-менее понятно
в ведь Rl не только ограничивает ток но и снижает напряжение? Тогда во втором случае Rl не снизит напряжение в цепи до логического нуля?
насчет длины - да, именно, какие длинные провода (и какого типа - витую пару неэкранированную, например?) можно использовать для кнопок (и до кучи для оптрона/датчика холла?)
и еще, думаю как упростить себе все эти провода на бредборде - ведь по идее при пяти вольтах питании для работы конкретного индикатора нужны конкретные сопротивления - их можно вынести в один блок с самим индикатором, ведь так?
Резисторы не снижают
Резисторы не снижают напряжение! Напряжение на них падает согласно закону Ома, когда ток протекает через него. Когда транз закрыт - его сопротивление равно бесконечности, ток не течет, значит на резисторе +5В.
Желательно провода покороче, чтобы избавиться от наводок. У меня использовалась кнопка с проводами в пару метров, работала, больше - не проверял, но в теории должно работать. Провода для кнопок - какие есть. Желательно экранировать сигнальные проводники при большой их длине и обязательно заземлять экран с двух сторон.
Есть резисторные сборки в виде микросхемы, желательно при разработке устройства разбить устройства на функциональные блоки и делать их отдельно. Упроститься разработка и отладка.