Схема. Обмен информацией с USB HID устройством

Среди устройств, подключаемых к компьютеру по шине USB, имеется класс так называемых HID (Human Interface Devices) — устройств взаимодействия человека с компьютером. «Слабое звено» здесь — человек, поэтому выходящий за его возможности высокоскоростной обмен большими объемами информации не требуется. Это значительно упрощает как аппаратную, так и программную реализацию интерфейса подобных устройств.
Хотя первоначально к HID были отнесены компьютерные клавиатуры, мыши, джойстики и другие устройства ручного ввода информации, сегодня по тем же принципам подключают к разъему USB компьютера программаторы и многие другие изделия. Облегчает эту задачу наличие доступных (их легко найти в Интернете) программных драйверов для встраивания в компьютерные и микроконтроллерные программы.
В предлагаемой статье автор на примере разработанных им демонстрационного HID на микроконтроллере ATmega88 и взаимодействующей с ним компьютерной программы рассказывает, как организовать и использовать связь HID с компьютером по интерфейсу USB.

Демонстрационное микроконтроллерное HID (далее по тексту — устройство) собрано на макетной плате по схеме, изображенной на рис. 1. Оно способно принимать по шине USB команды на включение и выключение светодиодов HL2 и HL3 и передавать по той же шине информацию о состоянии кнопок SB1 и SB2. Используется режим LS (Low Speed) шины USB — скорость до 1,5 Мбит/с, предусмотренный как спецификацией USB 2.0 [1], так и более старой USB 1.1 [2]

В режиме LS допускается соединение устройства с компьютером дешевым неэкранированным USB-кабелем На одном конце стандартного кабеля имеется вилка USB-AM, подключаемая к компьютеру, а на другом — вилка USB-BM для соединения с устройством, оборудованным розеткой USB-BF (XS1).
Поскольку уровни сигналов, подаваемых на сигнальные линии D+ и D-, не должны превышать 3,3 В, а микроконтроллер DD1 питается напряжением 5 В, предусмотрены ограничивающие напряжение на этих линиях стабилитроны VD1 и VD2 Резисторы R1 и R2 не только служат элементами ограничите лей, но и устраняют колебания («звон») на перепадах сигналов, принимаемых микроконтроллером.
С помощью резистора R4 на линии D- устанавливается уровень напряжения, по которому контроллер USB компьютера опознает подключенное устройство. Микроконтроллер DD1 после получения питания с линии Vbus или в случае принудительной установки в исходное состояние устанавливает на своем выводе РС2 и на контакте 2 разъема XS1 низкий уровень, означающий для контроллера USB, что устройство не подключено, а после небольшой паузы — высокий (подключено низкоскоростное устройство) Без этого устройство иногда опознается компьютером некорректно

Светодиод HL1 сигнализирует о наличии напряжения питания микроконтроллера. Конденсаторы С1 и С2 — блокировочные в цепи питания. Проводами, заканчивающимися стрелками (в верхнем правом углу схемы), микроконтроллер DD1, не извлекая из устройства, подключают к программатору для загрузки программы Конфигурация микроконтроллера должна соответствовать показанной на рис. 2 (изображен фрагмент окна «Configuration and Security bits» программы PonyProg).
Программа, исполняемая микроконтроллером DD1, содержит драйвер USB, разработанный Кристианом Старкйоганом (Christian Starkjohann) из фирмы OBJECTIVE DEVELOPMENT Software GmbH, распространяемый бесплатно. Он написан в основном на языке С, а входящий в нее обработчик внешнего прерывания, обеспечивающий прием и передачу сигналов по шине USB, — на языке ассемблера. Автору драйвера удалось реализовать программное декодирование сигналов «на лету» в реальном масштабе времени. Об этом имеется интересная статья [3].

