РадиоКот :: Связь микроконтроллеров семейства C8051 с компьютером через USB. Часть II.
Например TDA7294

РадиоКот >Статьи >

Теги статьи: C8051USBДобавить тег

Связь микроконтроллеров семейства C8051 с компьютером через USB. Часть II.

Автор: Сергей Безруков aka Ser60
Опубликовано 06.03.2012
Создано при помощи КотоРед.

  Реализация HID устройства

HID устройства используют, как известно, штатные USB драйверы, имеющиеся в любой операционной системе и не требуют установки никаких дополнительных драйверов на компьютере. Спецификация USB позволяет создавать очень универсальные устройства, гораздо более универсальные, чем требуется в большинстве практических случаев, особенно в радиолюбительской практике. Также и примеры из «аппноутов» описывают гораздо более сложные устройства, чем обычно требуется. Я расскажу как реализовать простейшую базовую функцию пересылки единичного байта (или массива байтов) между МК и компьютером.

Программное обеспечение для МК

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

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

Итак, USB устройство обменивается с компьютером через каналы связи, обеспечиваемыми конечными точками (endpoints, EP). Как известно, любое HID устройство должно поддерживать двунаправленную контрольную конечную точку EP0 для передачи, в основном, сигналов управления и одну однонаправленную конечную точку типа INTERRUPT-IN, через которую могут передаваться данные из МК в компьютер. Кроме того оно может иметь до одной однонаправленной конечной точки типа INTERRUPT-OUT, через которую могут передаваться данные из компьютера в МК.

Мы рассмотрим способ передачи данных через однонаправленные конечные точки EP1_IN и EP1_OUT типа INTERRUPT-IN и INTERRUPT-OUT, соответственно. У этого способа, как и у любого другого есть свои преимущества и недостатки. Преимущество в том, что при этом гарантируется передача данных с предсказуемой задержкой. Максимальная величина задержки устанавливается в момент опознавания устройства компьютером, как следствие обмена данных о конечных точках устройства. Недостатком, особенно проявляющемся при редком обмене данными, является более чем необходимая загрузка шины USB – по ней будут часто передаваться пакеты с запросом данных, которые еще не готовы для передачи, там самым снижая пропускную способность шины для других устройств.

Отметим, что в протоколе USB обмен данных между МК и компьютером может быть инициализирован только компьютером. Это очевидно при передаче данных из компьютера в МК. USB модуль МК принимает данные через конечную точку, располагает их в буфере и выставляет флаг прерывания процессору.  Остается только понять как настроить буфер и как брать из него данные в программу.

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

Для реализации описанного выше функционирования МК должен еще поддерживать и массу других функций, требуемых от устройства класса HID. Все они реализованы инженерами фирмы Silicon Labs и доступны для загрузки с вебсайта фирмы. В целом проект BlinkLED написан на языке С и состоит из 5 файлов программ (имеющих расширение .с) и соответствующих файлов заголовков (с расширением  .h). После открытия файла проекта BlinkLED.wsp из папки МК в IDE Вы увидите следующую картину:

Ниже приведено описание и назначение каждого из файлов программ.

Файл USB0_Main.c  в соответствии с его именем содержит главную программу пользователя. Собственно, подпрограмма main() в этом файле обеспечивает настройку тактового генератора МК на частоту 48 МГц, отключает сторожевой таймер, инициализирует выводы порта, подключенные к светодиодам D4 и D5 на вывод, инициализирует регистры USB модуля и разрешает прерывания. После этого она входит в бесконечный цикл и вся дальнейшая работа программы обеспечивается прерываниями. При адаптации к Вашему устройству может потребоватся переинизиализировать линии порта ввода/вывода в соответствии с требуемым функционированием.

Файл USB0_Standard_Requests.c содержит всю необходимую реализацию функций интерфейса HID. Никаких изменений в нем делать не следует, нужно просто включить этот файл в проект.

Файл USB0_InterruptServiceRoutine.c содержит релизацию функций обработки прерываний модуля USB. Никаких изменений в нем также делать не надо, следует просто включить его в проект как и предыдущий файл.

Мы уже прошли через 3 файла из 5 принадлежащих к проекту, т.е. как-бы больше половины пути, и пока не изменили ни строчки. Хорошая новость, что никаких изменений в .h-файлах тоже делать не следует (с одним-единственным исключением, см. ниже). Однако, в двух оставшихся С-файлах программы может потребоваться сделать некоторые изменения, исходя из назначения Вашего устройства. Рассмотрим подробнее что именно можно/нужно изменить и зачем.

