Jump to content

    
Sign in to follow this  
Mazay

STM32F103 виртуальный СОМ-порт (USB CDC) и сообщение WM_DEVICECHANGE

Recommended Posts

На STM32F103 поднят виртуальный СОМ-порт (USB CDC). Используется стандартная библиотека от STM. Все работает. Байты летают туда-сюда. Однако, при отлаживании устройства, наверно многие обратили внимание, что после перезагрузки/перепрошивки контроллера, виртуальный порт «отваливается». Однако во всех доступных мне терминальных программах он так и остается открытым. Данные при этом естественно никакие не идут. Если в терминальной программе закрыть порт, то открыть его не получится. Нужно опять перезагрузить микроконтроллер и только тогда снова открыть порт. Т.е. правильная последовательность действий должна быть такая:

1. Закрыть порт в терминальной программе.

2. Перезагрузить/препрошить и т.д.

3. Открыть порт в терминальной программе.

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

 

В самописной терминальной программе решил оптимизировать этот процесс. Реализовал получение от Windows системных сообщений WM_DEVICECHANGE указывающих на изменения в устройствах. Из этого сообщения нужны события DBT_DEVICEARRIVAL и DBT_DEVICEREMOVECOMPLETE – подключение и удаление устройства соответственно. Далее можно проанализировать Тип устройства которое подключилось или было удалено из системы. Наиболее интересен тип DBT_DEVTYP_PORT, который как раз и является указывающим на Последовательный или Параллельный порт. И вот тут с устройством на STM32 возникла неприятность. При подключении или отключении STM32 событие с типом устройства DBT_DEVTYP_PORT не присылается! Помимо типа DBT_DEVTYP_PORT есть общий тип DBT_DEVTYP_DEVICEINTERFACE указывающий вообще на любое устройство. Вот приходит только событие с этим типом и все. Я проверил имеющиеся у меня переходники USB<->COM на FT232, CPxxx, PL2303 и китайской CH340. При их подключении/отключении помимо событий с общим типом DBT_DEVTYP_DEVICEINTERFACE всегда приходит событие с типом DBT_DEVTYP_PORT по которому легко определить, что конфигурация портов изменилась, а получив из возвращаемой структуры поле Name, можно сразу определить какой порт подключился/удалился. Собственно вопросы к знатокам: В какую сторону стоит копнуть, чтоб и при подключение/удалении STM32 получать сообщение DBT_DEVTYP_PORT? Кто отвечает за рассылку этих сообщений? Я подозреваю, что драйвер, а он используется стандартный виндовый usbser.sys. В файле stmcdc.inf используемом при установке драйвера виртуального COM-порта прописан GUID класса портов. Так, что эта часть кажется правильной. В системе, в диспетчере устройств STM32 USB CDC присутствует в классе портов. WinAPI’шная функция SetupDiEnumDeviceInfo() возвращает среди прочих портов и STM32. Т.е. Винда знает, что это последовательный порт, но сообщения об этом не приходят.

 

Share this post


Link to post
Share on other sites
Собственно вопросы к знатокам: В какую сторону стоит копнуть, чтоб и при подключение/удалении STM32 получать сообщение DBT_DEVTYP_PORT? Кто отвечает за рассылку этих сообщений?

Т.е. Винда знает, что это последовательный порт, но сообщения об этом не приходят.

Положение дел еще хуже: в аналогичной ситуации с SIM800C даже парой DBT_DEVICEARRIVAL и DBT_DEVICEREMOVECOMPLETE не обойтись.

Так как совершенно один и тот же драйвер на одной машине выдавал "MTK USB port", а на другой "Mеdiatek USB port".

Остается надеяться, что не поменяется пара vid, pid и отслеживать через нее.

 

Share this post


Link to post
Share on other sites

Тут

http://stackoverflow.com/questions/2899862...lete-on-wm-devi

пишут что это не порт...

У меня после добавления флага DEVICE_NOTIFY_ALL_INTERFACE_CLASSES начало приходить с типом DBT_DEVTYP_PORT - со структурой PDEV_BROADCAST_PORT - и там правильное имя

 

