注:本文档主要用于stm32做usb fs主机时,将EC800M虚拟串口(CDC)接入进行AT通信使用
参考文档:
使用stm32cubemx的usb-host-cdc库驱动EC20模块_stm32 ec20 usb-CSDN博客
使用stm32cubeide的usb-host-cdc库驱动EC20模块_ec20 stm32_郑州-刘明鑫的博客-CSDN博客
STM32 USB HOST CDC 驱动CH340串口_stm32 otg host com-CSDN博客
一、使用mx cube创建usb host工程
1、设置USB频率为48MHz
2、USB_OTG_FS, 选择 Host_Only,别的不用动
选择 Middleware 标签下的USB_HOST
Class For FS IP 选择图示选项 Communication Host Class (Virtual Port com)
USBH_MAX_NUM_ENP… 3
USBH_MAX_NUM_INTER… 5
USBH_MAX_NUM_SUPP… 1
USBH_MAX_NUM_CONF… 4
二、修改源码
说明:11、ST USB HOST概述_st freertos usb host驱动-CSDN博客
1、修改接口类型代码(CDC CLASS)
找到usbh_cdc.h,第53行,修改为0xFFU
2、修改CDC使用的interface和对应的endpoint
/*
* 注:
* cubemx生成的例程中,标准的CDC类设备,1个配置描述符中需要2接口
* 其中一个为Communication Interface Class, 该接口需要一个方向为in的Ep
* 另外一个为Data Interface Class, 该接口需要一个方向为in的Ep和一个方向为out的Ep
* 所以USBH_CDC_InterfaceInit函数,调用了两次USBH_FindInterface函数
* 查找两个匹配的Interface, 分别进行配置
*
* USB-TTL串口工具,debug状态下查询设备描述符结构体中,只有一个接口
* 但是该接口拥有3个Ep, 2个方向为in, 1个方向为out.
* 由此猜测,串口工具并没有将Interface分开
* 经测试, Communication Interface使用的Ep为2, Data Interface使用Ep0,1
*
* Ec20模块,可以读到5个Interface,但是只有Interface 1 2 3 有3个Ep,0 和 4 只有2个Ep
* 经测试,接口AT指令的串口Interface为 2.
* Interface 2中,Communication Interface使用的Ep为0
* Data Interface使用的Ep为1 和 2
*/
① 找到usbh_cdc.c,第158行修改部分如图所示
注:根据实际情况修改,当endpoint为3时为需要的点位
EC800M为最后一个点位。
② 还是 usbh_cdc.c,注释掉 interface = …这两行,然后将图中画红色框框部分的5个Ep_Desc[0]修改为 Ep_Desc[2]
注:EC800G的AT口只有2个endpoint,目前暂未找到如何配置的方法
③ 修改usbh_core.c中此处,修改为3个endpoint的点位位置
3、修改AT通信串口波特率
usbh_cdc.c,找到函数 USBH_CDC_ClassRequest,按如图所示修改。
4、修改USB CDC接收完成回调函数
usb_host.c,在 USER CODE BEGIN 0 部分添加如下代码
5、在MX_USB_HOST_Process函数中循环调用CDC receive函数进行数据接收
6、添加USB CDC发送数据
当设备连接成功并动作后,发送AT命令等待EC800G模块回复
三、USB连接流程
1、MX_USB_HOST_Init()
主要包括,初始化US_HOST状态机,注册用户回调函数 USBH_UserProcess();注册USBH_CDC_CLASS结构体,也是为了注册回调函数
2、MX_USB_HOST_Process()
usb host状态机切换
① HOST_IDLE状态
等待USB_DEVICE设备的插入,一旦设备插入,引起中断,中断函处理具体的中断信息
USBH_StatusTypedef USBH_LL_Connect(USBH_HandleTypedef* phost) { phost->device.is_connected = 1U; .... }
在USBH_Process()中状态机HOST_IDLE中检测到插入
if( phost->device.is_connected) { phost->gState= HOST_DEV_WAIT_FOR_ATTACHMENT; //进行入下一个状态 }
② HOST_WAIT_FOR_ATTACHMENT等待接入
中断处理 void USBH_LL_PortEnabled(USBH_HandleTypedef *phost) { phost->device.PortEnalbed = 1U; } 然后 phost->gState = HOST_DEV_ATTACHED; //进入下一个状态设备接入
③ HOST_DEV_ATTACHED
if(phost->pUser !=NULL) { phost->pUser(phost, HOST_USER_CONNECTED); //如果初始化注册用户函数,则调用用户处理 } 等待100ms的复位,主要是为了电路稳定。分配pip_out pip_in的端点,默认使用 USBH_EP_CONTROL 0
④ HOST_ENUMMERATION 进入设备枚举阶段
status = USBH_HandleEnum(phost); 这个函数是枚举状态机,逐个分析。
设备枚举阶段:
(1) ENUM_IDLE
ReqStatus = USBH_Get_DevDesc(phost,8)默认采用8字节,获取设备描述符信息
然后调用 USBH_GetDescriptor() 函数,接着继续调用 USBH_CtlReq()函数
USBH_CtlReq是控制请求函数,具体处理通过 USBH_HandleControl()是个状态机。
可以理解为,数据请求发送了,然后等待数据接收即可。
USBH_ParseDevDesc(); 发送完数据,收到数据进行解析,这里开始处理设备描述符
由于第一次只接收到8个字节,解析后如下:
(2)ENUM_GET_FULL_DEV_DES
这次获取全部信息
下图是PC 通过USBlyzer获取的信息
这个有个地方需要注意 就是 bDeviceClass 0xFF Verdor-specfic 等会解释
(3)ENUM_SET_ADDR
这里程序进行设置地址,我没有理会,默认ok
根据实际size重新设定 端点信息。
(4) ENUM_GET_CFG_DESC
(5)ENUM_GET_FULL_CFG_GESC获取配置描述符
USBH_Get_CfgDesc(phost, ...);
PC获取的配置信息
(6)ENUM_GET_MFC_STRING_DESC,ENUM_GET_PRODUCT_STRING_DESC,获取不到,不影响
(7)ENUM_GET_SERIALNUM_STRING_DESC:
获取到USB-2.0Serial
(8)ENUM_GET_SERIALNUM_STRING_DESC:
到此,枚举结束。
⑤ HOST_SET_CONFIGURATION
⑥ HOST_SET_WAKUP_FEATURE
这里显示失败,但是我没有关注继续,HOST_CHECK_CLASS
⑦ HOST_CHECK_CLASS
这里是关键,一开始usbhost初始化的时候注册的是USBH_CDC_CLASS类,这个结构体中初始化的ClassCode是USB_CDC_CLASS这个数值等于2,但是刚才我获取的 bDeviceClass 是 0xFF,PC显示厂商自定义设备。
所以在这里我增加一个宏定义USB_CDC_CLASS_CH340 0xFF,初始化,USBH_CDC_CLASS,
这样 程序 phost-pActiveClass = phost-pClass[idx]能够执行,能获指向USBH_CDC_CLASS这个注册类。
接下来执行初始化,phost-pActiveClass->Init(pHost), 之后 phost->gState = HOST_CLASS_REQUEST;
调用USBH_CDC_InterfaceInit( phost) 这里需要注意,
USBH_FindInterface,这个函数是根据第2,3,4个参数
COMMUNICATION_INTERFACE_CLASS_CODE, 0X02
ABSTRACT_CONTROL, 0X02
COMMON_AT_COMMAND 0X01,
进入程序发现 if( (pif->bInterfaceClass == class) || (Class == 0xFFU)) &&
(pif->bInterfaceSubClass==SubClass)|| (SubClass == 0xFFU)) &&
.......)
这里3个参数都不匹配,因为程序是根据CDC进行查找的,而CH340的 class = 0xFF, SubClass = 0x01, Protocol = 0x02
所以增加宏定义增加CH340对应的数值,之后pip配置就设置完成。
⑧ HOST_CLASS_REQUEST
这里请求的时候失败,我觉得是发送具体信息的问题,由于具体发送什么我需要查资料,暂时不理会,直接 status = USBH_OK然后 phost->gState = HOST_CLASS跳转执行 默认是19200波特率
⑨ HOST_CLASS
phost->pActiveClass->BgndProcess(phost); 调用USBH_CDC_CLASS的函数
USBH_CDC_Process()这时一直处于IDLE状态,等待设定参数或者收发数据。
3、数据收发
在数函数中调用 USBH_CDC_Transmit(&hUsbHostFS,Sendbuf, 32);
然后不断调用 USBH_CDC_Receive进行数据解析。实际验证可以收发。
程序中主函数没有详细处理,直接自发自收的。