You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/** @defgroup usbd_cdc_Exported_Defines * @{ */#defineCDC_IN_EP 0x81U /* EP1 for data IN */#defineCDC_OUT_EP 0x01U /* EP1 for data OUT */#defineCDC_CMD_EP 0x82U /* EP2 for CDC commands */
显然对不上号,所以我这里把CDC_OUT_EP改为0x02U(EP2 OUT):
/** @defgroup usbd_cdc_Exported_Defines * @{ */#defineCDC_IN_EP 0x81U /* EP1 for data IN */#defineCDC_OUT_EP 0x02U /* EP1 for data OUT */#defineCDC_CMD_EP 0x82U /* EP2 for CDC commands */
之前写过一篇使用STM32虚拟串口功能的文章:实现USB CDC通信,但是这个有个很大的问题,它的Windows驱动的数字签名过期了,我在我的电脑里搜索了一下,发现有两个驱动:
不过很可惜,这两个驱动都过期了:
这就直接导致在Windows上使用ST自己的虚拟串口需要强制跳过数字签名这一步,而每次电脑重启之后Windows就会恢复默认设置,最麻烦的是每次还必须通过重启设置,不过Linux下倒没这个问题,因为数字签名是Windows自己搞出来的东西。
但还是很烦,所以我决定抛弃ST官方的虚拟串口驱动,正好我找到了别人用STM32模拟CH341的代码:blackmiaool/STM32_USB_CH341 ,但这已经是五六年前的代码了,当时还是标准库,所以我决定把它用HAL库实现。
踩了一些坑,这玩意儿花了我三天时间,主要还是对USB协议不太熟悉,下面就按照我踩坑的时间顺序记录。
第一天:让电脑识别为CH340
这一步很简单,只需要改变设备描述符就行了,具体更改如下:
使用STM32CubeMX生成代码
这里修改以下PID和VID,然后字符串名称就随便写,点击生成代码。
修改设备描述符
在
usbd_desc.c
里面,修改USBD_FS_DeviceDesc
变量:修改设备配置描述符
然后修改
usbd_cdc.c
文件里面的USBD_CDC_CfgFSDesc
变量(因为我配置的Full Speed,如果是其他速度就修改对应的变量就行):然后编译下载,插上USB,电脑就会识别到CH340了:
不过这里有感叹号,点开发现是因为Windows有的请求失败,这是显然的,因为我们还没有写相关的东西呢。
第二天:响应Windows请求
这里先介绍一下USB的请求类型。
USB规范定义了11个标准命令,它们分别是:Clear_Feature、Get_Configuration、Get_Descriptor、Get_Interface、Get_Status、Set_Address、Set_Configuration、Set_Descriptor、Set_Interface、Set_Feature、Synch_Frame。所有USB设备都必须支持这些命令(个别命令除外,如Set_Descriptor、Synch_Frame)。
所有的命令虽然有不同的数据和使用目的,有的USB命令结构是一样的。下表所示为USB命令的结构:
生成的代码中处理USB请求的代码在
usbd_cdc.c
中:经过代码分析可以发现,官方代码并没有处理“厂商”的请求,即
bmRequest
的五六位为10的请求,所以需要修改一下:然后编译下载,插上USB,哈哈,感叹号就没了,其实之前是因为厂商的请求代码都将其处理为错误情况,所以Windows会请求失败。
但其实我们还是没有处理Windows的请求,所以真正使用时连串口都打不开:
这里我参考了CH340的Linux驱动源码,当中有以下函数:
可以看到,上位机对CH340的初始化有几步,会发送好几个请求,其中任何一个不成功都会导致初始化失败,于是我根据这些请求编写了对应的处理函数,具体如下。
我将所有的处理代码都放在了一个单独的文件
ch340.c
:然后将这个处理函数添加到之前的请求处理函数中:
然后编译下载,插上USB,打开串口助手,成功打开串口!
但是还有问题,打开了串口却发送不了数据:
第三天:实现串口收发
这问题卡了我挺久的,甚至还跑去之前用标准库模拟CH341那哥们那里请教,结果他这样回复:
而且作者还顺手把仓库设置为只读模式,啊,看来只能看我自己了。
我选择了
USBlyzer
工具进行USB抓包,看看究竟是通信中的哪个步骤出了问题(软件下载地址)抓到的结果如下:
可以看出这里并不是USB的请求出了问题,而是数据传输阶段出了问题,经过一系列查找资料后我突然意识到,可能是终端开的不对。
再回头看前面改的设备描述符中是这样写的:
我发现这里使用的是
EndPoint 1 IN
、EndPoint 2 IN
、EndPoint 2 OUT
、而ST自己的代码里默认使用的是:显然对不上号,所以我这里把
CDC_OUT_EP
改为0x02U
(EP2 OUT):编译下载,插上USB,发送数据,成功!
现在串口的接收功能已经实现了,然后实现串口的发送功能。
ST官方代码的发送使用的是EP1,但CH340应该使用EP2,这里修改
usbd_cdc.c
中的USBD_CDC_TransmitPacket
函数,将其中的CDC_IN_EP
改为CDC_CMD_EP
:最后为了测试,参照实现USB CDC通信实现一个复读机:
编译下载,插上USB,发送数据:
成功!
参考
The text was updated successfully, but these errors were encountered: