Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
USB
STM32
USB主机_host
stm32 usb host 解决驱动挑盘、读写死机、鼠标、键盘读写问题。
发布于 2021-12-09 09:33:06 浏览:3538
订阅该版
**rt_thread stm32f407 USB host disk 调试中挑盘、读写死机的问题分享!** 1、工程中,HAL库的延时函数没有实现,在枚举时候复位设备过程中,延时不足,造成有些USB能枚举成功,有些则不能,造成挑盘(有的盘确实性能不用延时也能枚举)。`void 2、上面的坑填了以后(其实没填好,为了延时随便写了个),发现能够枚举成功(后来发现能枚举成功还是看运气,个别差点U盘还是是通不过的),RT—THREAD系统能加载U盘进目录。此时用MSH命令ls目录时,HOST死机,用示波器查看USB端口引线,有6MHZ方波持续输出,拔出U盘HOST输出波形犹在,而且HOST无法控制,怀疑HOST控制主机死机了(这个现象无论插手头上的哪个盘都出现)!! 通过实验,每次写CBW封块达到相同次数后就会死机,怀疑FIFO到顶溢出,对照正常寄存器设置值,确实不对。 单步调试运行到相关代寄存器设置码后,查看设置值是正常的,但是全速运行却不能正常赋值。 后来发现在 HAL库的`static HAL_StatusTypeDef USB_CoreReset(USB_OTG_GlobalTypeDef *USBx)`后面增加延时,就能全速运行,且不会HOST死机。分析该函数复位主机后,USB外设不够时间复位,所以不能够正确赋值。经过排查,查出还是`void HAL_Delay(uint32_t Delay){}`编写不正确,延时不足。 控制主机复位延时不足,寄存器初始化不正确。碰巧到寄存器大小初始化不正确时会造成主机控制器死机。 HAL_Delay(uint32_t Delay){1ms}` ``` void HAL_Delay(__IO uint32_t ms) { rt_thread_delay(ms); } ``` 3、系统时钟设置不正确也会导致挑盘。由于之前的调试以为是U盘质量不好,改小点减慢速度,改动了数值,RCC_OscInitStruct.PLL.PLLN = 160;改为了160 忘记改回来为168,导致有的盘能够顺利读写。有的触发TXERR中断,用总线分析仪看总线:主机发送了OUT令牌、DATA0、EOP后等待设备返回响应,但设备没有任何响应,主机判判断传输出错。开始时驱动里加大重试多次也能通过,但是出错概率非常高。后来用示波器能看到同步信号频率异常在5.7MHZ 正常频率在6.0MHZ ,有挑盘的也可以用示波器看看时钟频率对不对,差一点就造成挑盘。有时候设置正确但是晶振有可能偏差。 ``` void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; /**Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /**Initializes the CPU, AHB and APB busses clocks */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE |RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.LSEState = RCC_LSE_ON; RCC_OscInitStruct.LSIState = RCC_LSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 4; RCC_OscInitStruct.PLL.PLLN = 168;//改为了160没有改回来,有的盘能够顺利读写 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 7; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /**Initializes the CPU, AHB and APB busses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); } PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { Error_Handler(); } } ``` 补充:还有个地方延时发现不足个别设备主机也无法枚举 ``` rt_err_t rt_usbh_attatch_instance(uinst_t device) { int i = 0; rt_err_t ret = RT_EOK; struct uconfig_descriptor cfg_desc; udev_desc_t dev_desc; uintf_desc_t intf_desc; uep_desc_t ep_desc; rt_uint8_t ep_index; upipe_t pipe; ucd_t drv; RT_ASSERT(device != RT_NULL); rt_memset(&cfg_desc, 0, sizeof(struct uconfig_descriptor)); dev_desc = &device->dev_desc; /* alloc address 0 ep0 pipe*/ ep0_out_desc.wMaxPacketSize = 8; ep0_in_desc.wMaxPacketSize = 8; rt_usb_hcd_alloc_pipe(device->hcd, &device->pipe_ep0_out, device, &ep0_out_desc); rt_usb_hcd_alloc_pipe(device->hcd, &device->pipe_ep0_in, device, &ep0_in_desc); LOG_W ("enumnation..."); /* get device descriptor head */ //LOG_W ("USB HOST start get DEVICE head descriptor"); ret = rt_usbh_get_descriptor(device, USB_DESC_TYPE_DEVICE, (void*)dev_desc, 8);//取前8字节设备描述符 if(ret != RT_EOK) { LOG_E("USB HOST get DEVICE head descriptor failed"); return ret; } /* reset bus */ //LOG_W ("USB HOST reset bus"); rt_usbh_hub_reset_port(device->parent_hub, device->port); rt_thread_mdelay(500);//**修改加大,由于鼠标通不过** rt_usbh_hub_clear_port_feature(device->parent_hub, i + 1, PORT_FEAT_C_CONNECTION); /* set device address */ //LOG_W ("USB HOST set device address"); ret = rt_usbh_set_address(device);//设置地址 。。。。。。。 以下代码省略 } ``` 4、以上的问题都解决后,还是出现个别盘读写不正常。通过调试,分析协议,原驱动drv_pipe_xfer()不完善对返回的的错误状态,没有能够正确处理,导致当USB设备返回错误时没能正确处理,如果U盘好,不返回错误,那么就可以通过。遇到好的盘可以,但是差的盘就不行了。 例如:对NAK错误返回,没有分in、out两类处理,应该是要分开是IN还是OUT进行处理,如果是OUT 需要重发,IN则不需要发送,忽略即可(官方的中断中已经重启通道接收)。分类以后原驱动的1毫秒等待可以取消。 对IDLE返回也没有分类处理。修改在中断传说中需要对IDLE返回状态处理。 完成量的判断也不区分通道。修改成每个通道单独一个完成量。 还有STALL返回也没有进行处理,导致只要返回STALL就错误。经过以上修改后这个错误出现很少,目前还没完善。 修改原驱动,U盘大部分已经能够枚举成功,并挂载读写,并能用MSH>ls显示目录。 改好的代码: ``` void HAL_HCD_HC_NotifyURBChange_Callback(HCD_HandleTypeDef *hhcd, uint8_t chnum, HCD_URBStateTypeDef urb_state) { rt_completion_done(&urb_completion[chnum]); } static int drv_pipe_xfer(upipe_t pipe, rt_uint8_t token, void *buffer, int nbytes, int timeouts) { int timeout = timeouts*5; HCD_StateTypeDef state; HCD_URBStateTypeDef urbstate; int status = 0; int count = 0; uint8_t direction = (pipe->ep.bEndpointAddress & 0x80) >> 7; while (1) { if (!connect_status) { pipe->status = UPIPE_STATUS_ERROR; return -1; } switch(status) { case 0: rt_completion_init( &urb_completion[pipe->pipe_index] ); drv_hcd_hc_SubmitRequest(&stm32_hhcd_fs, pipe->pipe_index,//通道号 direction, pipe->ep.bmAttributes, token, buffer, nbytes, 0); status = 1; case 1: if(rt_completion_wait( &urb_completion[pipe->pipe_index], timeout ) != RT_EOK) { LOG_E("timeout!"); pipe->status = UPIPE_STATUS_ERROR; return -1; } status = 2; case 2: urbstate = HAL_HCD_HC_GetURBState(&stm32_hhcd_fs, pipe->pipe_index); if(URB_DONE == urbstate)//DONE { pipe->status = UPIPE_STATUS_OK; if (pipe->callback != RT_NULL)//中断传输中会用到 { pipe->callback(pipe); } if (direction)//IN { return HAL_HCD_HC_GetXferCount(&stm32_hhcd_fs, pipe->pipe_index); } return nbytes;//OUT } else if(URB_IDLE == urbstate)//IDLE { if (pipe->ep.bmAttributes == USB_EP_ATTR_INT)//中断传输中返回的NAK响应在服务程序中不更新URB_STATA,才会返回IDLE { rt_thread_delay((pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) > 0 ? (pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) : 1); status = 0; continue; } status = 1; continue; } else if(URB_NOTREADY == urbstate)//设备返回了NAK { if (direction == 0) //out { if ( ++count>50000 ) { LOG_E("NAK"); pipe->status = UPIPE_STATUS_ERROR; return -1; } status = 0;//重发 } else//in { status = 1;//继续等待 } continue; } else if(URB_NYET == urbstate)//高速设备上用 { LOG_E("NYET"); pipe->status = UPIPE_STATUS_ERROR; return -1; } else if(URB_ERROR == urbstate)//发送了out令牌及数据后,在规定规定时间设备没有回应,达到3次,多半是电气故障,如频率不对,线路干扰 { //发送了IN令牌后,但是设备没有任何回应,则会超时。 LOG_E("ERROR"); pipe->status = UPIPE_STATUS_ERROR; if (pipe->callback != RT_NULL) { pipe->callback(pipe); } return -1; } else if(URB_STALL == urbstate)//通道关闭,调试过程中很少遇到所以没有完善处理程序 { LOG_E("STALL"); pipe->status = UPIPE_STATUS_STALL; if (pipe->callback != RT_NULL) { pipe->callback(pipe); } return -1; } else { LOG_E("ERR state"); return -1; } } } } ``` 5、经过以上修改后,读取鼠标设备不正常,会出发数据同步错误DATAERR。 发现问题:在控制传输中,设备的端点最大数据包大小为8时,控制输入总字节大于8字节的时候,需要多次INT才能完成一个事务的时候,会发生数据同步错误。而U盘输入字节多为64字节能一次传输完成不需要翻转,所以不报错。 找到问题:HAL库中下面的HAL_StatusTypeDef HAL_HCD_HC_SubmitRequest()函数对控制传输的IN固定了DATA1不会翻转。个人认为这是BUG。 问题解决:1、重写该函数 2、添加设置手动修改数据翻转的函数。在控制传输用端口0输出的SETUP、DATA0包后,在调用函数把端口1进行输入时,根据协议规定强制翻转该端口为DATA1.把设计到控制传输的IN之前都强制设置该端口为DATA1 读取鼠标设备正常。 逻辑分析仪分析的截图,可以看到设备返回的数据为DATA0,主机原驱动仍然设置为DATA1 ![DSView-211229-220242.png](https://oss-club.rt-thread.org/uploads/20211229/d9ebfebc9adba7e409e9be84c4e3e8ac.png) 重写的函数代码如下: ``` HAL_StatusTypeDef drv_hcd_hc_SubmitRequest(HCD_HandleTypeDef *hhcd, uint8_t ch_num, uint8_t direction,//0 : Output / 1 : Input uint8_t ep_type, uint8_t token,// 0: HC_PID_SETUP / 1: HC_PID_DATA1 uint8_t *pbuff, uint16_t length, uint8_t do_ping) { UNUSED(do_ping); hhcd->hc[ch_num].ep_is_in = direction; hhcd->hc[ch_num].ep_type = ep_type;//事务类型 if (token == 0U) { hhcd->hc[ch_num].data_pid = HC_PID_SETUP; } else { hhcd->hc[ch_num].data_pid = HC_PID_DATA1; } /* Manage Data Toggle *///管理数据切换 switch (ep_type) { case EP_TYPE_CTRL: if ((token == 1U) && (direction == 0U)) /*send data */ { if (length == 0U) { /* For Status OUT stage, Length==0, Status Out PID = 1 */ hhcd->hc[ch_num].toggle_out = 1U; } /* Set the Data Toggle bit as per the Flag */ if (hhcd->hc[ch_num].toggle_out == 0U) { /* Put the PID 0 */ hhcd->hc[ch_num].data_pid = HC_PID_DATA0; } else { /* Put the PID 1 */ hhcd->hc[ch_num].data_pid = HC_PID_DATA1; } } if ((token == 1U) && (direction == 1U)) /*get data */ { /* Set the Data Toggle bit as per the Flag */ if (hhcd->hc[ch_num].toggle_in == 0U) { /* Put the PID 0 */ hhcd->hc[ch_num].data_pid = HC_PID_DATA0; } else { /* Put the PID 1 */ hhcd->hc[ch_num].data_pid = HC_PID_DATA1; } if (length == 0U) { /* For Status OUT stage, Length==0, Status Out PID = 1 */ hhcd->hc[ch_num].data_pid = HC_PID_DATA1; } } break; case EP_TYPE_BULK: if (direction == 0U) { /* Set the Data Toggle bit as per the Flag */ if (hhcd->hc[ch_num].toggle_out == 0U) { /* Put the PID 0 */ hhcd->hc[ch_num].data_pid = HC_PID_DATA0; } else { /* Put the PID 1 */ hhcd->hc[ch_num].data_pid = HC_PID_DATA1; } } else { if (hhcd->hc[ch_num].toggle_in == 0U) { hhcd->hc[ch_num].data_pid = HC_PID_DATA0; } else { hhcd->hc[ch_num].data_pid = HC_PID_DATA1; } } break; case EP_TYPE_INTR: if (direction == 0U) { /* Set the Data Toggle bit as per the Flag */ if (hhcd->hc[ch_num].toggle_out == 0U) { /* Put the PID 0 */ hhcd->hc[ch_num].data_pid = HC_PID_DATA0; } else { /* Put the PID 1 */ hhcd->hc[ch_num].data_pid = HC_PID_DATA1; } } else { if (hhcd->hc[ch_num].toggle_in == 0U) { hhcd->hc[ch_num].data_pid = HC_PID_DATA0; } else { hhcd->hc[ch_num].data_pid = HC_PID_DATA1; } } break; case EP_TYPE_ISOC: hhcd->hc[ch_num].data_pid = HC_PID_DATA0; break; default: break; } hhcd->hc[ch_num].xfer_buff = pbuff; hhcd->hc[ch_num].xfer_len = length; hhcd->hc[ch_num].urb_state = URB_IDLE; hhcd->hc[ch_num].xfer_count = 0U; hhcd->hc[ch_num].ch_num = ch_num; hhcd->hc[ch_num].state = HC_IDLE; //状态机变化 return USB_HC_StartXfer(hhcd->Instance, &hhcd->hc[ch_num], (uint8_t)hhcd->Init.dma_enable); } ``` 增加的代码如下: ``` rt_err_t drv_set_pipe_pid(uhcd_t hcd,upipe_t pipe,uint8_t direction,uint8_t pid) { HCD_HandleTypeDef *h = hcd->parent.user_data; if (direction == 1) { h->hc[pipe->pipe_index].toggle_in = pid; } else { h->hc[pipe->pipe_index].toggle_out = pid; } return RT_EOK; } ``` 例如获取描述符代码修改 ``` rt_err_t rt_usbh_get_descriptor(uinst_t device, rt_uint8_t type, void* buffer, int nbytes) { struct urequest setup; int timeout = USB_TIMEOUT_BASIC; RT_ASSERT(device != RT_NULL); setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_DEVICE; setup.bRequest = USB_REQ_GET_DESCRIPTOR; setup.wIndex = 0; setup.wLength = nbytes; setup.wValue = type << 8;//低字节为0 默认为0了 if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) == 8)//OUT令牌 { drv_set_pipe_pid(device->hcd,device->pipe_ep0_in,1,1);//增加强制设置DATA1 if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, buffer, nbytes, timeout) == nbytes) { if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_out, RT_NULL, 0, timeout) == 0)//事务阶段 { return RT_EOK; } } } return RT_ERROR; } ``` 6、发现原驱动默认打开的端口是从1开始的,0没有打开。不从0端口通讯有些设备无法枚举。 7、原驱动里,没有对设备的速度进行判断,默认所有接入设备都为全速,需要改进。 8、主机的端口设置都为输入和输出用一个端口是不是可行? 9、但是发现插一些特殊设备如密码狗会造成线程:USBH死机。有时间再研究。 10、插上集线器后能正确加载CLASS驱动,但集线器下行口只有一个口对插入的盘有反应,能枚举,但读写错误, 经过上面6个问题的更改,已经能够正常读写,速度也可以!余下的三个问题有待解决。
7
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
chenkai
CTRL+V
文章
1
回答
4
被采纳
0
关注TA
发私信
相关文章
1
请教USB Host
2
STM32F4调试USB 读卡器(Slave)提示格式化
3
急求 STM32F4 USB Device MSC+SD 的相关问题
4
USB 框架问题
5
USB键盘
6
LPC17xx 如何添加USB HOST设备
7
RT-Thread目前支持USB HOST了吗?
8
USB HOST的支持问题
9
RTT 2.0.1 USB存储设备问题,枚举到USBREQ_GET_MAX_LUN后复位
10
USB库已经很久没更新了
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
PWM
cubemx
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
xusiwei1236
8
个答案
2
次被采纳
踩姑娘的小蘑菇
1
个答案
2
次被采纳
用户名由3_15位
7
个答案
1
次被采纳
bernard
4
个答案
1
次被采纳
RTT_逍遥
3
个答案
1
次被采纳
本月文章贡献
聚散无由
2
篇文章
15
次点赞
catcatbing
2
篇文章
5
次点赞
Wade
2
篇文章
4
次点赞
Ghost_Girls
1
篇文章
6
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部