Разработка микроконтроллерных USB-устройств в среде BASCOM


Читателям предлагается созданный автором статьи программный драйвер USB, позволяющий подключать к этой шине микроконтроллерные устройства, создавая программы для них в среде разработки BASCOM для AVR. Приведён пример такого устройства, которое при установке в нём датчика температуры и небольшой модификации программы превращается из чисто демонстрационного в USB-термометр, показания которого выводятся на экран компьютера.

Спецификацией USB (Universal Serial Bus — универсальная последовательная шина) определено, что среди одновременно подключённых к ней устройств только одно может быть ведущим, в роли которого чаще всего выступает компьютер, а все остальные — ведомые им периферийные устройства. Обмен информацией производится только по инициативе ведущего. Периферийные устройства не могут его начинать сами.

Электрические сигналы, несущие информацию, передаются по паре проводов D- и D+, образующим двунаправленную двухпроводную линию связи. Используется дифференциальный способ передачи — сигналы в проводах одинаковы по форме, но взаимно инверсны. Приёмник воспринимает их разность, а не уровень напряжения относительно общего провода. Логической единицей считается напряжение на входе D+, на 200 мВ большее, чем на входе D-. Логическому нулю соответствует обратная ситуация — напряжение на D+ на 200 мВ меньше, чем на D-.

Кроме информационных дифференциальных, по цепям D+ и D- передают и управляющие синфазные сигналы. Здесь логической единицей считается напряжение выше 2,8, но не более 3,6 В, а нулём — ниже 0,3 В относительно общего провода.

Информация кодируется по принципу NRZI (Non Return to Zero, Inverted — без возврата к нулю с инверсией). Чтобы предотвратить длительное отсутствие перепадов уровня в передаваемом сигнале, применяется "битстаф-финг" — в длинные последовательности разрядов одного логического значения с известным периодом вставляются разряды противоположного значения, автоматически исключаемые из потока на приёмной стороне.

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

Предусмотрено несколько типов транзакций. Управляющие используются ведущим для настройки ведомого

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

Передачи массивов байтов применяют, когда требуется гарантированная доставка информации адресату, но её время не оговаривается. Например, при работе с принтером или сканером. Обмен по прерываниям используют для пересылки единичных пакетов информации небольшого объёма за ограниченные промежутки времени. Так работают с компьютерной мышью или клавиатурой.

Изохронные передачи ведут, когда необходим обмен информацией в реальном времени, например, при пересылке аудио- или видеоинформации. Однако её доставка в этом случае не гарантирована!

Обработка информации в USB-устройствах (как ведущем, так и ведомых) ведётся на нескольких уровнях. Самый нижний — уровень шины USB. На нём происходит взаимодействие устройств по аппаратному интерфейсу USB, передача и приём пакетов информации.

Средним считается уровень логического USB-устройства. В каждом из них организованы конечные точки (endpoints) — логические источники и приёмники информации, циркулирующей между ведущим и ведомыми.

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

Конечная точка представляет собой выделенную в памяти устройства буферную область, имеющую свой уникальный номер. В каждом устройстве обязательно предусматривают управляющую двунаправленную конечную точку с нулевым номером. Её используют для настройки устройства и определения его текущего статуса. Число дополнительных конечных точек зависит от типа устройства и его конфигурации. Низкоскоростные USB-устройства могут иметь не более двух дополнительных конечных точек, а высокоскоростные — до 15 входных и столько же выходных.

Когда ведущий шины фиксирует подключение к ней нового устройства, он, обратившись к нему по адресу 0, назначает ему уникальный адрес в интервале 1—127. В дальнейшем (до отключения

от шины) устройство будет отзываться только на него. Затем ведущий посылает запрос GETDESCRIPTOR и определяет тип и конфигурацию нового устройства.