Принятый в USB способ передачи двоичных последовательностей NRZI (Non Return to Zero Invert — без возврата к нулю с инвертированием) иллюстрируют графики на рис. 3. При передаче каждого нуля текущий уровень сигнала инвертируется, а при передаче единицы он остается неизменным Когда таким способом передается длинная последовательность нулей, уровень сигнала изменяется с каждым новым разрядом, так что синхронизация приемника не представляет трудности. Но когда передается длинная последовательность единиц, изменения сигнала отсутствуют и синхронизация легко сбивается. Эту проблему решают битстаффингом — после каждых шести единиц подряд в последовательность вставляют незначащий ноль, чем принудительно вызывают изменение уровня. Из принятой последовательности такие нули удаляют.

Программа для микроконтроллера написана на языке С в интегрированной среде разработки AVR STUDIO 4.14.589 с установленным плагином AVR GCC. Плагин встраивается в среду разработки после установки свободно распространяемого пакета программ WinAVR.
Важная часть микроконтроллерной программы — дескриптор сообщения, содержащий информацию об устройстве, передаваемую им компьютеру. Дескриптор описываемого устройства (он находится в файле out_data.c) создан с помощью бесплатно распространяемой программы HID Descriptor Toolwww.download.intel.com/intelpress/usb/Examples/ZipFiles/DT.zip Описание дескриптора в программе представлено в табл. 1.

USAGE PAGE и USAGE — эти элементы определяют назначение устройства. Программа HID Descriptor Tool предлагает много вариантов для стандартных устройств, для нестандартного же выбираем Vendor Defined Page 1 и Vendor Usage 1 — назначение задает поставщик устройства.
COLLECTION — объединяет следующие за ним элементы в группу, которую замыкает элемент END COLLECTION. Группа может быть прикладной (application), физической (physical) или логической (logical), как в рассматриваемом случае.
REPORT ID — идентификатор сообщения, указано его значение (номер сообщения) 0x77. В данном случае идентификатор единственный, но бывают устройства, где необходимо наличие нескольких. Например, в статье [4] описан адаптер для подключения к компьютеру по шине USB четырех джойстиков от игровых приставок. В его дескрипторе четыре идентификатора — для каждого джойстика.
USAGE — следуя за идентификатором, этот элемент задает начало «конечной точки» — буфера длиной в несколько байтов, где устройство временно хранит информацию, принятую от компьютера или ожидающую передачи ему. Каждое USB-устройство имеет хотя бы одну конечную точку, которая предназначена для передачи его типа и параметров, инициализации и конфигурирования. Кроме нулевой, устройства могут иметь дополнительные конечные точки В частности, одну или две низкоскоростные. В рассматриваемом дескрипторе описаны дополнительные входная конечная точка INPUT для информации, предназначенной компьютеру, и выходная конечная точка OUTPUT для информации, принятой от него.
За элементом USAGE следует описание свойств конечной точки INPUT (элемент с таким названием завершает их список). Элементы LOGICAL_MINIMUM и LOGICAL_MAXIMUM задают допустимый интервал хранящихся в буфере конечной точки значений. В данном случае это 0 и 1 чего вполне достаточно для передачи состояния кнопок устройства. Элемент REPORT COUNT задает число двоичных разрядов в элементе данных конечной точки, a REPORT SIZE — число элементов такой размерности, умещающихся в буфере. Конечная точка INPUT имеет буфер рассчитанный на три восьмиразрядных слова (байта).

Следующий элемент USAGE обозначает начало описания свойств конечной точки OUTPUT. Для нее определены другие пределы значений (от 0 до 255), а размер ее буфера — восемь байтов.
Созданный дескриптор можно проверить средствами программы НЮ Descriptor Tool. Предоставляется возможность сохранить дескриптор в нескольких форматах, один из которых — заголовочный файл языка С. Вместе с программой поставляется набор готовых дескрипторов для различных стандартных USB HID устройств.

