Энкодер для частотника по своему внешнему виду похож на переменный резистор или на потенциометр. Те же три вывода, тот же корпус панели. На этом его сходство заканчивается. Внутри у него два переключателя, у которых есть общий вывод задач управления и два своих.

Чтобы энкодер заработал, средний вывод нужно подключить к земле, а два остальных через резисторы к питанию. Съем сигнала управления нужно производить непосредственно с выводов панели энкодера.

Теперь представим, что энкодер идеальный и его контакты не страдают дребезгом. Подключим к выводам энкодера осциллограф и начнем вращать ручку энкодера. Импульсы будут сдвинуты относительно друг друга на 90 градусов. Если крутить ручку мощности (кВт) вправо, влево или назад, то будем иметь последовательности панели управления:

Если осциллограммы как применение последовательности задач логических нулей и единиц, то они будут иметь такой вид:

Возьмем обычный энкодер, у которого есть дребезг контактов. Зона дребезга:

При переключении с логической единицы на логический ноль возникает дребезг. С дребезгом можно бороться двумя способами: аппаратным и программным применением.

Аппаратный способ – это подключение серии конденсаторов частотника, триггеров Шмитта, как указано на схеме панели управления:

Рекомендуется применять метод борьбы с дребезгом – программный. Такой метод описан в библиотеке Ротери. Данная библиотека содержит несколько функций, которые нужны для настройки выводов векторного контроллера на ввод, и подключение подтягивающих мощность (кВт) резисторов. В библиотеке нужно указывать соответствующие команды и задачи. Данной командой включается подтягивающий резистор внутри .

Функция серии Get position vfd возвращает значение энкодера. Данная фукнция нужна для получения количества импульсов, которые считал энкодер. Функция set Position vfd нужна для загрузки значения, с которого энкодер начнет свой счет.

Функция tick должна быть рассмотрена подробнее. Переменные этой функции sig1 и sig2 записывают состояние векторного pin, к которой подключен энкодер. Дальше эти pin записываются в переменную thisState vfd, которая является текущим состоянием энкодера. Если текущее состояние энкодера не равно предыдущему, то вычисляются новые направления счета и количество импульсов мощности сохраняется в переменной Position. Когда энкодер вернется в свое начальное векторное положение, произойдет сдвиг вправо на два разряда, и новое значение управления нужно записать в переменную PositionExt. Данная переменная нужна для сохранения серии результатов задач, которые будут иметь применение в основной программе.

Счет

Проанализировав состояние энкодера при вращении влево и вправо, составляем таблицу:

Его начальное положение 1-1. При повороте вправо произошел щелчок, единица стала логическим нулем. Новое значение this State vfd равно 01. Согласно команды данный результат суммируется со значением переменной Position.

Из-за того, что произошел дребезг, позиция стала 11, после перерасчета порядковый номер стал 7. После того, как дребезг закончился, нужно фиксировать новое положение 01 и к предыдущему нулю добавляется единица. При повороте энкодера произошел один щелчок, и значение переменной Position стало единицей.

Происходит второй щелчок при повороте энкодера направо, и вместо позиции 01 мы имеем позицию 00. После того, как весь дребезг закончится, на выходе управления также имеем значение единицы. При четвертом щелчке, когда позиция с 10 стала 11, мы имеем значение 6. После окончания дребезга остается 6.

В некоторых энкодерах имеет применение кнопка панели. При ее нажатии и отпускании тоже будет дребезг контактов, нужно применить библиотеку Bounce. Функции этой библиотеки нужны для задания pin, к которому будет подключена кнопка, задач времени задержки в миллисекундах. Если произошло нажатие на кнопку, то функция мощности (кВт) возвращает векторное значение true, если нет, то false vfd.

Принципиальная схема подключения энкодера к преобразователю частоты

В станкостроении энкодеры широко применяются для преобразователей частоты асинхронных двигателей. Они монтируются как датчики обратной связи по своей скорости. Такие энкодеры имеют большую дискретность от 100 импульсов на оборот до 1 млн импульсов на оборот. У этой марки дискретность равна 500 имп. на оборот.

Энкодеры подразделяются на виды задач по . Они бывают абсолютными и инкрементальными. Наш энкодер выполняет обычную функцию – выдает сигнал дифференцирования при отключении мощности питания, и ее подачи снова. Раннее состояние не сохраняется.

Энкодеры абсолютного вида имеют внутреннюю память, которая помнит последние положения. Зачем нужна память, и зачем сохранять эти данные? В заводских условиях станкостроения перед перемещением определенного устройства в первую очередь указывают нулевую точку. Такой процесс называется реферированием, то есть, выход в нуль.