Когда получена вся требуемая информация, по идентификаторам Product ID и Release Number производится поиск предназначенного для данного устройства программного драйвера. В случае отсутствия у ведущего INF-файла с описанием именно этих идентификаторов, подбор драйвера производится согласно классу, подклассу и типу устройства. После успешной установки драйвера происходит настройка устройства, конфигурация имеющихся у него дополнительных конечных точек. После этого устройство готово к работе.

Даже из краткого описания принципов работы USB ясно, что организация взаимодействия периферийных устройств с компьютером по этой шине значительно сложнее, чем через традиционные, но уходящие в историю компьютерные порты LPT и СОМ. Для реализации всех возможностей USB требуются специализированные микросхемы или микроконтроллеры с встроенным модулем USB, реализующим протоколы USB нижнего и частично среднего уровней. К сожалению, такие микросхемы и микроконтроллеры далеко не дёшевы, многие из них приобрести не просто.

Но когда не требуется большая скорость обмена информацией, то организовать его по USB удаётся программным способом с помощью обычных широко распространённых микроконтроллеров достаточно большой производительности. Например, многие восьмиразрядные микроконтроллеры семейств ATtiny и ATmega, работая на пределе своих возможностей, позволяют реализовать предусмотренный спецификацией USB низкоскоростной обмен информацией (не более 1,5 Мбайт/с). К сожалению, эти микроконтроллеры не обладают достаточными ресурсами для программной реализации высоко- и полноскоростных устройств.

На страницах журнала уже публиковались описания приборов на таких микроконтроллерах, использующих программный USB-драйвер на языке С [1, 2]. Теперь речь пойдёт об аналогичном драйвере, предназначенном для среды BASCOM-AVR. Часть программы, отвечающая за работу с шиной USB и декодирование информации в режиме реального времени, написана на языке ассемблера и оформлена в виде библиотеки (файла Swusb.lbx). Этот файл следует скопировать в папку LIB установленной программы BASCOM. Остальные части драйвера написаны на языке BASIC и находятся в файлах USB_Config.bas, Const_swusb-includes. bas, USB_Descriptor.bas и USB Subroutines, bas. Их включают в разрабатываемую программу.

Файл USB_Config.bas отвечает за конфигурацию драйвера. В самом начале записанной в нём программы подключается библиотека Swusb.lbx и декларируются подпрограммы. Далее выбирается порт микроконтроллера, к

которому будут подключены информационные линии USB. В константах _usb_dplus и _usb_dmjnus должны быть указаны разряды порта, соединённые с контактами D+ и D- разъёма USB. Учтите, для сигнала D+ должен быть выбран вывод микроконтроллера, принимающий внешние запросы прерывания INTO.

Значение константы _usb_use_eeprom определяет, где будут храниться дескрипторы USB-устройства. Если оно равно 1, дескрипторы помещаются в EEPROM микроконтроллера. При любом другом значении они хранятся во FLASH-памяти вместе с программой. Использовать EEPROM имеет смысл только при недостаточности свободного места в программной памяти.

Константами _usb_pid и _usb_vid задают значения PID и VID, идентифицирующие устройство в системе. Одновременно подключать к компьютеру несколько устройств с одинаковыми PID и VID нельзя. Это вызовет конфликты между ними и приведёт к их неправильной работе.

Режим питания периферийного устройства задан константой _usb_powered. Если оно имеет собственный источник питания, эта константа должна иметь значение ОСОН. Если же устройство питается от шины USB, эта константа равна 80Н. В последнем случае константой _usb_maxpower можно указать ток, потребляемый устройством. Ей присваивают значение, вдвое меньшее максимального значения тока в миллиамперах. Например, при _usb_maxpower=100 (десятичное) устройство имеет право потреблять от линии Vbus разъёма USB не более 200 мА.

Если разрабатывается НЮ-устрой-ство, константа _usb_hids должна быть равна 1, в противном случае — 0.

Как упоминалось выше, обмен информацией происходит через конечные точки. Как правило, используют дополнительные конечные точки, но иногда применяют канал Feature двунаправленной нулевой конечной точки. У низкоскоростного USB-устройства может быть не более двух дополнительных однонаправленных конечных точек. Их требуемое число указывают константой _usb_ifaceendpoints.