Обмен данными с устройством ведется по прерываниям, что отражено в дескрипторах конечных точек, описанных в файле usbdrv.c (табл. 2). Этот фрагмент программы компилируется условно, только при ненулевом значении константы USB_CFG_HAVE_INTRIN_ ENDPOINT В данном случае в файле usbconfig.h ей присвоено значение 2 (кроме нулевой, имеются еще две конечные точки).
Число 7 — длина дескриптора в байтах. Константа USBDESCR_ENDPOINT заданная в файле usbdrv.h, означает, что данный дескриптор описывает конечную точку. Число 0x81 показывает, что эта точка — входная номер 1, а 0x03 означает, что она предназначена для обмена информацией по прерываниям Число 8 задает максимально возможную длину передаваемого пакета в байтах. Константа USB_CFG_INTR_POLL_ INTERVAL определена в файле usbcon-fig.h со значением 10 — это период опроса устройства компьютером в миллисекундах.
В следующем дескрипторе число 0x01 говорит о том, что описывается выходная конечная точка номер 1, остальные параметры аналогичны описанным выше.
Для приема информации от компьютера в программе микроконтроллера имеется функция usbFunctionWriteOut, которая определена в файле main.с. Она помещает принятую по шине USB информацию в массив in_data. Вызов этой функции происходит из описанной в файле usbdrv.c функции usbProcessRx.
Каждая транзакция (цикл обмена информацией) состоит из трех пакетов. Первый пакет посылает компьютер. Это маркер (Token Packet), в котором содержатся сведения о типе и направлении передачи, адресе устройства и номере конечной точки. Формат этого пакета в режиме LS показан в табл. 3 [2, 3]. Передаваемые двоичные разряды образуют непрерывную последовательность, начинающуюся с младшего разряда поля SYNC (синхронизация). Значение этого поля — 0x80. Почему выбрано именно оно, становится ясным из рис. 4.

Поле PID — идентификатор пакета (Packet Identifier), причем его четыре старших разряда образованы поразрядной инверсией четырех младших (табл. 4). Именно при указанном здесь значении PID (ОхЕ1) программа микроконтроллера вызывает, как показано в табл. 5, упоминавшуюся выше функцию usbFunctionWnteOut.

Семиразрядное поле адреса (ADDR) позволяет адресовать до 127 «функций», подключенных к шине USB устройств и их отдельных блоков с адресами 0x1—Ox7R Еще один адрес, нулевой, используется только при конфигурировании. В рассматриваемом случае адрес единственной функции описанного выше HID — 0x77. Он указан в дескрипторе сообщения.
Четырехразрядное поле ENDP задает номер (от 0 до 15) выходной конечной точки. Здесь он равен 1. Далее следует CRC5 — пятиразрядный циклический избыточный код полей ADDR и ENDR служащий для контроля правильности приема их содержимого. Завершает пакет интервал ЕОР (End of Packet — конец пакета) длительностью минимум в два тактовых интервала.

Второй пакет, называемый Data Packet (пакет данных), также посылает компьютер. Его формат показан в табл. 6. Поле SYNC здесь такое же, как в пакете-маркере. В поле PID четных пакетов содержится код ОхСЗ, а нечетных — Ох4В Поле DATA несет восемь байтов информации, пересылаемой в адресованную маркером конечную точку, и сопровождается 16-разрядным на этот раз циклическим избыточным кодом.
Завершает транзакцию третий пакет, посылаемый устройством компьютеру (табл. 7) и сообщающий о ее результате в поле PID: OxD2 (ACK) — подтверждение приема. Ох5А (NAC) — неподтверждение приема, в буфере конечной точки еще имеется необработанная информация, 0x1 Е (STALL) — обнаружена ошибка.
Ограниченный объем журнальной статьи не дает возможности описать обмен по шине USB более подробно. Необходимые сведения можно найти как в спецификациях [1, 2], так и в статьях [3, 5, 6].

При разработке программы пришлось столкнуться с особенностями программирования сторожевого таймера микроконтроллера ATmega88 с помощью компилятора AVR GCC. Если для установки нужной выдержки этого таймера использовать последовательность команд, представленную в табл. 8, то оказывается, что значение разряда WDP3 в регистре WDTCSR остается нулевым и сторожевой таймер по-прежнему перезапускает микроконтроллер каждые 16 мс.