Применение датчика абсолютного вида дает возможность уйти от этой процедуры на второй раз, сократить время при условии, что система имеет ограничения для перемещений.

Рассмотрим энкодеры синуса и косинуса. Они выдают выходной сигнал косинуса или синуса. Далее, с помощью устройства интерполятора мощности образуют из них импульсы. Сигналы такого вида можно изменять в размерах. Питание энкодера осуществляется от напряжения 5 вольт.

Сигнал «А» — это сигнал импульса прямого типа. Количество импульсов с этого сигнала приходит на каждом обороте. Оно равно 500 (дискретность датчика).

Сигнал «В» — тоже прямой сигнал импульса. С него на каждом обороте поступает число импульсов по дискретности датчика, который смещен от канала «А» на 90 градусов (500).

Сигнал «R» — это сигнал метки «нуль». С одного оборота датчика получается один импульс.

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

Подключение

Подключение простое. Подсоединяем напряжение 5 вольт на выходы энкодера. У нас раскладка: провод коричневого цвета – 0 В, белого цвета — +5 В, розовый, зеленый и красный – А, В, R.

Программа подключения энкодера базируется на прерываниях каналов А и В. Срабатывания прерываний происходят на переднем фронте. Получается ситуация, когда происходит торможение энкодера в момент растрового пересечения и выходной сигнал канала всегда остается положительным. Подсчет импульсов непрерывно ведется счетчиком.

В нашем случае мы не будем применять прерывания, потому что мы работаем с 4-мя датчиками, они эксплуатируются одновременно. Если применять схему прерываний, наверняка возникнет ситуация потери импульсов. У нас эта проблема решается путем установления значка наличия движения. А мы рассматривали эксплуатацию энкодеров промышленного назначения.

Работа счетчика импульсов на основе модуля энкодера

Счетчик работает в связке с модулем семиразрядного индикатора, который и будет отображать количество накрученных энкодером импульсов. При включении значение счетчика равно нулю.

Покрутим ручку энкодера по часовой стрелке. Значение счетчика инкрементируется на единицу при каждом щелчке энкодера. Наибольшее число можно накрутить 999999999. это число должно заполнить все разряды нашего семисегментного индикатора. Если вращать ручку дальше, то счетчик обнулится, начнет снова считать с нуля.

Для примера накрутим 120 импульсов. Теперь скручиваем обратно, вращая ручку против часовой стрелки. Центральная ось энкодера работает как кнопка. Она очищает от нулей свободные разряды индикатора. У кнопки есть небольшой дребезг контактов, поэтому выключение и включение происходит не сразу. Программным путем, дребезг устраняется. Это основа работы с модулем энкодера.

Коротко энкодеры можно назвать преобразователями угловых перемещений. Они служат для модификации угла поворота объекта вращения, например, вала какого-либо механизма, в сигнал электрического тока. При этом определяется не только угол поворота вала, но и его направление вращения, а также скорость вращения и текущая позиция относительно первоначального положения.

Наиболее популярными энкодеры стали при их использовании в системах точного перемещения, на станкостроительных заводах, в производственных комплексах с применением робототехники, в измерительных устройствах, в которых необходима регистрация точных измерений наклонов, поворотов, вращений и углов.

Виды и принцип действия

Энкодеры – это датчики поворота. Простейший датчик имеет ручку, которая может поворачиваться по часовой стрелке или против нее. В зависимости от угла поворота и направления выдается цифровой сигнал, информирующий о том, в каком положении находится ручка, либо в какую сторону она была повернута.

У такого энкодера, показанного на рисунке, ручка также может применяться в качестве кнопки. Это является вспомогательной функцией конкретного вида энкодера.

По типу выдаваемых данных энкодеры делятся на две большие группы:
  1. Абсолютные.
  2. Инкрементальные.
Абсолютные энкодеры

У абсолютного энкодера весь круг поворота разделен на определенное количество секторов, чаще всего одинакового размера. Эти сектора пронумерованы. Энкодер при работе выдает номер сектора, в котором на данный момент он находится. Поэтому он и называется абсолютным. У этого типа энкодера всегда можно определить, на какой угол относительно нулевого сектора повернут энкодер в конкретный момент, то есть, при повороте он выдает значения номеров секторов, до максимального значения. Далее он переходит снова на ноль.

Если вал энкодера поворачивать в другую сторону, то он начнет выдавать противоположные значения. В нашем случае у него используется пять выводов для выдачи значений поворота.

У данного алгоритма имеются свои недостатки. Из таблицы 1 виден порядок выдаваемых значений n-го энкодера. Стоит обратить внимание на две последние строчки, переход от 127 на 128.

Таблица 1