зы: тестировалось с USB CDC, W10/64, для CDC не требуется inf файлов, винда теперь его знает.

post-28634-1486725428_thumb.png

Edited by Genadi Zawidowski

Share this post


Link to post
Share on other sites

Проблема в драйвере usbser.sys, он не регистрирует отправку событий PnP. Можно обкостылить добавив в .inf файл драйвер фильтр serenum.sys.

Но возникнут другие проблемы: потеряется цифровая подпись от ST и этот драйвер предназначен для обнаружения PnP устройств на COM порту.

При добавлении serenum.sys, Windows при некоторых последовательностей байт может находить мышки, трекболы и.т.д.

Проблема в usbser.sys решена в Windows 10, там уведомления о подключении и отключении присылаются.

 

Переходники от FTDI и Prolific используют свои драйверы, за место usbser.sys. Они даже не USBCDC класс.

 

При использовании serenum.sys уведомления будут приходить только при регистрации класса GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR.

Номер COM порта можно узнать из реестра, открыв ключ реестра с помощью SetupDiOpenDevRegKey.

Share this post


Link to post
Share on other sites
Проблема в драйвере usbser.sys, он не регистрирует отправку событий PnP.

 

Да, видимо вы правы. Нашел в закромах плату с атмеловским AT91SAM7 и тоже включенным CDC - ситуация аналогична с STM32. Драйвер используется все тот-же usbser.sys и в событиях DBT_DEVICEARRIVAL и DBT_DEVICEREMOVECOMPLETE приходит только в виде "USB Raw Device" {a5dcbf10-6530-11d2-901f-00c04fb951ed}. По умолчанию, в отличии от портов, эти сообщения не приходят и нужно или подписываться на них, или "включать" все сообщения (DEVICE_NOTIFY_ALL_INTERFACE_CLASSES).

 

Видимо придется при получении событий об добавлении/удалении устройств перезапрашивать у Винды список портов. Причем замечена одна особенность с китайским переходником USB<->COM CH340. Если порт открыт в терминалке и выдернуть переходник, то сообщение об удалении Порта приходит. Но в реестре он остается в списке имеющихся портов и можно писать в него не получая ошибки. Удаляется он из списка портов только после того, как его закрыли. Поэтому по приходу событий DBT_DEVICEARRIVAL и DBT_DEVICEREMOVECOMPLETE просто перечитать реестр не помогает. Порт еще будет присутствовать. Надо или разбирать тип DBT_DEVTYP_PORT для извлечения Имени, или получать список портов функцией SetupDiEnumDeviceInfo().

Share this post


Link to post
Share on other sites

СТранно...

USB CDC на десятке такое дает:

'DBT_DEVNODES_CHANGED'

'DBT_DEVNODES_CHANGED'

'DBT_DEVNODES_CHANGED'

'DBT_DEVICEREMOVECOMPLETE: COM5'

'DBT_DEVNODES_CHANGED'

'DBT_DEVICEREMOVECOMPLETE: COM4'

'DBT_DEVNODES_CHANGED'

'DBT_DEVNODES_CHANGED'

'DBT_DEVICEARRIVAL: COM5'

'DBT_DEVNODES_CHANGED'

'DBT_DEVICEARRIVAL: COM4'

'DBT_DEVNODES_CHANGED'

'DBT_DEVNODES_CHANGED'

'DBT_DEVNODES_CHANGED'

'DBT_DEVNODES_CHANGED'

'DBT_DEVNODES_CHANGED'

'DBT_DEVNODES_CHANGED'

'DBT_DEVICEREMOVECOMPLETE: COM5'

'DBT_DEVICEREMOVECOMPLETE: COM4'

'DBT_DEVNODES_CHANGED'

'DBT_DEVNODES_CHANGED'

'DBT_DEVICEARRIVAL: COM5'

'DBT_DEVNODES_CHANGED'

'DBT_DEVICEARRIVAL: COM4'

'DBT_DEVNODES_CHANGED'

'DBT_DEVNODES_CHANGED'

Составное кстройство с двумя компортами и аудиоплатой. Это я кабель отсоединял и включал. W10.

 

Что у Вас с разными девайсами будет с тестовой программой отсюда https://code.google.com/archive/p/arduino/issues/934

DevNfyTest.rar

Edited by Genadi Zawidowski

Share this post


Link to post
Share on other sites

класс Ports для USB CDC ACM начал поддерживаться только с десятки, поэтому уведомления в десятке будут приходить.

 

 

https://msdn.microsoft.com/en-us/library/wi...0(v=vs.85).aspx

 

 

 

Communications and CDC Control (02h)

 

Видимо придется при получении событий об добавлении/удалении устройств перезапрашивать у Винды список портов. Причем замечена одна особенность с китайским переходником USB<->COM CH340. Если порт открыт в терминалке и выдернуть переходник, то сообщение об удалении Порта приходит. Но в реестре он остается в списке имеющихся портов и можно писать в него не получая ошибки. Удаляется он из списка портов только после того, как его закрыли. Поэтому по приходу событий DBT_DEVICEARRIVAL и DBT_DEVICEREMOVECOMPLETE просто перечитать реестр не помогает. Порт еще будет присутствовать. Надо или разбирать тип DBT_DEVTYP_PORT для извлечения Имени, или получать список портов функцией SetupDiEnumDeviceInfo().

 

Насколько помню до Windows 10 usbser.sys кроме DBT_DEVNODES_CHANGED в WM_DEVICECHANGE ничего больше не присылает.

Если всё же приходят DBT_DEVICEARRIVAL и DBT_DEVICEREMOVECOMPLETE, то все порты перебирать не обязательно.

Номер порта можно получить из реестра зная путь к интерфейсу.

 

lParam указывает на DEV_BROADCAST_HDR, для интерфейса он будет типа DBT_DEVTYP_DEVICEINTERFACE

DBT_DEVTYP_DEVICEINTERFACE->dbcc_name это путь к устройству

SetupDiOpenDeviceInterface даст интерфейс

SetupDiGetDeviceInterfaceDetail даст инфу по интерфейсу

SetupDiOpenDevRegKey откроет ключ в HKLM\SYSTEM\ControlSet\Enum\USB для этого интерфейса, откуда и можно узнать номер порта

Share this post


Link to post
Share on other sites

Если нет стойкой ненависти к паскалю - ниже прилагаю честно подсмотренный где-то модуль, которым пользуюсь в одном проекте.

Им определяю подключение\отключение последовательных портов

U_Usb.rar

 

Share this post


Link to post
Share on other sites
Поэтому по приходу событий DBT_DEVICEARRIVAL и DBT_DEVICEREMOVECOMPLETE просто перечитать реестр не помогает. Порт еще будет присутствовать.

Вот не считаю правильным лезть в реестр что-то смотреть. Или этот механизм вполне документирован Mirosoft-ом?

Если есть альтернатива сделать то же самое через API - стоит этим воспользоваться.

 

Попалась мне недавно одна поделка, вроде как через реестр определяет наличие драйвера STM VCP, ставит его если не обнаружила....

Может на XP это и прокатит, а на старших ОС со стороны пользователя это выглядит как некая неизвестная прграмма без всяких подписей хочет для запуска права администратора! :wacko:

 

Share this post


Link to post
Share on other sites

Всем доброго времени суток.

Я тоже сталкивался с проблемой, когда терминальная программа (для СОМ-портов), при внезапном отключении устройства теряла порт и при повторном подключении его не находила.

Тем не менее, недавно закончил коммерческую программу для РС, которая работает с устройством, прикидывающимся СОМ-портом. У заказчика было жесткое требование, чтобы программа устойчиво работала с устройством при его горячем отключении-подключении.

 

Ни чего сложного не придумывал. Ни какие хитрые функции не использовал. Обычный АПИ - CreateFile, Read/WriteFile, CloseHandle - весь набор.

1. Устройство находится через SetupAPI. Открывается как обычный СОМ-порт с именем "COMxx" в асинхронном режиме.

2. Все операции ввода вывода происходят в отдельном потоке в бесконечном цикле. Поток всегда ожидает команды от приложения через событие Start.