Чтобы определить, почему так происходит, пришлось дизассемблировать фрагмент созданного компилятором программного кода Результат показан в табл. 9. В отличие от микроконтроллера ATmega88, у которого регистр управления сторожевым таймером расположен в адресном пространстве ввода/вывода, регистр управления и состояния сторожевого таймера WDTCSR микроконтроллера ATmega88 находится в адресном пространстве дополнительных регистров ввода/вывода, и доступ к нему возможен только как к ячейке памяти.

По этой причине компилятор задает адрес регистра WDTCSR в регистре Z (R30 — младший байт, R31 — старший байт) и лишь затем заносит необходимое значение выдержки в регистр общего назначения. Только на пятом такте происходит запись этого значения в регистр WDTCSR. А это недопустимо, в описании микроконтроллера ATmega88 [7] прямо сказано, что после записи единиц в разряды WDCE и WDE новое значение выдержки необходимо задать в течение четырех тактов, пока не произошло аппаратное обнуление разряда WDCE.

Чтобы выполнить это условие, следует задавать режим сторожевого таймера последовательностью команд, приведенной в табл. 10. Новое значение выдержки будет записано уже на третьем такте, разряд WDP3 примет значение 1 и сторожевой таймер станет перезапускать микроконтроллер каждые 4 с. В этом можно убедиться, если в бесконечном цикле опроса состояния кнопок закомментировать команду asm(«wdr»), повторно скомпилировать программу и загрузить результат в микроконтроллер. В окне рассмотренной ниже программы USB_HID.exe можно будет наблюдать, как каждые 4 с устройство станет отключаться от шины USB и после небольшой паузы подключаться к ней заново.

Приложенная к статье компьютерная программа USB_HID.exe демонстрирует обмен информацией между компьютером и описанным устройством. Она разработана в среде визуального программирования C++ Builder 6 с использованием компонента TJvHidDeviceController, созданного Робертом Мартином Марк-вардтом (Robert Martin Marquardt) из международного сообщества программистов Project Jedi. Без этого компонента, установленного в среде программирования, исходный текст программы будет компилироваться с ошибкой.

Для его установки необходимо запустить на компьютере среду C++ Builder 6, распаковать в любом удобном месте архив   HIDKomponente.zip , в папке HIDKomponenteBCB выбрать пакетный файл HidBcb.bpk и запустить его. В среде C++ Builder 6 откроется окно Диспетчера Пакетов (Package Manager) с именем Package HidBcb.bpk. В нем видны две папки: Contains — содержимое пакета устанавливаемого компонента и Requires — список пакетов, необходимых для поддержки компонента в операционной системе. В верхней части окна следует щелкнуть левой кнопкой мыши по экранной кнопке Install, проект компонента откомпилируется и появится сообщение о том, что он установлен и зарегистрирован. Теперь, выполнив команду Component Configure Palette из главного меню среды C++ Builder, откроем окно Palette Properties, где в левой части виден список всех установленных компонентов.

Переместившись с помощью полосы прокрутки в нижнюю часть списка, можно увидеть название закладки нового компонента в палитре компонентов — Project JEDI. После щелчка левой кнопкой мыши по названию закладки в правой части окна появится изображение пиктограммы компонента и его название — TJvhiddevicecontroller. Экранной кнопкой Move Up в нижней части окна можно переместить закладку вверх по списку, чтобы она стала более удобной для поиска и доступа. Закладку Project JEDI и пиктограмму этого компонента можно увидеть и в палитре компонентов среды C++ Builder.

Теперь можно распаковать архив   USB_HID.zip   и в папке с таким же названием двойным щелчком левой кнопкой мыши по файлу USB_HID.bpr открыть в среде разработки проект программы. Ее исходный текст сосредоточен в файле UnUSBHID.cpp.
В начале этого файла описаны обработчики событий — щелчков по трем экранным кнопкам, имеющимся в окне программы. При наступлении любого из них в буфер поочередно записываются идентификатор сообщения устройства REPORTJD = 0x77 и числа 0x55, ОхАА (команды включения свето-диодов в устройстве). Если устройство подключено к шине USB компьютера, процедура WriteFile(BufOut, ToWrite, Written) передает эту информацию его выходной конечной точке. Параметр ToWrite указывает, сколько байтов следует передать в буфер BufOut, а в параметре Written функция возвращает число реально переданных байтов.