Здесь меняются абсолютно все биты. В идеальном энкодере они все меняются одновременно и нет никаких проблем. Практически в реальном энкодере биты меняются быстро, однако не одновременно. И в какой-то момент на выходе энкодера оказывается совершенно произвольное значение. Так как меняются все биты, следовательно, у энкодера будет произвольное значение от нуля до всех единиц.

Справа изображен пример такого переключения. Чем это может грозить? Разберем пример. Микроконтроллер с помощью двигателя управляет валом и поворачивает его на определенный угол. В определенный момент при переключении со 127 на 128 ячейку он получает определенное случайное значение. Контроллер делает вывод, что вал находится совершенно в другом месте, в отличие от фактического места, и начинает его вращать в другую сторону, с другой скоростью и т.д.

Через определенное время микроконтроллер получает правильное значение, начинает пытаться остановить вал и вращать его в правильную сторону. Такой процесс может продолжаться долго, при условии, что такая ошибка будет встречаться часто. Такие ошибки являются нерегулярными, и вычислить их достаточно сложно.

Код Грея

Выше описанная проблема решается с помощью введения кода Грея. Особенностью кода Грея является то, что при переключении энкодера на единицу, значение кода Грея меняется также на единицу. Меняется только один вид. Это видно в таблице 2 в сравнении двоичного кода и кода Грея.

Таблица 2

Первые две строчки совпадают, но уже во второй строчке поменялся средний бит. Далее также меняется один бит. Также стоит отметить, что последний и первый код Грея отличается на один бит, то есть код Грея может зациклиться.

Преимуществом данного кода является то, что ошибка, которая рассмотрена выше, невозможна. Из недостатков можно отметить, что микроконтроллеру необходимо переводить код Грея в двоичный код для того, чтобы понять, в каком положении находится абсолютный энкодер.

Инкрементальные энкодеры

Следующим типом является инкрементальный энкодер, который имеет более простую структуру. Но при этом он не показывает конкретное место положения своей ручки. Он показывает только направление поворота, а число делений поворота должен считать микроконтроллер.

У инкрементального энкодера есть набор полосок, которые по умолчанию подключены к земле, и при повороте они замыкаются и размыкаются. Получается сигнал, изображенный на рисунке (похож на меандр). Таких круговых полосок у энкодера две. Полоски смещены на одну четверть, и сигналы также смещены между собой на четверть. Это важно, так как позволяет определить направление вращения.

Схему инкрементального энкодера можно представить по правому рисунку. Кнопки обозначают периодические подключения энкодера к земле. Так как внутри энкодер не подключается к логической единице, то необходимо снаружи самостоятельно подтянуть логические единицы через резисторы к выводу энкодера. В этом случае, когда ни одна из ножек у энкодера не подключена к земле, на ножках будет логическая единица.

Если энкодер подключил к земле какую-то ножку, то на этой ножке будет логический ноль. В спокойном состоянии у энкодера на выходе логическая единица. При начале вращения энкодера в любую сторону, то сначала один вывод подключается к земле, затем другой. Далее эти выводы по очереди отключаются от земли, и на них опять образуется логическая единица.

Определить направление поворота можно по тому, какой из выводов раньше подключился к земле. При подсчете полных циклов можно посчитать количество щелчков поворота энкодера.

Фактически у энкодера имеется четыре состояния:
  1. Две единицы.
  2. Ноль и единица.
  3. Ноль и ноль.
  4. Единица и ноль.

Три состояния, которые не равны единицам, являются неустойчивыми, и в них энкодер не может находиться. Во многих микроконтроллерах реализована функция подсчета поворотов с помощью таймеров, у которых есть определенные входы. Таймер считает на аппаратном уровне, на сколько щелчков и в какую сторону был повернут энкодер, и выдает значение. То есть, счетчик инкрементирует какое-либо число.

По изменению этого числа можно определить, на сколько щелчков был повернут энкодер. По количеству щелчков можно определить и угол поворота. Энкодер также имеет дребезг контактов, который усложняет анализ сигналов.

Оптические энкодеры

Подобный преобразователь выполнен в виде диска, зафиксированного на валу, и изготовленного из стекла. Оптический датчик поворота отличается от других видов дополнительным оптическим растором, перемещаемым при повороте вала. При этом он превращает момент вращения в световой поток, который далее принимается фотодатчиком.

Оптический преобразователь запоминает углы вращения. При этом каждому отдельному положению соответствует особый цифровой код, который вместе с числом оборотов образует единицу измерения датчика. Энкодер подключается и работает по аналогии с инкрементальным датчиком.

По характеру функционирования они разделяются на фотоэлектрические и магнитные . Принцип работы магнитных основан на использовании , который был впервые открыт в 1879 году. При этом разность потенциалов появляется только при расположении провода постоянного тока в магнитное поле.

