本文主要讲述的是在STM32F407VE板子上通过STMCube生成的USB HOST CDC 程序驱动CH340进行数据传输。
创作背景:
打算用RT1052开发一个项目,由于项目中用到的传感器比较多,像雷达,ublox等需要通过UART进行通讯,基本分配之后占用了8个串口。而RT1052只有8个串口,没有预留串口也没有调试串口,考虑到RT1052有两路USB-OTG而且内置高速PHY,打算一路用作USB-PC接口做U盘用,一路通过USB-HOST扩展接口,其中包括USB-UART,初步打算先挂载CH340进行通讯测试。
调试过程:
想法确定之后打算先通过RT1052的管方开发板进行调试,发现不行,问题是“Device not support”,由于对于usb-host的机制不是特别熟悉,而且NXP的资料感觉不如ST多(其他方面不清楚单片机感觉ST资料比较丰富)。于是为了弄清楚问题出在哪里,打算用STM32F407的一个小开发板进行调试,搞清楚机制,看看问题到底出在哪里。
ST32F407调试:
步骤一: 通过STM32Cubemx生成一个工程不带操作系统(便于分析),只包括USART1,USBhost_CDC功能。USART1为了 调试打印信息。
步骤二:通过USBlyzer抓取CH340串口的设备信息,进行对比,查找差异。
步骤三:调试代码,进行信息对比。
代码分析:
STM32Cubemx生成的代码主要如下:
int main()
{
MX_USB_HOST_Init();
while(1)
{
MX_USB_HOST_Process();
}
}
MX_USB_HOST_Init(); 主要包括,初始化US_HOST状态机,注册用户回调函数 USBH_UserProcess();
注册USBH_CDC_CLASS结构体,也是为了注册回调函数
MX_USB_HOST_Process(); HOST状态机
1.HOST_IDLE 状态,等待 USB_DEVICE设备的插入,一旦CH340设备插入,引起中断,中断函处理具体的中断信息
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; //进行入下一个状态
}
2. HOST_WAIT_FOR_ATTACHMENT等待接入
中断处理 void USBH_LL_PortEnabled(USBH_HandleTypedef *phost)
{
phost->device.PortEnalbed = 1U;
}
然后 phost->gState = HOST_DEV_ATTACHED; //进入下一个状态设备接入
3. HOST_DEV_ATTACHED
if(phost->pUser !=NULL)
{
phost->pUser(phost, HOST_USER_CONNECTED); //如果初始化注册用户函数,则调用用户处理
}
等待100ms的复位,主要是为了电路稳定。分配pip_out pip_in的端点,默认使用 USBH_EP_CONTROL 0
4. HOST_ENUMMERATION 进入设备枚举阶段
status = USBH_HandleEnum(phost); 这个函数是枚举状态机,逐个分析。
设备枚举阶段:
(1) ENUM_IDLE
ReqStatus = USBH_Get_DevDesc(phost,8)默认采用8字节,获取设备描述符信息
然后调用 USBH_GetDescriptor() 函数,这个函数继续调用 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:
N/A
到此,枚举结束。
5.进入HOST_SET_CONFIGURATION
6.进入HOST_SET_WAKUP_FEATURE
这里显示失败,但是我没有关注继续,HOST_CHECK_CLASS
7. 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配置就设置完成。
8.HOST_CLASS_REQUEST
这里请求的时候失败,我觉得是发送具体信息的问题,由于具体发送什么我需要查资料,暂时不理会,直接
status = USBH_OK然后 phost->gState = HOST_CLASS跳转执行
默认是19200波特率
9. HOST_CLASS
phost->pActiveClass->BgndProcess(phost); 调用USBH_CDC_CLASS的函数
USBH_CDC_Process()这时一直处于IDLE状态,等待设定参数或者收发数据。
10.数据收发
在数函数中调用 USBH_CDC_Transmit(&hUsbHostFS,Sendbuf, 32);
然后不断调用 USBH_CDC_Receive进行数据解析。实际验证可以收发。
程序中主函数没有详细处理,直接自发自收的。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/k1542308627/article/details/107285898/