3. Цикл завершается отправкой сообщения приложению через PostMessage.

4. При любом значении GetLastError отличным от 0 закрываем устройство в потоке, спим 5 секунд и пытаемся открыть устройство.

 

Испытано на Windows XP, 7 32/64 бит. Работает железно. Можно втыкать-вытыкать порт сколько угодно - все работает.

Share this post


Link to post
Share on other sites

Mazay, у Вас pull-up на D+ отключаемый или нет? Если нет - сделайте отключаемый.

И драйвера CDC тут ни при чём. У меня HID device, соответственно никаких ST-шных дров нет вообще, а симптомы те же. Отключаемый pull-up должен помочь. Правда руки до этого у меня ещё не дошли :( . На него винда всегда корректно реагирует.

 

А переподключение кабеля по сути и реализует то самое переподключение резюка. Никакие PnP сообщения тут, имхо ни при чём.

Я эту байду наблюдал и на AVR+ObDev лет этак 8 назад, наблюдаю её и здесь. Винды разные были XP, 2003 сервер, семёрка. Более высоких не пробовал, но думается, один хрен.

Share this post


Link to post
Share on other sites

Если в операционной системе кто-то держит открытым порт, соответственно и его имя (в именах kernel devices) занято... Похоже, про появлении нового устройств в системе новое создается без имени или с игнорированием ошибок - но до него по имени "не достучатсья". ИЛи символическая ссылка от COMxx не создается.

 

Edited by Genadi Zawidowski

Share this post


Link to post
Share on other sites
Всем доброго времени суток.

Я тоже сталкивался с проблемой, когда терминальная программа (для СОМ-портов), при внезапном отключении устройства теряла порт и при повторном подключении его не находила.

 

Вот в этом и стоит вопрос, что программе терминалу нужно по большей части только читать из порта, а ReadFile не завершается с ошибкой, если устройство отключить.

Программа просто продолжает читать из порта ноль байт. Не получается отследить момент когда устройство отсоединено.

И если не успеть закрыть порт, до того как устройство вставили повторно, получим Error = 2 при вызове CreateFile.

 

К ошибке приводит только запись с отличным от нуля количеством байт. Такое поведение характерно только для usbser.sys.

 

И драйвера CDC тут ни при чём. У меня HID device, соответственно никаких ST-шных дров нет вообще, а симптомы те же. Отключаемый pull-up должен помочь. Правда руки до этого у меня ещё не дошли :( . На него винда всегда корректно реагирует.

 

Как раз драйвер CDC очень даже причем. ST драйвера никогда не писала, их драйвер для CDC это только цифровая подпись для stmcdc.inf,

чтоб с их VID PID установить usbser.sys который уже имеется в Windows. Для HID устанавливается функциональный драйвер по классу устройств,

и для его привязки не важен VID PID.

 

Share this post


Link to post
Share on other sites
Как раз драйвер CDC очень даже причем. ST драйвера никогда не писала, их драйвер для CDC это только цифровая подпись для stmcdc.inf,

чтоб с их VID PID установить usbser.sys который уже имеется в Windows.

 

Всем спасибо за помощь! Отдельное спасибо уважаемому DeNi за подробное объяснение проблем с usbser.sys и советы по использованию WinAPI.

Не написал в первом посте - ОС действительно старая WinXP/Win7. При включении-выключении, перзагрузке, выдергивании и т.д. устройств использующих виндовый драйвер usbser.sys все-таки приходят сообщения с событиями DBT_DEVICEARRIVAL и DBT_DEVICEREMOVECOMPLETE и типом DBT_DEVTYP_DEVICEINTERFACE. Из них уже можно получить путь к устройству и реализовать автоматическое закрывание порта, а в будущем и автоматическое переподключение.

 

 

 

А вот возник такой, непонятный для меня вопрос при перечислении портов имеющихся в системе:

Примерная рыба кода:

 

hDevInfo = SetupDiGetClassDevs(&ClassGuid, NULL, NULL, /*DIGCF_DEVICEINTERFACE |*/ DIGCF_PRESENT);  // Получаю хендл на список устройств имеющихся в системе

for(int n = 0; SetupDiEnumDeviceInfo(hDevInfo, n, &DevInfoData); n++)  // Перечисляю устройства
{
   .......
    for(int m = 0; SetupDiEnumDeviceInterfaces(hDevInfo, &DevInfoData, &ClassGuid, m, &DeviceInterfaceData); m++)  // Перечисляю интерфейсы для устройства
    {
          ........
    }
}

 

И вот если в функции SetupDiGetClassDevs указан только флаг DIGCF_PRESENT то определяются все порты имеющиеся в системе (в том числе и использующие usbser.sys), но функция SetupDiEnumDeviceInterfaces возвращает ошибку ERROR_NO_MORE_ITEMS для любого порта. Если я добавляю флаг DIGCF_DEVICEINTERFACE, то интерфейсы находятся, но из списка устройств исчезают порты как раз использующие usbser.sys. Вот логи небольшой программки, набросанной для лучшего понимания работы с винайпишными функциями Практической пользы в получении интерфейсов при перечислении портов я пока не вижу, это скорее тренировка инженера-железячника.

 

Флага DIGCF_DEVICEINTERFACE нет:

 

Описание класса: Порты (COM и LPT)

====================================================

Порт Найден: Последовательный порт (COM1)

Поиск Интерфейсов закончен!

====================================================

Порт Найден: PCI Serial Port (COM22)

Поиск Интерфейсов закончен!

====================================================

Порт Найден: PCI Serial Port (COM21)

Поиск Интерфейсов закончен!

====================================================

Порт Найден: PCI Parallel Port (LPT1)

Поиск Интерфейсов закончен!

====================================================

Порт Найден: AT91 USB to Serial Converter (COM15)

Поиск Интерфейсов закончен!

====================================================

Порт Найден: STMicroelectronics Virtual COM Port (COM4)

Поиск Интерфейсов закончен!

Поиск Устройств закончен!

 

Флаг DIGCF_DEVICEINTERFACE добавлен:

 

Описание класса: Порты (COM и LPT)

====================================================

Порт Найден: Последовательный порт (COM1)

Интерфейс Найден: Порядковый номер: 0

\\?\acpi#pnp0501#1#{4d36e978-e325-11ce-bfc1-08002be10318}

Имя порта из Реестра (SetupDiOpenDevRegKey/RegQueryValueEx): COM1

Поиск Интерфейсов закончен!

====================================================

Порт Найден: PCI Serial Port (COM22)

Интерфейс Найден: Порядковый номер: 0

\\?\mf#pci#ven_9710&dev_9835&subsys_00121000&rev_01#6&8e99d9a&0&0000e6#child0000#{4d36e978-e325-11ce-bfc1-08002be10318}

Имя порта из Реестра (SetupDiOpenDevRegKey/RegQueryValueEx): COM22

Поиск Интерфейсов закончен!

====================================================

Порт Найден: PCI Serial Port (COM21)

Интерфейс Найден: Порядковый номер: 0

\\?\mf#pci#ven_9710&dev_9835&subsys_00121000&rev_01#6&8e99d9a&0&0000e6#child0001#{4d36e978-e325-11ce-bfc1-08002be10318}

Имя порта из Реестра (SetupDiOpenDevRegKey/RegQueryValueEx): COM21

Поиск Интерфейсов закончен!

Поиск Устройств закончен!

 

Share this post


Link to post
Share on other sites
lParam указывает на DEV_BROADCAST_HDR, для интерфейса он будет типа DBT_DEVTYP_DEVICEINTERFACE

DBT_DEVTYP_DEVICEINTERFACE->dbcc_name это путь к устройству

SetupDiOpenDeviceInterface даст интерфейс

SetupDiGetDeviceInterfaceDetail даст инфу по интерфейсу

SetupDiOpenDevRegKey откроет ключ в HKLM\SYSTEM\ControlSet\Enum\USB для этого интерфейса, откуда и можно узнать номер порта

 

Все получилось. Еще раз спасибо!

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this