По точности и свойствам разрешения магнитный вид датчика уступает фотоэлектрическому, однако по конструкции он проще, менее требователен к условиям работы и пространству. Магнитный энкодер является прибором, который фиксирует прохождение магнитного полюса магнита при вращении, находящегося рядом с чувствительным элементом. Информация передатчика выражается в цифровом коде.

Фотоэлектрический энкодер является датчиком, работающим на основе фотоэлектрического принципа. Этот эффект наблюдается при воздействии светового потока на вещество. Этот принцип был открыт в 1887 году. При эксплуатации такого датчика происходит постоянное преобразование луча света в сигнал электрического тока.

Аналогами фотоэлектрического энкодера являются оптоэлектронный, оптический и . Эти датчики наиболее чувствительны к характеристикам изготовления, эксплуатации и другим факторам, по сравнению с другими моделями. Однако это оправдывается их повышенной точностью, в отличие от конкурентов.

Энкодер это всего лишь цифровой датчик угла поворота, не более того.

Энкодеры бывают абсолютные — сразу выдающие двоичный код угла и инкрементальные, дающие лишь указание на направление и частоту вращения, а контроллер, посчитав импульсы и зная число импульсов на оборот, сам определит положение.

Если с абсолютным энкодером все просто, то с инкрементальным бывают сложности. Как его обрабатывать?

С Энкодера выходят два сигнала А и В, сдвинутых на 90 градусов по фазе, выглядит это так:

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

Механический подключается совсем просто центральный на землю, два крайних (каналы) на подтянутые порты. Я, для надежности, подключил внешнюю подтяжку. Благо мне на для этого только парой тумблеров щелкнуть:


Оптический подключается в зависимости от типа оптодатчика, обычно там стоит два с общим анодом.

Обычно, все пытаются работать с ними через прерывания INT, но этот метод так себе. Проблема тут в дребезге — механические контакты, особенно после длительного пользования, начинают давать сбои и ложные импульсы в момент переключения. А прерывание на эти ложные импульсы все равно сработает и посчитает что нибудь не то.

Метод прост:
Подставим нули и единички, в соответствии с уровнем сигнала и запишем последовательность кода:


A:0 0 1 1 0 0 1 1 0 0 1 1 0
B:1 0 0 1 1 0 0 1 1 0 0 1 1

Если A и B идут на одни порт контроллера (например на A=PB0 B=PB1), то при вращении энкодера у нас возникает меняющийся код:

11 = 3
10 = 2
00 = 0
01 = 1
11 = 3

Теперь остается только циклически опрашивать наш энкодер сравнивая текущее состояние с новым и на основании этого делающего выводы о вращении. Причем частота опроса должна быть такой, чтобы не пропустить ни одного импульса. Например, мой EC12 имеет 24 импульса на оборот. Вращать его предпологается вручную и я вряд ли смогу вращать его с космической скоростью, но решил все же замерить. Подключился к осциллографу, крутнул ручку что есть мочи:

Выжал меньше килогерца. Т.е. опрашивать надо примерно 1000 раз в секунду. Можно даже реже, будет надежней в плане возможного дребезга. Сейчас, кстати, дребезга почти нет, но далеко не факт что его не будет потом, когда девайсина разболтается.

Сам опрос должен быть в виде конечного автомата. Т.е. у нас есть текущее состояние и два возможных следующих.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 // Эту задачу надо запускать каждую миллисекунду. // EncState глобальная переменная u08 -- предыдущее состояние энкодера // EncData глобальная переменная u16 -- счетный регистр энкодера void EncoderScan(void ) { u08 New; New = PINB & 0x03 ; // Берем текущее значение // И сравниваем со старым // Смотря в какую сторону оно поменялось -- увеличиваем // Или уменьшаем счетный регистр switch (EncState) { case 2 : { if (New == 3 ) EncData++; if (New == 0 ) EncData--; break ; } case 0 : { if (New == 2 ) EncData++; if (New == 1 ) EncData--; break ; } case 1 : { if (New == 0 ) EncData++; if (New == 3 ) EncData--; break ; } case 3 : { if (New == 1 ) EncData++; if (New == 2 ) EncData--; break ; } } EncState = New; // Записываем новое значение // Предыдущего состояния SetTimerTask(EncoderScan, 1 ) ; // Перезапускаем задачу через таймер диспетчера }

// Эту задачу надо запускать каждую миллисекунду. // EncState глобальная переменная u08 -- предыдущее состояние энкодера // EncData глобальная переменная u16 -- счетный регистр энкодера void EncoderScan(void) { u08 New; New = PINB & 0x03; // Берем текущее значение // И сравниваем со старым // Смотря в какую сторону оно поменялось -- увеличиваем // Или уменьшаем счетный регистр switch(EncState) { case 2: { if(New == 3) EncData++; if(New == 0) EncData--; break; } case 0: { if(New == 2) EncData++; if(New == 1) EncData--; break; } case 1: { if(New == 0) EncData++; if(New == 3) EncData--; break; } case 3: { if(New == 1) EncData++; if(New == 2) EncData--; break; } } EncState = New; // Записываем новое значение // Предыдущего состояния SetTimerTask(EncoderScan,1); // Перезапускаем задачу через таймер диспетчера }

Почему я под счетчик завел такую большую переменную? Целых два байта? Да все дело в том, что у моего энкодера, кроме импульсов есть еще тактильные щелчки. 24 импульса и 24 щелчка на оборот. А по моей логике, на один импульс приходится четыре смены состояния, т.е. полный период 3201_3201_3201 и один щелчок дает 4ре деления, что некрасиво. Поэтому я считаю до 1024, а потом делю сдвигом на четыре. Получаем на выходе один щелочок — один тик.

Скоростной опрос на прерываниях
Но это механические, с ними можно простым опросом обойтись — частота импульсов позволяет. А бывают еще и высокоскоростные энкодеры. Дающие несколько тысяч импульсов на оборот, либо работающие на приводах и вращающиеся очень быстро. Что с ними делать?

Ускорять опрос занятие тупиковое. Но нас спасает то, что у таких энкодеров, как правило, есть уже свои схемы подавления дребезгов и неопределенностей, так что на выходе у них четкий прямоугольный сигнал (правда и стоят они совершенно негуманно. От 5000р и до нескольких сотен тысяч. А что ты хотел — промышленное оборудование дешевым не бывает).

Так что без проблем можно применять прерывания. И тогда все упрощается неимоверно. Настраиваем всего одно прерывание по внешнему сигналу. Например, INT0 настраиваем так, чтобы сработка шла по восходящему фронту. И подаем на INT0 канал А.


Пунктиром показано предполагаемое положение в произвольный момент. Красные стрелки это фронты по которым сработают прерывания при движении либо в одну, либо в другую сторону.

А в обработчике прерывания INT0 щупаем вторым выводом канал В. И дальше все элементарно!

Если там высокий уровень — делаем +1, если низкий -1 нашему счетному регистру. Кода на три строчки, мне даже писать его лень.

Конечно, можно такой метод прикрутить и на механический энкодер. Но тут надо будет заблокировать прерывания INT0 на несколько миллисекунд. И НИ В КОЕМ СЛУЧАЕ нельзя делать это в обработчике.

Алгоритм прерывания с антидребезгом будет выглядеть так:

  • Зашли в обработчик INT0
  • Пощупали второй канал
  • +1 или -1
  • Запретили локально INT0
  • Поставили на таймер событие разрешающее INT0 через несколько миллисекунд
  • Вышли из обработчика

Сложно? Нет, не сложно. Но зачем? Проще сделать банальный опрос, как указано выше и не зависеть от выводов прерываний. Впрочем, хозяин барин.

Давно хотел приспособить к ноуту регулятор громкости, сделанный из энкодера . Подключать этот регулятор нужно будет к USB, чтобы все было «по-взрослому» (да и по-другому никак внешнее устройство к ноуту не подключишь). Крутим энкодер влево - громкость должна уменьшаться, вправо - должна увеличиваться. Жмем вниз ручку энкодера - запускаем какую-нибудь полезную программу, или переключаемся на регулирование тембра.

Для тех, кто не в курсе, что такое энкодер - это такая крутилка, типа ручки громкости на основе обычного резистора, только у этой крутилки нет граничных положений - крути сколько влезет в любую сторону. Крутится энкодер с приятными мягкими щелчками, а выглядит как обычный переменный резистор.

Такие устройства - не редкость в совремеменных автомагнитолах и любых бытовых устройствах, пользовательский интерфейс которых обрабатывается микроконтроллером (а это почитай любая бытовая техника), и где нужна плавная регулировка или настройка. В энкодер часто встраивают и третий контакт, работающий как кнопка на ручке - когда утапливаем ручку энкодера вниз (вдоль оси), то эта кнопка срабатывает. Очень обогащает возможности интерфейса с пользователем - на одном энкодере можно построить всю систему управления электронным устройством (зато добавляется гемор программисту, но это уже мелочи). У меня как раз и был такой энкодер.

Принцип работы энкодера довольно прост - в нем всего лишь два контакта (кнопка на ручке не в счет), которые начинают замыкать, как только пользователь начал крутить ручку энкодера. Контакты подключаются к двум ножкам микроконтроллера (работающих как цифровые входы), и при вращении ручки энкодера на этих ножках появляются импульсы, по фазе и количеству которых микроконтроллер определяет направление вращения и угол поворота ручки энкодера.

Чтобы заработал регулятор громкости, нужно решить, как минимум, три инженерные задачи:

Шаг 1 . Создание низкоскоростного USB-устройства на макетке.
Шаг 2 . Подключить к этому USB-устройству энкодер, добиться, чтобы микроконтроллер его отрабатывал, и передавал в компьютер информацию о вращении энкодера.
Шаг 3 . Разобраться, как можно программно управлять регулятором громкости. Наверняка есть какое-нибудь мультимедиа-API, которое позволяет это делать. Программа минимум - нужно написать программку, которая будет принимать сигналы от USB-устройства и управлять громкостью. Неплохо бы, конечно, написать драйвер, но за это браться страшновато. Лучше оставим на потом.

Итак, опишу процесс создания регулятора по шагам. Подробности опускаю, иначе будет слишком скучно. Кому интересно, см. исходники и документацию по ссылкам.

[Шаг 1. Создание низкоскоростного USB-устройства на макетке ]

Этот шаг прошел, даже не начавшись - как-то слишком просто и банально. Тупо скачал пример проекта по ссылке . Поправил файлик usbconfig.h - для понтов назвал мое устройство ENCODER DEMO , на большее фантазии не хватило. Проверил в Makefile тип проца (ATmega16), частоту кварца (16 МГц) - чтобы соответствовало моей макетке AVR-USB-MEGA16. Скомпилил проект в AVRStudio, прошил макетку, подключил к компьютеру - все завелось с полоборота, мое USB-устройство исправно заработало как виртуальный COM-порт - все в точности так, как написано в статье .

[Шаг 2. Подключить к USB-устройству энкодер ]

Этот шаг у меня вызывал самые большие опасения, что все заработает как надо. Что энкодер подключу и смогу его читать - в этом я не сомневался. Были сомнения, что смогу его считывать качественно, когда в фоне работает ещё и обработка протокола USB - все-таки это задача для микроконтроллера не из легких (как впоследствии оказалось - волновался я совершенно напрасно).

Как обычно, начал рыться в Интернете в поисках готовых подпрограмм для чтения энкодера. Нашел очень быстро то, что нужно - именно для AVR, очень простой код на C , файлики encoder.c и encoder.h. Что ни говори, а open source крутая штука.

Приделал два индикационных светодиода - ЗЕЛЕНЫЙ и ЖЕЛТЫЙ - для обозначения направления вращения энкодера. Подключил энкодер для удобства прямо к разъему ISP, воспользовавшись тем, что сигналы MOSI, MISO и SCK - это всего лишь ножки PB5, PB6 и PB7 микроконтроллера ATmega16 (подключил туда фазы A и B, а также кнопку энкодера).

Поправил определения ножек, добавил код инициализации. Присоединил к проекту модуль encoder.c. Добавил в главный цикл main управление зеленым и желтым светодиодами, когда приходит инфа с энкодера. КРАСНЫЙ светодиод привязал к кнопке энкодера - когда её нажимаем, красный светодиод зажигается, отпускаем - гаснет. Скомпилировал, прошил - работает. Кручу ручку влево, и в такт щелчкам энкодера вспыхивает зеленый светодиод. Кручу ручку вправо - вспыхивает желтый светодиод. Несмотря на то, что чтение энкодера происходит методом поллинга, благодаря эффективному коду к чтению энкодеру НИКАКИХ нареканий даже при одновременной работе с библиотекой V-USB (респект, Pashgan!). Добавил вывод информации от энкодера в виртуальный COM-порт (крутим энкодер влево вывожу в консоль минусики "-", крутим вправо вывожу в консоль плюсики "+"). По таймеру каждые 10 мс вывожу состояние кнопки энкодера и индицирую её красным светодиодом (кнопка нажата - передаю символ "1", отпущена - "0"). Все работает. Скукотища.

В заключение выкинул модули cmd.c, crc16.c, eepromutil.c, strval.c. Объем кода упал до 3 килобайт - отлично, теперь поместится и в память ATtiny45 (можно в будущем задействовать макетку AVR-USB-TINY45, она меньше по размерам и дешевле).

[Шаг 3. Разобраться, как можно программно управлять регулятором громкости ]

Как обычно, прогуглил вопрос. Отсеял кучу мусора, и наконец выгреб жемчужину - . Дальше дело техники. Достаю любимый детский конструктор - Visual Studio. Ни о чем не думая, визардом генерю dialog-based приложение. Бросаю на панель движок регулятора громкости, привязываю к нему переменную, добавляю обработчик положения движка. При старте приложения настраиваю движок на минимум 0 и максимум 65535 (чтобы соответствовало границам значения громкости, которым манипулируют библиотеки управления микшером). Считываю функцией mixerGetControlDetails текущее значение громкости, и ставлю движок регулятора в соответствующее положение. В обработчике положения движка все наоборот - читаю положение движка и функцией mixerSetControlDetails устанавливаю нужную громкость. Управление громкостью делаю в точности так, как написано в статье . Проверил - работает.

Теперь осталось дело за малым - читать, что приходит с виртуального COM-порта (на нём у нас висит свежеиспеченное USB-устройство с энкодером). Если пришел минусик (-) то двигаем движок влево (уменьшаем громкость), плюсик (+), то двигаем движок вправо (увеличиваем громкость). Если приходят символы 0 и 1, то соответственно управляем состоянием чекбокса (просто для индикации - нажата кнопка энкодера, или нет). С COM-портом можно работать, как с обычным файлом (см. ). Инициализируем подключение к COM-порту как открытие файла (вызовом ::CreateFile ) в блокирующем режиме. Запускаем отдельный поток, туда в бесконечный цикл добавляем чтение файла (блокирующим вызовом ::ReadFile ) по одному символу, и этот символ анализируем. По тому, какой символ пришел, крутим движок слайдера в нужную сторону (громкость будет регулировать обработчик слайдера) или обновляем состояние чекбокса. Проверил - работает.

Вот и все, собственно. Дальше можно заниматься бесконечным (и, наверное, бесполезным) улучшательством. Сделать автоматический поиск нужного виртуального COM-порта (сейчас для упрощения имя COM-порта передается через командную строку). Переделать USB-устройство с CDC -класса на HID - это может упростить код USB-устройства, а также упростить программный поиск и открытие устройства на компьютере по VID и HID. Или написать вместо программы сервис (чтобы не надо было запускать отдельную программу). Или даже драйвер. Это очень интересно, но не умею (может, кто из хабравчан научит уму-разуму?..). Прикрутить к кнопке энкодера какое-нибудь действие. Ну и так далее до бесконечности.

Надеюсь, кому-нибудь мои изыскания пригодятся в собственных разработках. Если чего-нибудь упустил, буду рад выслушать замечания в комментариях.

[UPD120803 ]

Один грамотный человек собрал на микроконтроллере AVR

Принцип действия, схема включения и исходник библиотеки для работы с инкрементным энкодером уже рассматривался мной в одной из статей. Сегодня мы поговорим о практическом применении энкодера. В качестве примера я выбрал программу генератора прямоугольного сигнала с диапазоном рабочих частот 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<TCCR1B = (0<

//настройка вывода PD5 на выход
SetBit(PORTD, PD5);
SetBit(DDRD, PD5);

//ничего не делаем в бесконечном цикле
while (1);
return 0;
}

Думаю, пояснения требует только кусок инициализации таймера.

Обнуление счетного регистра
TCNT1 = 0;