Функция JvHidDeviceControllerl Enumerate выполняет перечисление всех устройств, подключенных в данный момент к шине USB, если произошло подключение к шине устройства, уже зарегистрированного в операционной системе (ОС) компьютера или отключение ранее подключенного устройства. В программе присутствует невидимый в ее окне компонент ListBoxl типа TListBox, хранящий список подключенных устройств. Если устройство имеет название, оно добавляется в список. Если название обнаруженному устройству не присвоено, в список заносятся его VID (идентификатор производителя) и PID (идентификатор устройства). Нужное устройство выбирается из списка по его названию, заданному строкой программы S=»USB устройство обмена данными».

В архиве имеется также вариант рассматриваемой программы USBHIDWind. ехе с видимым окном компонента ListBoxl, что позволяет пользователю просматривать список подключенных к шине USB устройств и наблюдать за его изменениями при подключении или отключении устройств.
Функция JvHidDeviceControllerl Device Change выполняет подготовку и обновление списка устройств она же выводит сообщение об отсутствии необходимого.
Функция ShowRead заносит информацию, полученную от устройства, в массив Р и определяет, какие кнопки устройства нажаты В окно программы выводятся сообщения: «Белая кнопка = х» и «Черная кнопка = х», где х — 0 или 1 в зависимости от состояния кнопок.
Функция JvHidDeviceControllerl Device DataError обнаруживает ошибки приема информации от устройства и выводит сообщения о них.

При первом соединении изготовленного устройства с компьютером ОС Windows находит его и выдает на экран сообщение: «Найдено новое устройство — USB-устройство обмена данными». Вскоре это сообщение сменяется другим: «Найдено новое устройство — USB HID совместимое устройство». Для принятия решения о том, какой драйвер назначить найденному устройству, ОС использует INF-файл (в Windows XP он называется mput.inf) После того как ОС самостоятельно найдет и установит драйвер, появится следующее сообщение: «Новое оборудование установлено и готово к использованию» Теперь, пройдя по пути «Пуск—»Настройка-»Панель управления -> Система —> Оборудование -> Диспетчер устройств», можно увидеть обнаруженное устройство в списке зарегистрированных в системе. Его название выведено во множественном числе: «Устройства HID (Human Interface Devices)», потому что устройство состоит как бы из двух частей — HID интерфейса и пользовательского интерфейса Обмен информацией происходит со второй частью устройства. Наведя на строку «USB HID совместимое устройство» указатель мыши и щелкнув один раз. в открывшемся меню выберем «Свойства» и на закладке «Общее» увидим: «Размещение О (USB устройство обмена данными)».

Когда устройство подключено к шине USB, окно программы USB_HID.exe выглядит, как показано на рис. 5. Если же устройство не подключено или ОС не смогла его обнаружить, на месте надписей, отображающих состояние кнопок, будет выведено: «Устройство обмена данными не подключено»
Если нажать на кнопку SB1 (в изготовленной конструкции она черного цвета), то включится светодиод HL2 зеленого цвета свечения, а в окне программы синяя надпись «Черная кнопка = 0» сменится красной «Черная кнопка = 1» и останется такой до отпускания кнопки. Нажатие на белую кнопку SB2 приведет к включению желтого светодиода HL3, что будет аналогичным образом отображено в окне программы.
При нажатии на экранную кнопку «Зеленый светодиод» на некоторое время будет включен светодиод HL2 на плате устройства. Точно так же при нажатии на экранную кнопку «Желтый светодиод» поведет себя светодиод HL3, а при нажатии на кнопку «Зажечь оба» — HL2 и HL3 одновременно.

Прилагаемые архивы:
19_58_52__18_03_2010.zip
19_59_22__18_03_2010.zip
19_59_36__18_03_2010.zip
19_59_51__18_03_2010.zip

«Радио» № 3, 2010г.
С. СУРОВ, г. Нижний Новгород

Читайте также:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *