STM32 и USB. Часть 2. Немного о драйверах и софте.

STM32 и USB. Часть 1. Проект для Keil.
В прошлой части я рассказал как примерно должен выглядеть проект-заготовка для Keil девайса с USB, дал ссылку на мой проект и рассказал как его настроить под практически любую плату с STM32.
В проекте был реализован интерфейс с двумя bulk-ендпойнтами (in и out), с моим «кастомным» протоколом, при помощи которого можно включать, выключать и заставлять светоиоды мигать с нужными временами горения/не горения.
Ну и выложил небольшую программу для всего этого:

Пользователь Vga в комментариях справедливо заметил, что разработка своего драйвера под Windows — задача далеко не тривиальная, и что проще реализовать стандартный класс, например HID, под который драйверы есть.
В этой статье я проведу небольшой обзор средств, которыми можно воспользоваться, чтобы сильно упростить себе жизнь.
Итак, у нас на повестке следующие варианты:
1) Написать драйвер самому. Задача сложная, неблагодарная, долгая. Тут сразу можно отсылать, например к Windows Driver Foundation. Есть куча примеров, которые можно расковырять и адаптировать под свои задачи.
Несомненный плюс этого варианта — решение получится красивое и компактное. Но стоит ли это потраченного времени…
Извините, если разочаровал тех, кто полагал, что я опишу именно этот вариант :)
2) Реализовать на девайсе один из стандартных классов, например тот же HID.
Но давайте предположим что мы вот буквально вчера наконец-то заставили наш девайс определяться по USB как самостоятельное устройство, и все что мы умеем — это читать и писать в ендпойнты. Нам нужно что-то еще проще.
3) Использовать готовые средства. Вот на этом «варианте для начинающих и не только» мы остановимся.
Но вначале небольшое отступление.
Как Windows опознает наши USB-устройства?
В большинстве случаев это «зашитые» в девайс VID (Vendor ID) и PID (Product ID). Есть исключения, например HID и Mass Storage, там Windows опознает класс устройства и подсовывает уже приготовленный драйвер.
Если мы воткнем наш девайс в компьютер, система определит его, но, очевидно, будет ругаться на отсутствие драйвера, и предложит выбрать .inf- файл.
Именно в этом файле и прописываются, помимо прочего, наши VID и PID, а также путь к драйверу.
Есть, конечно, и более хитрые варианты, но мы на них пока останавливаться не будем.
Наши VID и PID можно подсмотреть в файле usb_desc.c проекта. И обязательно прочтите комментарий ;-)
Итак, встречаем: libusb-win32 и Jungo WinDriver.
Jungo WinDriver
http://www.jungo.com/st/windriver_usb_pci_driver_development_software.html
Заметьте, что поддерживает не только USB, но и PCI, и многое другое.
Весьма удобная штука.
Запускаем Wizard, выбираем по VID-PID наш девайс:

Генерим inf-файл, сохраняем, устанавливаем тут же драйвер, и вуаля. Вот они наши два bulk-ендпойнта + управляющий, нулевой ендпойнт:

А вот наш девайс определился в диспетчере устройств:

Но и это не все. Жмем на волшебную кнопку Generate Code:

И получаем воистину огромный набор вариантов на любой вкус:

Генерим проект, запускаем. Вот — готовая программа для работы с нашим девайсом:

Можем отправлять данные в ендпойнты и читать их оттуда.
Например, отправка пакета на скриншоте согласно моему протоколу зажжет светодиод номер два. (См protocol.txt в проекте Keil)
Я сгенерил проект для C# (.NET) и мне он выдал солюшен с двумя проектами: Собственно сама программа и либа для работы с девайсом по USB. В последней есть все необходимое, вплоть до событий подключения-отключения девайса. А вообще если не заморачиваться, то все можно свести к обычному чтению-записи в ендпойнты.
Дальше сами справитесь? ;-)
Ну а теперь поговорим о недостатках.
1) Jungo WinDriver — штука очень уж платная. Кто хочет расстроиться — цены лежат тут.
2) У некоторых USB-девайсов бывает несколько конфигураций. Такое встречается редко, но встречается.
WinDriver с такими работать не умеет, а функция смены конфигурации помечена как Not Implemented Yet.
libusb-win32
https://sourceforge.net/apps/trac/libusb-win32
Распаковываем и в папочке bin лежит программка inf-wizard.exe. Тоже визард для генерации инф-файла, а заодно и всех остальных файлов, необходимых для установки драйвера.
Запускаем, выбираем наш девайс, сохраняем inf и прочее в отдельную папочку:

Ну и сразу инсталлируем.

Теперь, чтобы создать свой проект, необходимо собрать все нужные от libusb файлы в папках lib, include, подсмотреть как работать с устройством, в папке exampes. А работать — проще простого (см bulk.c)
...
usb_init(); /* initialize the library */
usb_find_busses(); /* find all busses */
usb_find_devices(); /* find all connected devices */
...
if (!(dev = open_dev()))
{
printf("error opening device: \n%s\n", usb_strerror());
return 0;
}
...
if (usb_set_configuration(dev, MY_CONFIG) < 0)
{
printf("error setting config #%d: %s\n", MY_CONFIG, usb_strerror());
usb_close(dev);
return 0;
}
...
if (usb_claim_interface(dev, 0) < 0)
{
printf("error claiming interface #%d:\n%s\n", MY_INTF, usb_strerror());
usb_close(dev);
return 0;
}
...
// Running a sync write test
ret = usb_bulk_write(dev, EP_OUT, tmp, sizeof(tmp), 5000);
...
Но и этого мне показалось мало. Поскольку меня в свое время подсадили на тяжелый наркотик, именуемый C# + .NET, я стал искать решения, такие же простые, как и Jungo WinDriver.
И нашел следующее:
LibUsbDotNet
https://sourceforge.net/projects/libusbdotnet/
Нет, вы только вдумайтесь: .NET-надстройка над библиотекой, портированной из линукса под win32!
Конечно, изврат извратом, но мне понравилось.
Именно при помощи этой либы я и написал программу из предыдущей статьи. Работать с ендпойнтами так же просто.
UsbDevice SomeUsbDevice;
UsbDeviceFinder usbDeviceFinder = new UsbDeviceFinder(0x0483, 0xfff0);
SomeUsbDevice = UsbDevice.OpenUsbDevice(usbDeviceFinder);
...
UsbEndpointReader ep81Reader;
UsbEndpointWriter ep02Writer;
...
ep81Reader = SomeUsbDevice.OpenEndpointReader(ReadEndpointID.Ep01);
ep02Writer = SomeUsbDevice.OpenEndpointWriter(WriteEndpointID.Ep02);
...
ErrorCode ec = ep02Writer.Write(_req, 1000, out nBytesWritten);
...
Ну а дальше реализуем небольшой протокол, пишем-читаем ендпойнты и радуемся мигающим светодиодам :)
Только не забываем одну тонкость: весь обмен с USB-девайсом происходит по инициативе хоста. Поэтому, данные не попадут в хост до тех пор пока хост сам не захочет их прочитать.
Вот и все.
Итого
Вот, в принципе, все необходимое для того чтобы создать на STM32 примитивный USB-девайс. Весь обмен сводится к чтению и записи в ендпойнты и разбору того, что же туда все-таки пришло и что с этим делать.Лично мне кажется этот вариант проще, чем реализация на девайсе стандатного класса.
Как всегда, файлы с проектом находятся тут.
Ну а в следующий раз, когда дойдут руки, будем поднимать USB Mass Storage, причем поверх уже сделанного интерфейса для светодиодов, т.е. составное USB-устройство :)

- +4
- 21 сентября 2011, 14:03
- Ezhik
LibUsb штука хорошая, но стандартные классы тоже не плохи. Например, класс CDC видится в системе как виртуальный КОМ порт, что не может не радовать, а реализуется очень просто. Всё-таки работа на любой системе без установки своих драйверов много стоит.
За статьи спасибо, интересно и познавательно. Сейчас помаленьку тоже ковыряю USB. Помаленьку — потому, что пока я не вижу особого смысла креативить свои USB-утройства, ибо есть FT232. VCP — наше все! :)
Есть. FTDI дубовая и железная. В ней все уже реализовано и намертво протестировано. Если ничего кроме не требуется, то зачем что либо еще?
Мне FTDI (а именно FT232RL) не понравился тем что он от малейшего шума зависает. Поставил AT90USB82 (которая как минимум в 2 раза дешевле) — стало все отлично.
ORLY??? Может плата кривая? Я так наоборот так и не смог повесить FTDI. А цена… FTDI стоит от 90 до 130 рублей. Может у вас кривой поставщик? ;)
Поставщик терраэлектроника, цены у них на FT232RQ-R 215.31р, а AT90USB82 87.15р. На сколько кривой этот поставщик не знаю, ну уж явно лучше чип и дипа. У нас тут в Караганде FT232R в некоторых местах если переводить на ваши деньги вообще за 400р. продают, а AT90USB82 за 120р., но на заказ. А какой поставщик не «кривой». Схему для платы брал из даташита. Питаетcя от USB, за ней стоит ATmega644, которая не висла не разу. Затем поискал в инете, проблема с зависанием FT232 общая, посоветовали посадить все неиспользуемые ножки на землю. Так и сделал, но все равно иногда подвисала. Заменил на AT90USB — все стало ок.
Для штучного производства пофигу.
Для серии совсем другие цены будут. Зато FTDI не надо прошивать и не надо париться по поводу возможных глюков в прошивке.
Для серии совсем другие цены будут. Зато FTDI не надо прошивать и не надо париться по поводу возможных глюков в прошивке.
Ну в общем как всегда, у всего свои плюсы и минусы. Если нужно быстро и просто и большего не какого функционала кроме бриджа я тоже FTDI, возьму. А по поводу прошивки у atmel есть пример для CDC, ничего править практически не надо. И не думаю что там ошибки могут быть, все таки официальный пример.
Единственное, что смущает как так сделали что FT232RL, без внешнего кварца работает. Причем работает же и неплохо в нормальных условиях.
Единственное, что смущает как так сделали что FT232RL, без внешнего кварца работает. Причем работает же и неплохо в нормальных условиях.
Чудесно. А как теперь, пофиксили? Но вообще это проблема дров.
Дрова FT232 тоже чудненько повисают где-то в ядре, если пытаться писать в ее ком-порт при отключенной микросхеме. В сочетании со странной запиткой фт-шки на пинборде я раз десять с матом к ресету тянулся.
Дрова FT232 тоже чудненько повисают где-то в ядре, если пытаться писать в ее ком-порт при отключенной микросхеме. В сочетании со странной запиткой фт-шки на пинборде я раз десять с матом к ресету тянулся.
Не знаю, с тех пор я зарекся юзать эту микруху.
Да? У меня с FTDI таких проблем не было. При попытке рыгнуть что нибудь в несуществующий уже порт не происходит ровным счетом ничего.
Да? У меня с FTDI таких проблем не было. При попытке рыгнуть что нибудь в несуществующий уже порт не происходит ровным счетом ничего.
ft232 легко можно изолировать от компа. Две оптопары всего. Причем линии TxD и RxD можно инвертировать программно.
Спасибо, полезная статья.
Давно хочу попробовать идею управления USB-устройствами через виртуальную файловую систему.
То есть обмен информацией с mass-storage через чтение/запись файлов на нем.
Получается простая и абсолютно кросс-платформенная система без надобности в каких-либо драйверах (даже на андроиде должно работать), обмен может происходить в любом формате, хоть в текстовом (напр. изменить настройки прибора можно просто набрав в консоли «echo 'param1=0,param2=1,...' >файл_на_флешке»). Возможна реализация и динамического Web-интерфейса, когда броузером открываем html файл и подгружается динамический JSON-контент.
Давно хочу попробовать идею управления USB-устройствами через виртуальную файловую систему.
То есть обмен информацией с mass-storage через чтение/запись файлов на нем.
Получается простая и абсолютно кросс-платформенная система без надобности в каких-либо драйверах (даже на андроиде должно работать), обмен может происходить в любом формате, хоть в текстовом (напр. изменить настройки прибора можно просто набрав в консоли «echo 'param1=0,param2=1,...' >файл_на_флешке»). Возможна реализация и динамического Web-интерфейса, когда броузером открываем html файл и подгружается динамический JSON-контент.
Ну да, как-то так.
Например, универсальный генератор/частотомер/осциллограф — настройки — в одном файле (ini/conf), форма сигнала — в другом (CSV), результат измерения/буфер осциллографа — в третьем. Управление — еще один файл. Просто пишем туда «start» для начала измерения. Тут же на диске Web-интерфейс. Статический html c какой-нибудь GUI библиотекой и рядом Javascript с JSON-данными измерений.
Например, универсальный генератор/частотомер/осциллограф — настройки — в одном файле (ini/conf), форма сигнала — в другом (CSV), результат измерения/буфер осциллографа — в третьем. Управление — еще один файл. Просто пишем туда «start» для начала измерения. Тут же на диске Web-интерфейс. Статический html c какой-нибудь GUI библиотекой и рядом Javascript с JSON-данными измерений.
Пропустили еще один способ — WinUSB.
В качестве драйвера ставится стандартный winusb.sys, а приложение линкуется с winusb.dll
На выходе получаем тот же API чтения/записи в end-point'ыю
msdn.microsoft.com/en-us/library/ff540196%28v=VS.85%29.aspx
В качестве драйвера ставится стандартный winusb.sys, а приложение линкуется с winusb.dll
На выходе получаем тот же API чтения/записи в end-point'ыю
msdn.microsoft.com/en-us/library/ff540196%28v=VS.85%29.aspx
- extracomlex
- 23 сентября 2011, 15:00
- ↓
Да, пропустил. Способ достойный внимания, хотя для начинающих он будет чуть посложнее, чем последние два.
http://msdn.microsoft.com/en-us/library/ff540174(v=VS.85).aspx
И красивеньких визардов для быстрой генерации inf файла там я тоже не нашел :)
Ну и, кроме того, либа LibUsbDotNet также работает с WinUSB:
http://msdn.microsoft.com/en-us/library/ff540174(v=VS.85).aspx
И красивеньких визардов для быстрой генерации inf файла там я тоже не нашел :)
Ну и, кроме того, либа LibUsbDotNet также работает с WinUSB:
(где-то в исходниках)
...
internal const string WIN_USB_DLL = "winusb.dll";
...
[DllImport(WIN_USB_DLL, EntryPoint = "WinUsb_ReadPipe", SetLastError = true)]
private static extern bool WinUsb_ReadPipe([In] SafeHandle InterfaceHandle,
byte PipeID,
Byte[] Buffer,
int BufferLength,
out int LengthTransferred,
IntPtr pOVERLAPPED);
...
Спасибо за полезную статью.
Перенес твою разработку на свою Discovery с stm32l152 + сделал кое-какие изменения (скрестил твой проект с примером HID-устройства из либы stm'овской). Я правильно понимаю, что при подсоединении к компу (Win7) мастер установки оборудования сам должен предложить установить драйвер?
Плата без серийного номера устройства (процедура GetSerialNum() в hw_config) не хотела работать, я взял исходник этой процедуры из примера Custom_HID фирменной USB-библиотеки, теперь устройство обнаруживается, но не распознается, и драйвер ставиться не хочет.
Пробывал запускать Installer_x64 из вложения — ноль эффекта. Что сделать, чтобы система распознала устройство?
Перенес твою разработку на свою Discovery с stm32l152 + сделал кое-какие изменения (скрестил твой проект с примером HID-устройства из либы stm'овской). Я правильно понимаю, что при подсоединении к компу (Win7) мастер установки оборудования сам должен предложить установить драйвер?
Плата без серийного номера устройства (процедура GetSerialNum() в hw_config) не хотела работать, я взял исходник этой процедуры из примера Custom_HID фирменной USB-библиотеки, теперь устройство обнаруживается, но не распознается, и драйвер ставиться не хочет.
Пробывал запускать Installer_x64 из вложения — ноль эффекта. Что сделать, чтобы система распознала устройство?
а кто-нибудь видел в сети примеры на STM с двумя VCP в одном?
и ещё вопрос, как калибровать RC, чтобы работать без кварца?
и ещё вопрос, как калибровать RC, чтобы работать без кварца?
Здравствуйте. К сожалению, я уже достаточно давно не занимаюсь STMками (пересел на ПЛИС), поэтому не уверен что смогу что-либо вспомнить и вразумительно ответить на вопросы.
Думаю, на этом сайте есть еще много примеров работы с USB, более актуальные.
Но все равно можете написать в личку, вдруг что вспомню.
Думаю, на этом сайте есть еще много примеров работы с USB, более актуальные.
Но все равно можете написать в личку, вдруг что вспомню.
Спасибо! Я так и сделаю. Мне трудно это передать в коротком тексте, но вы осветили именно те проблемы, что меня интересуют на сегодня, и поскольку я только начал изучать STM32, то пересмотрел много примеров — для меня Ваш лучший на порядок, сделан пример реально охватывающий большую нужную часть программирования и проектирования. Потом тут коротко и понятно как в среде Windows, так и в ARM! Профи возможно начнут спорить — но у каждого свой опыт и свои предпочтения.
Хочу выразить автору свою искреннюю благодарность за терпеливую и бескорыстную помощь. На мой взгляд, прекрасный пример для освоения ARM начинающими, в нем есть довольно сложная идея и её простая реализация, комплексное решение – не просто помигать светодиодами, а управление от ПК и связь МК с ПК по USB. Для желающих повторить подчеркиваю – нужно взять примеры с этого сайта, а не с сайта автора.
Комментарии (48)
RSS свернуть / развернуть