1 July 2010

Подключение поворотного энкодера к компьютеру через USB

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

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

image

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

image

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

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

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

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

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

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

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

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

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

image

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

image

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

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

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

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

image

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

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

[UPD120803]

Один грамотный человек собрал на микроконтроллере AVR регулятор громкости — USB HID устройство, эмулирующее стандартную мультимедийную клавиатуру (как предлагалось в комментариях). Такая клавиатура имеет возможность регулировать громкость без дополнительного программного обеспечения, всю необходимую работу выполняет драйвер операционной системы.

image

[Ссылки]

1. USB консоль для управления радиолюбительскими приборами.
2. Тут нашел код для энкодера. Тут авторское описание алгоритма.
3. Описание, как работать с микшером звуковой карты через mixerSetControlDetails.
4. Как работать с COM-портом в Windows.
5. Видеоролик, демонстрирующий работу регулятора громкости.
6. Исходники проекта ENCODER DEMO (проект AVRStudio и проект Visual Studio).
Tags:AVRUSBCDCV-USBencodermixer
Hubs: DIY
+68
27.4k 104
Comments 43
Popular right now
Инженер DevOps
from 140,000 to 160,000 ₽ЭкспобанкМоскваRemote job
Аккаунт-менеджер
from 40,000 ₽JetStyleЕкатеринбург
Программист 1С
from 150,000 ₽LITOKOLМосква
Бэкенд-разработчик (Ruby on Rails)
from 120,000 ₽FunBoxМоскваRemote job
Бэкенд-разработчик (Ruby on Rails)
from 120,000 ₽FunBoxНовосибирскRemote job
Top of the last 24 hours