Адреса дополнительных конечных


точек задают значениями констант _usb_endp2addr и _usb_endp3addr. Если какая-либо из них для разрабатываемой программы не требуется, соответствующую константу можно "закомментировать" — поставить в начале описывающей её строки символ ' (апостроф) Это освободит немного памяти микроконтроллера, поскольку для неиспользуемой конечной точки не будут выделяться буферы, а из программы будут исключены описывающие её дескрипторы.

Значения констант _usb_endp2direction и _usb_endp3direction определяют, будут ли точки использоваться для передачи информации в компьютер (1) или приёма из него (0). Режим работы точек задаётся константами _usb_endp2type и

_usb_endp3type. При обмене информацией по прерываниям их значения должны быть равны 3.

Период опроса конечных точек компьютером задаётся в миллисекундах константами _usb_endp2interval и _usb_endp3interval. Для низкоскоростного устройства период не может быть менее 10 мс.

В конце текста программы в неё включают файл Const_swusb-includes.bas, который не следует модифицировать. Здесь вычисляются требуемые размеры массивов и дескрипторов, объявляются константы, переменные и массивы для драйвера. После этого вызывается подпрограмма Usb_reset, инициализирующая USB-драйвер. Обработчиком внешнего прерывания INTO назначается подпрограмма Usb_isr из библиотеки Swusb.lbx. Она будет вызываться при возникновении активности на шине USB и принимать пакеты информации.

В файле USB_Descriptor.bas находятся дескрипторы устройства, описывающие его характеристики и тип. Если создаётся HID-устройство (константа _usb_hids больше нуля), то в программу будет вставлен HID-дескриптор сообщения. По умолчанию он имеет вид, показанный в табл. 1.

Значение константы Size_hid_report-descriptor — длина дескриптора в байтах. Следующие две строки DATA означают, что создаётся нестандартное

USB НЮ устройство. Далее описываются дополнительные конечные точки типов Input и Output. Параметр Report_size определяет длину пакета в битах, а Report_count — число пакетов Указанные в таблице значения этих параметров таковы, что за одну посылку можно принять или передать только один байт Строковые дескрипторы, описывающие изготовителя, продукт и серийный номер устройства, имеют метки_изЬ_ mandescriptor, _usb_proddescriptor и _usb_numdescriptor соответственно. Первые два байта дескриптора — его длина в байтах. За ними следует константа _usb_desc_string, означающая, что это строковый дескриптор, а после неё — символы текста в кодировке Unicode.


Для создания строкового дескриптора можно использовать утилиту Bascom_USB_descriptor.exe, окно которой показано на рис. 1. В его поле "Текст" вводят с клавиатуры или вставляют из буфера обмена Windows текст дескриптора в обычной кодировке и нажимают на экранную кнопку "Преобразовать". В поле "Код Bascom" будет выведен созданный код дескриптора в формате, необходимом для вставки в BASIC-программу. Туда его переносят через буфер обмена.

В файле USB_Subroutines.bas собраны подпрограммы, обсуживающие USB-драйвер. Подпрограмма Usbjefresh повторно инициализирует драйвер при продолжительном отсутствии активности на шине. Её вызывают в главном цикле программы.

Подпрограмма Usb_processsetup обрабатывает системные запросы (так называемые пакеты SETUP) интерфейса USB. Она, например, передаёт дескрипторы по запросу ведущего, присваивает адреса устройствам и выполняет многие другие действия, необходимые для правильного функционирования USB-устройства.

Подпрограмма Usb_senddescriptor передаёт дескрипторы устройства ведущему. Подпрограмму Usb_send

используют для отправки ему информации. Подпрограмма Usb_reset необходима для инициализации USB-драйвера.

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

Для проведения экспериментов нужно собрать устройство по схеме, показанной на рис. 2. Она полностью совпадает с изображенной на рис. 2 в [3]. Напомним, что резистор R1 устанавливает на линии D-напряжение, служащее для ведущего признаком, что к нему подключено низкоскоростное устройство. Выбор выводов 4 и 5 (PD2 и PD3) микроконтроллера для подключения информационных линий шины USB отражён значениями констант _usb_dplus и _usb_dmlnus в файле USB_Config.bas. Он не случаен. В микроконтроллере ATmega8 изменение уровня на линии PD2 может генерировать запрос внешнего прерывания INTO, что используется для организации приёма информации, поступающей от компьютера.

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

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

Директива $include включает в программу ранее рассмотренный файл USB_Config.bas, выполняющий конфигурирование и инициализацию USB-драйвера. Далее линия порта РВО, к которой подключена кнопка, назначается входом, а управляющая светодиодом линия РСО — выходом.

Операторы Dim создают две переменные, предназначенные для хранения информации о текущем и прошлом состояниях кнопки SB1. Каждая из них занимает один байт памяти. Затем разрешается обработка прерываний, что необходимо для USB-драйвера, обра-


батывающего запросы прерывания INTO.

Следующий далее основной цикл программ выполняется всё время, пока работает микроконтроллер. В начале цикла вызывается подпрограмма Usb_refresh, следящая за активностью на шине USB и при необходимости повторно инициализирующая USB-драйвер. Затем с помощью условного оператора IF анализируется состояние разряда _usb_rxc переменной _usb_status, хранящей текущий статус драйвера. При поступлении по USB новой информации условие будет выполнено. В этом случае анализируется состояние разрядов _usb_setup и _usb_endp1 той же переменной.

Выполнение условия _usb.setup=1 означает приём системного сообщения (например, запроса текущей конфигурации устройства) от ведущего. В этом

случае вызывается подпрограмма Usb_ processsetup, в которой обрабатываются эти команды. А вот выполнение условия _usb_endp1=1 при текущей конфигурации драйвера показывает, что ведущий передал информацию в первую дополнительную конечную точку ведомого. Она находится в массиве _usb_rx_buffer.

В рассматриваемом примере младший разряд полученного байта заносится в разряд РС1 регистра порта С микроконтроллера. Это позволяет в зависимости от значения принятого байта включать и выключать светодиод HL1. После обработки полученной информации в разряды переменной _usb_status заносят следующие значения: _usb_rtr — 1, usbrxc — 0. Это сообщает драйверу, что полученная информация обработана и можно принимать следующий пакет.

Далее следует опрос состояния кнопки SB1, подключённой к линии РСО микроконтроллера. Если оно изменилось (кнопка была нажата либо отпущена), то сообщение об этом отправляется в компьютер. Но перед этим, чтобы убедиться в готовности драйвера к передаче, программа проверяет значение разряда _usb_txc переменной _usb_tx_status2. Для передачи содержимое переменной Button копируется в массив _usb_tx_buffer2, после чего следует вызов подпрограммы Usb_send. Её второй аргумент — число передаваемых байтов.

При первом подключении устройства к компьютеру операционная система определит его как USB НЮ совместимое устройство с именем "Демо-уст-ройство". Это имя хранится в строковом дескрипторе продукта, находящемся в файле USB_Descriptor.bas.

ЛИТЕРАТУРА

1. Рюмик С. Подключение джойстиков от игровых приставок к шине USB. — Радио, 2007, №1,с. 28—31.

2. Суров С. Обмен информацией с USB HID устройством. — Радио, 2010, № 3, с. 25—28.

3. Высочанский П. Работа с USB НЮ устройствами в Windows. — Радио, 2011, № 4, с. 27-30.

Скачать дополнительные файлы


Добавил:  Павел (Admin)  [email protected] | 

Автор:  П. ВЫСОЧАНСКИЙ, г. Рыбница, Приднестровье, Молдавия  Рейтинг@Mail.ru