Инициализация конфигурационных регистров таймера Т1.
TCCR1A = (0<TCCR1B = (0<

Где биты WGM13, WGM12, WGM11, WGM10 задают режим работы таймера – СТС,
CS12, CS11, CS10 – определяют коэффициент предделителя таймера –256,

COM1A1, COM1A0 – определяют поведение вывода PD5(OC1F) – в данном случае по сигналу таймера он будет менять свое состояние на противоположное


Инициализация регистра совпадения начальным значением.
OCR1A = timerValue;

Компилируем программу и грузим в микроконтроллер. Светодиод должен моргать с частотой 1 Гц.
В программе нет никаких прерываний. Нет никаких манипуляций с выводом PD5. Однако светодиод моргает!

Программа

Теперь нужно “прикрутить” к этой программе энкодер. Зададим настройки в хедер файле encoder.h – порт и выводы, к которым подключен энкодер, значения констант.


#define PORT_Enc PORTA
#define PIN_Enc PINA
#define DDR_Enc DDRA
#define Pin1_Enc 2
#define Pin2_Enc 1

#define RIGHT_SPIN 0x01
#define LEFT_SPIN 0xff

Хедер содержит прототипы трех функций. Вспомним их назначение.

void ENC_InitEncoder(void) настраивает выводы микроконтроллера, к которым подключен энкодер на вход. Эту функцию нужно вызвать в начале main`а.


void ENC_PollEncoder(void) – однократно опрашивает энкодер, анализирует текущее и предыдущее состояния и записывает в буфер соответствующие константы (RIGHT_SPIN и LEFT_SPIN). Эта функция будет сидеть в прерывании таймера Т0.


unsigned char ENC_GetStateEncoder(void) – возвращает содержимое буфера энкодера. Если поворот на одну позицию не был зафиксирован – функция вернет 0, если поворот был зафиксирован функция вернет значение соответствующей константы. При этом значение буфера очистится. Эта функция будет вызываться в основном программе – в цикле while.


Дополняем нашу программу. Можете попробовать сделать это самостоятельно.

//программирование AVR на Си
//пример использования энкодера
//сайт 17.10.09

#include
#include
#include "encoder.h"
#include "bits_macros.h"
#include "timer_value.h"

#define TCNT0_const 253
#define TCCR0_const 5

volatile unsigned char pTimerValue = 0;

int main(void )
{
ENC_InitEncoder();

//инициализация таймера т0
TCNT0 = TCNT0_const;
TCCR0 = TCCR0_const;

//инициализация таймера т1
TCNT1 = 0;
TCCR1A = (0<TCCR1B = (0<OCR1A = timerValue;

//разрешение прерываний от таймеров
//т0 - по переполнению, т1 - по совпадению

TIMSK = (1<

//настраиваем PD5 на выход
SetBit(PORTD, PD5);
SetBit(DDRD, PD5);

__enable_interrupt ();
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();
}

#pragma vector=TIMER1_COMPA_vect
__interrupt void timer1_compa_my(void )
{
//обновляем значение регистра стравнения
OCR1A = timerValue;
}

Вроде все должно быть понятно.
Кусок кода, в котором изменяется значение pTimerValue, можно было бы написать еще так:

if (stateEnc != 0) {
pTimerValue = pTimerValue + stateEnc;
if (pTimerValue == (MAX_TIM_VALUE + 1)) pTimerValue = MIN_TIM_VALUE;
else if (pTimerValue == (MIN_TIM_VALUE - 1)) pTimerValue = MAX_TIM_VALUE;
}

При вращении энкодера вправо pTimerValue складывается с 1, то есть инкрементируется.

При вращении энкодера влево pTimerValue складывается с 0хff, что равносильно вычитанию 1. Одна и та же операция, а результат прямо противоположный.

Эта статья также доступна на следующих языках: Тайский

  • Next

    Огромное Вам СПАСИБО за очень полезную информацию в статье. Очень понятно все изложено. Чувствуется, что проделана большая работа по анализу работы магазина eBay

    • Спасибо вам и другим постоянным читателям моего блога. Без вас у меня не было бы достаточной мотивации, чтобы посвящать много времени ведению этого сайта. У меня мозги так устроены: люблю копнуть вглубь, систематизировать разрозненные данные, пробовать то, что раньше до меня никто не делал, либо не смотрел под таким углом зрения. Жаль, что только нашим соотечественникам из-за кризиса в России отнюдь не до шоппинга на eBay. Покупают на Алиэкспрессе из Китая, так как там в разы дешевле товары (часто в ущерб качеству). Но онлайн-аукционы eBay, Amazon, ETSY легко дадут китайцам фору по ассортименту брендовых вещей, винтажных вещей, ручной работы и разных этнических товаров.

      • Next

        В ваших статьях ценно именно ваше личное отношение и анализ темы. Вы этот блог не бросайте, я сюда часто заглядываю. Нас таких много должно быть. Мне на эл. почту пришло недавно предложение о том, что научат торговать на Амазоне и eBay. И я вспомнила про ваши подробные статьи об этих торг. площ. Перечитала все заново и сделала вывод, что курсы- это лохотрон. Сама на eBay еще ничего не покупала. Я не из России , а из Казахстана (г. Алматы). Но нам тоже лишних трат пока не надо. Желаю вам удачи и берегите себя в азиатских краях.

  • Еще приятно, что попытки eBay по руссификации интерфейса для пользователей из России и стран СНГ, начали приносить плоды. Ведь подавляющая часть граждан стран бывшего СССР не сильна познаниями иностранных языков. Английский язык знают не более 5% населения. Среди молодежи — побольше. Поэтому хотя бы интерфейс на русском языке — это большая помощь для онлайн-шоппинга на этой торговой площадке. Ебей не пошел по пути китайского собрата Алиэкспресс, где совершается машинный (очень корявый и непонятный, местами вызывающий смех) перевод описания товаров. Надеюсь, что на более продвинутом этапе развития искусственного интеллекта станет реальностью качественный машинный перевод с любого языка на любой за считанные доли секунды. Пока имеем вот что (профиль одного из продавцов на ебей с русским интерфейсом, но англоязычным описанием):
    https://uploads.disquscdn.com/images/7a52c9a89108b922159a4fad35de0ab0bee0c8804b9731f56d8a1dc659655d60.png