Файл USB0_Dexm_aor.c. Как известно, при подключении USB-устройства к компьютеру, последний запрашивает конфигурацию устройства, чтобы понять как с ним работать. Конфигурация устройства начинается с дескриптора устройства и соответствующие данные содержатся в структуре DEVICEDESC.

Поле idVendor со значением 0xC410 соответствует фирме Silicon Laboratories, и его можно оставить как есть (фирма разрешает это). Поле idProduct со значением 0х0001 означает идентификатор устройства. Для Вашего устройства его можно получить бесплатно по запросу в Silicon Labs. Оба поля существенны для программы компьютера для установления связи с устройством. Если изменить эти поля здесь, соответствующие изменения потребуются и в программе компьютера, иначе Ваше устройство им не опознается. Короче, можно также оставить пока эти поля как есть.

Следующая структура HIDCONFIGDESC содержит данные, специфичные для USB устройства класса HID. Эти данные настроены на работу с двумя конечными точками, как указано выше, которые будут опрашиваться не реже, чем через каждые 10 миллисекунд (последние значения в структурах IN endpoint и OUT endpoint. Число 10 можно изменить при желании, но оно должно быть не менее 1 и не более 1000, в соответствии с требованиями для транзакций типа INTERRUPТ  в спецификации USB. Помимо этого, значения EP1_PACKET_SIZE_LE и EP2_PACKET_SIZE_LE определяют максимальный размер массива байтов, передаваемый в одном пакете через контрольную точку. Они определены в соответствующем файле заголовков USB0_Dexm_aor.h и равны 10. Максимальное их значение при простой реализации не должно превышать максимальный размер данных пакета, т.е. 63. Важная установка MaxPower, определяющая максимальное токопотребление устройства в градациях 2 мА. Для нашего устройства  20*2 = 40 мА будет достаточно.

Ну а что-же тогда менять? Менять можно значения в дескрипторе сообщений HID (HIDREPORTDESC), причем только поля report count и report ID. В этом дескрипторе определяется тип данных, передаваемых в компьютер и из него. Малейшее несоответствие декларируемого типа данных с реально посланным – и Ваше устройство не будет понято компьютером.

В тестовой программе предполагается, что обмен информацией между МК и компьютером будет производиться на основе байтов, т.е. report size равно 8. Каждому сообщению, посылаемому через конечную точку типа INTERRUPT должен быть присвоен некоторый идентификатор (report ID), и номера эти не должны повторяться. В программе используются лишь 2 сообщения: передаваемое из МК в компьютер (reportID = 1) и из компьютера в устройство (reportID = 2). Если Вас это устраивает можете оставить номера без изменений (иначе придется делать соответствующие изменения в программе компьютера и в следующем файле программы МК, см. ниже). Значение report count соответствует числу байтов, передаваемых в сообщении. Мы передаем лишь 1 байт, т.е. это число равно 1. При передаче массива эти числа можно  увеличить, но не более, чем до значений EP1_PACKET_SIZE_LE и EP2_PACKET_SIZE_LE, соответственно. Шеснадцатеричные числа, предшествующие описанным здесь значениям полей являются идентификаторами полей, в соответствии со стандартом HID, и используются компьютером, чтобы ему разобраться к чему относятся значения полей. Изменять их, естественно, не следует. Если Вы захотите добавить другие сообщения, каждому из них следует присвоить свой report ID, добавить для каждого его описание в структуре HIDREPORTDESC и скорректировать длину полученного массива в 2-байтовых константах HID_REPORT_DExm_aOR_SIZE и HID_REPORT_DExm_aOR_SIZE_LE определяемых в файле USB0_InterruptServiceRoutine.h. Вторая из этих констант получена из первой путем перестановки байтов (little endian) и требуется для использования в таком виде в структурах данных HID. Например, если первая константа равна 0xABCD, то вторая должна быть определена как 0xCDAB.

Ну а где-же в программе МК происходит мигание светодиодами? Все функционирование устройства при получении данных из компьютера реализовано в последнем файле проекта – USB0_ReportHandler.c. Файл начинается с декларирования буферов USB OUT_PACKET и IN_PACKET, располагаемых во «внешней» памяти МК. Как известно, МК семейства х51 поддерживают внешнюю память данных, обычно располагаемую вне МК. Однако, в семействе С8051 на борту МК имеется 2Кб или 4Кб такой памяти помимо стандартных 256 байтов RAM и памяти программ МК. На языке С такая память регламентируется как xdata.

В рассматриваемомом файле имеется всего 3 коротких подпрограммы. Первую их них – Setup_OUT_BUFFER() (см. выше) - менять не надо. Она устанавливает указатель на буфер для отсылки в компьютер в структуре OUT_BUFFER и его длину.

Следующая подпрограмма – ReportHandler_OUT() – отвечает за функционирование МК при приеме данных из компьютера. Вызов ее производится обработчиком аппаратных прерываний МК, и пользователям никогда вызывать ее не следует. Тело этой подпрограммы следует поменять под Ваши условия. В нашем случае проверяется номер сообщения (report ID) присланного компьютером, и если он равен 2, то принятый байт выводится в порт МК P1 для включения или выключения светодиодов. Массив байтов, принятых из компьютера хранится в буфере данных USB и доступен по индексу через указатель OUT_BUFFER.Ptr. По-русски это означает, что получить первый (и единственный в нашем случае) байт посланный компьютером можно с помошью операции c = OUT_BUFFER_Ptr[1]. Как известно, нумерация элементов массива в языке С начинается с 0, но байт с индексом 0 – это report ID сообщения из компьютера (в нашем случае этот ID равен 2), а байт с индексом 1 – собственно информационный для управления светиками. Сдвиг байта с на 4 позиции влево в нашем случае нужен потому, что биты управления светодиодами в программе компьютера имеют номера 0 и 1, а светодиоды подключены к линиям порта P1.4 и P1.5. Данные из буфера OUT_BUFFER, предназначенные для дальнейшего использования в программе, лучше сразу скопировать в другую часть памяти, поскольку их сохранность в буфере USB не гарантируется – после окончания работы обработчика прерываний они в любой момент могут быть заменены другой принятой информацией.

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

Таким образом, мы проинформировали модуль USB, что готовы передать сообщение в компьютер. Как только компьютер разрешит передачу сообщения, произойдет прерывание и вызовется последняя в рассматриваемом файле подпрограмма ReportHandler_IN(). Параметр этой подпрограммы формируется обработчиком прерываний и равен номеру сообщения, ожидаемого компьютером. Поскольку мы запросили передачу сообщения под номером 1 в строке кода SendPacket(1), неудивительно, что параметр подпрограммы тоже будет равен 1. Все, что нужно сделать для передачи байта сейчас – это сформировать сообщение в буфере USB IN_PACKET. Первым байтом в буфере должен быть номер сообщения (1), затем сами передаваемые байты (в нашем случае лишь 1 байт). В завершении, нужно установить полную длину сообщения в байтах, включая его идентификатор (т.е. 2 для передачи одного байта) в поле Length структуры IN_BUFFER.

Подпрограмма ReportHandler_IN() вызывается обработчиком прерываний по запросу данных из компьютера (подпрограммой Get_Report() в файле USB0_Standard_Requests.c),  а также по запросу МК (из подпрограммы SendPacket() в файле USB0_InterruptServiceRoutine.c).

Объем загрузочной части програмы около 3,07 Кб из 32 Кб, имеющихся в МК.

Программное обеспечение для компьютера

Как известно, каждая ОС, включая Windows, имеет свой набор стандартных функций для работы с USB. Однако, пользоваться этими функциями, особенно без наличия соответствующего опыта и знаний, непросто. Для упрощения разработки устройств на МК семейства С8051 фирма Silicon Laboratories разработала библиотеку функций SLABHIDDevice.dll, доступную с вебсайта фирмы. Функции этой библиотеки вызывают надлежашие функции операционной системы и пользоваться ими гораздо проще. Библиотека подробно описана в «аппноуте» AN532 и может быть использована для интеграции в программы написанные на Visual Basic, C++, или C#. Аппноут AN532 содержит инструкции по использованию библиотеки с каждым из этих языков. В частности, для программ на C# следует включить в проект файл SLABHIDDevice.cs из библиотеки.

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

Весь проект состоит из 3-х файлов, находящихся в папке ComputerTestUSB: MainWindows.xaml, MainWindow.xaml.cs, и UsbDevice.cs. В первом из этих файлов содержится описание графического интерфейса (кнопки и пр.), во втором – главная часть программы (см. ниже), и в последнем – описание класса HID устройства.

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

  • vid  и pid – идентификатор производителя (мы используем стандартный идентификатор Silicon Laboratories 0x10C4) и идентификатор устройства определенные в программе для МК.

  • numDevices – число подключенных к шине USB устройств с определенными в классе полями vid и pid (их, вообще говоря, может быть несколько).

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

  • handle – внутренняя переменная, используемая для связи с выбранным устройством.

  • bytesRead -  число байтов полученных от устройства.

Конструктор класса просто инициализирует поля vid  и pid в соответствии со значениями, посланными из основной программы.

Основная программа MainWindow.xaml.cs также тачинается с описания полей своего класса и включает его конструктор и несколько подпрограмм обработчиков событий. Регистраторы событий cWatcher и rWatcher (connect и  remove) отслеживают моменты подключения USB устройств к компьютеру и отключения от него, соответственно. В главной программе они настроены на проверку этих событий каждую секунду. Этим обеспечивается автоматическое распознавание подключения и нашего устройства. Массивы байтов outBuffer и inBuffer служат для хранения информации посылаемой в устройство и принимаемой из устройства, соответственно.

Отметим также создание объекта класса UsbDevice, упомянутого ранее. Он настроен на работу с устройствами со значением vendorID и productID, определенными в программе микроконтроллера (см. выше). Однако, в программе МК они должны быть представлены в порядке байтов от младшего к старшему (little endian), т.е. как 0xC410 и 0х0001, или с точностью до наоборот по сравнению с программой на C#. Порядок байтов определяется требованием стандарта USB.

При подключении какого-либо USB устройства к компьютеру вызывается функция HandleEvent. Она проверяет наше-ли это устройство (по vid и pid) и если да, и устройство не было опознано ранее, то устанавливает с ним контакт. Также создаются буфера ввода/вывода и посылается запрос в устройство о статусе светодиодов. Таким образом компьютер получит достоверную информацию о текущем состоянии светодиодов даже если программа была запущена после подключения устройства к компьютеру, или наоборот.

Очень важно (!!!) отметить следующее. Несмотря на коммуникацию устройства и компьютера через конечные точки типа INTERRUPT, запрос о состоянии светодиодов производится сейчас через контрольную конечную точку EP0 (device.getControl). Только так можно заставить MK ответить на запрос информации из компьютера. При этом в первый байт входного буфера inBuffer следует до посылки запроса поместить значение идентификатора требуемого сообщения (в нашем случае 1). Если вместо этого посылать запрос через контрольную точку типа INTERRUPT (device.getInterrupt), то МК пошлет обратно в компьютер данные, только если они к моменту запроса будут помещены в его USB буфер. Наша программа в МК посылает в буфер USB данные о состоянии светодиодов только после включения/выключения одного из них. Таким образом, в этом случае, например при запуске программы компьютера, в него не будет послано ничего в ответ на запрос!

Обработчик событий rWatcher_EventArrived вызывается при отключении какого-либо USB устройства от компьютера. При этом проверяется наше-ли это устройство, и если да, то переменная deviceInstantiated устанавливается в FALSE, что, в частности, прекращает реакцию программы на кнопки интерфейса и тем самым препятствует попытку коммуникации с отключенным устройством. В верхней части окна программы высветится Device Disconnected.

Следующий обработчик события redButton_Click вызывается каждый раз по-нажатии на «красную» кнопку интерфейса. При этом подготавливается посылка в МК сообщения с идентификатором 2 (в байте outBuffer[0]) для изменения состояния красного светодиода. Посылаемое значение 1 вызывает смену его состояния с включен на выключен или наоборот.

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

Обработчик события greenButton_Click нажатия на «зеленую» кнопку интерфейса аналогичен предыдущему. Отличие лишь в значении байта, посылаемого в МК. Этот обработчик производит управление зеленым светодиодом на плате.

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

Запуск программы можно осуществить либо из Visual Studio путем загрузки в нее файла проекта TestUSB.sln, либо путем кликания мышкой на файл TestUSB.exe в папке binDebug проекта.

 


Файлы:
Архив программ


Все вопросы в Форум.




Как вам эта статья?

Заработало ли это устройство у вас?

24 3 1
1 0 0

Эти статьи вам тоже могут пригодиться: