Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
USB主机_host
rt-thread STM32F4 usbhost 调试笔记
发布于 2021-07-12 18:28:37 浏览:6139
订阅该版
[tocm] # 前言 rtt 里的 usbhost 驱动有问题这是众所周知的事情了,很多人在论坛上提问,也出现了各种解决方案。这里做个汇总,同时把我最终的解决方法说一下。 平台环境:STM32F429 正点原子阿波罗 ## usbhost 驱动相关疑问 ### 第一个疑问 https://club.rt-thread.org/ask/question/430499.html 关于这里的延时,好像 stm32 官方的某个手册或者 usb 规范里有讲,因为这里 https://bbs.21ic.com/icview-106567-1-1.html 也提到了这个 1ms 延时是必需的。 我曾经去掉过这个延时,去掉是有严重问题的。识别不出 U盘还是小事儿,还可能严重的搞坏系统,这一点儿下面细讲。 ### 第二个疑问 https://club.rt-thread.org/ask/question/425072.html 出现死循环的原因只有一个,usb 控制器出现 nak 并且自己不可恢复。原驱动中相关代码如下: ```c if (HAL_HCD_HC_GetState(&stm32_hhcd_fs, pipe->pipe_index) == HC_NAK) { RT_DEBUG_LOG(RT_DEBUG_USB, ("nak\n")); if (pipe->ep.bmAttributes == USB_EP_ATTR_INT) { rt_thread_delay((pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) > 0 ? (pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) : 1); } HAL_HCD_HC_Halt(&stm32_hhcd_fs, pipe->pipe_index); HAL_HCD_HC_Init(&stm32_hhcd_fs, pipe->pipe_index, pipe->ep.bEndpointAddress, pipe->inst->address, USB_OTG_SPEED_FULL, pipe->ep.bmAttributes, pipe->ep.wMaxPacketSize); continue; } ``` 即便这里有初始化操作,但是实际上并不能恢复,也不能 continue 实现重新提交请求。 ### 其它疑问 不识别,枚举设备失败,无法挂载。。。 其它各种问题都和 drv_usbh.c 的 `drv_pipe_xfer` 函数有千丝万缕的联系。 ## 调试记录 ### 删 `drv_pipe_xfer` 中的延时 这个延时应该是硬件的硬性要求,去掉它会很容易 nak,然后进入上面的死循环里面。我去掉了延时,所以在 nak 死循环了。接下来去掉 nak 的死循环。 ### 修改一个 bug 原代码是这么写的 `else if (HAL_HCD_HC_GetState(&stm32_hhcd_fs, pipe->pipe_index) == URB_ERROR)` ,`HAL_HCD_HC_GetState` 返回值是 `HCD_HCStateTypeDef` 类型,而 `URB_ERROR` 是 `HCD_URBStateTypeDef` 类型的,明显调用的函数和比较值类型不一样。 修改后 `else if (HAL_HCD_HC_GetURBState(&stm32_hhcd_fs, pipe->pipe_index) == URB_ERROR)`。 ### 重写任何可能引起**死循环**的代码 死循环可不是什么好东西,如果有这个可能,就必须改掉它,比如通过计数,重试有限次之后退出尝试。 #### nak retry 把 `continue` 改成重试 10 ,感觉把错误转移了,也没见有多少改善。 #### nak 退出 去掉 `continue` ,去掉 retry,直接返回错误退出 `drv_pipe_xfer` 函数。那么,问题来了。 首先说明,`drv_pipe_xfer` 函数被 `rt_usb_hcd_setup_xfer` 和 `rt_usb_hcd_pipe_xfer` 两个函数直接调用,然后被其它几十个函数间接调用。 `drv_pipe_xfer` 函数的第三个参数 `void *buffer`,用于传递输入输出数据缓存地址指针。输出数据过程没有多大问题,当它扮演接收数据缓存时就存在潜在的隐患。 因为接收数据的内存缓存多半不是全局的,而是临时申请的内存,或者是从栈上分配的。假如 `drv_pipe_xfer` 函数**“假”**失败返回,而 stm32 的 usb 控制器还在工作。比如上面的 nak,当我直接错误返回退出 `drv_pipe_xfer` 函数后,开始发现各种内存异常修改。比如返回上层调用函数的过程中发现局部变量(栈)莫名变成其它随机值。从这里我猜测虽然是 nak,但是并不一定表示有什么严重的问题。既然 usb 控制器仍然使用传递给他的寄存器地址,如果再稍微等待一下是不是变成完成状态了? ### nak 超时 处理 HC 状态和 URB 状态前先判断是否是 nak,如果是 nak 就等待,等待 timeout 个 tick 超时,然后交给下面处理;不是 nak, ok 的可能性很大,直接交给下面处理。 ```c tick = rt_tick_get(); while(HAL_HCD_HC_GetState(&stm32_hhcd_fs, pipe->pipe_index) == HC_NAK) { if ((rt_tick_get() - tick) >= timeout) { break; } else { rt_thread_yield(); } } ``` 这么处理以后,nak 少多了,但是还是有,而且一经出现就很难再继续。 ### 阶段小结 修改了状态比较的错误,添加上 `drv_pipe_xfer` 中的 1ms 延时,同时添加了 nak 状态等待。去掉任何死循环操作,如果 nak 等待超时后还是 nak 就错误退出。到此,U 盘失败和 sd 卡识别已经有明显的改观了,大多时候可以走到 df_mount 之前(修改前走到 rt_udisk_run 就很难,rt_udisk_run 和 df_mount 中间有几个地方都可能出错终止)。 下面继续 debug。 ### 上层操作 retry `drv_pipe_xfer` 里 retry 的操作尝试过了,经过测试才知道,底层的工作原理不允许这么暴力 retry,这样可能引起 usb 控制器工作异常。 换个思路 retry,把 retry 往上层移动,找最容易出错误的函数调用路径中的某个接口,比如 `rt_usbh_storage_read10` 或 `rt_usbh_storage_write10` ,读操作最多返回错误,应用层操作失败。写操作失败意味着可能写 U 盘的任意节点出现问题了,这个时候放任不管可能会丢失数据的。 于是 `rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout);` 返回值不是 OK 就重试。这种尝试好像有效果,但是还是有多次重试后失败的。 ### 继续查找原因 最底层的和比较上层的部分都使用各种方法尝试过了,问题还是存在。而且,让人头疼的是出现问题后 U 盘的文件系统有被损坏的概率。经过多次格式化 U 盘后发现也只有 `rt_usbh_storage_write10` -> `rt_usb_bulk_only_xfer` 出错概率最大,写坏 U 盘的操作也出现在这里。 那么,进入 `rt_usb_bulk_only_xfer` 函数寻找机会。 ### rt_usb_bulk_only_xfer 这个函数大概率出错,肯定有它自己的独特的操作。 `rt_usbh_storage_read10` 也调用了这个函数但是不出错,由此,我把可疑范围缩小到 ```c if(cmd->xfer_len != 0) { ... size = rt_usb_hcd_pipe_xfer(pipe->inst->hcd, pipe, (void*)buffer, cmd->xfer_len, timeout); ... } ``` `cmd->xfer_len` 比较大的时候,也就是说读写数据量大的时候,`rt_usb_hcd_pipe_xfer` 函数执行就容易出错。 为什么呢?数据量多少直接影响了出错概率。其它地方调用 `drv_pipe_xfer` 能正常工作,这里出错,能说明硬件配置有问题吗? `rt_usb_hcd_pipe_xfer` 里面把大数据分包,分成 64 字节的小包一次次发送,这个包能改大一点儿吗?发大包会有影响吗? 多处判断包大小和 wMaxPacketSize 的关系, wMaxPacketSize 能修改大一点吗? 加延时,还是 retry ?哪种解决方法更高效? ### 最终方案 调用 `rt_usb_hcd_pipe_xfer` 之前加个短延时,比如 3ms 的延时。因为其它方法都尝试过,暂时没找到能解决问题的。 如果对延时引起的性能降低比较在意,先判断发送的包大小有多大,如果一包处理不完,延时一下;如果一包就可以处理完不需要延时。 读和写延时也不一样,写操作要求延时时间长,读操作可能不延时也没问题。 当这里加延时后,手头的 U 盘和读卡器识别和读写文件都正常了。 因为这里的延时是经验值,有的需要延时时间短,有的需要延时时间长。不确定延时多少怎么办?可以如下动态调整延时时间。 ```c ... if(cmd->xfer_len > pipe->ep.wMaxPacketSize) { rt_thread_mdelay(wr_delay); } size = rt_usb_hcd_pipe_xfer(pipe->inst->hcd, pipe, (void*)buffer, cmd->xfer_len, timeout); if(size != cmd->xfer_len) { if (wr_delay < 5) { wr_delay += 2; } ... } ``` ## 其它 不知道这个算不算问题,无论从理论上还是实际应用中,读写 U 盘的时候是禁止拔掉 U 盘的。 不说写 U 盘,假设读 U 盘的时候拔掉 U 盘,原驱动有一定的机率在读操作过程中清理掉了对应通道的设备。因为,监测 usb 端口在一个独立线程,然后读写接口被文件系统调用,肯定是另外的应用层线程操作的了。两个不同的线程,一个使用 usb 通道时,另一个清理掉了它! 所以,在 `rt_usbh_hub_port_change` `rt_udisk_read` `rt_udisk_write` 等接口出添加互斥操作避免上述情况出现。这个对读写都是有效的。 > 本文所有提到的更改已经提交到 gitee ,欢迎大家测试 https://gitee.com/thewon/rt_thread_repo
30
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
出出啊
恃人不如自恃,人之为己者不如己之自为也
文章
43
回答
1517
被采纳
342
关注TA
发私信
相关文章
1
RT-Thread Studio USBHOST问题。
2
STM32F407遇到USB读取U盘无法读取问题
3
解决usbhost 无法使用hid设备的问题,
4
usbhost无法设别NTFS格式的U盘吗?
5
stm32f767配置usb host
6
有偿开发基于RT-thread+M3 实现 USB host RNDIS
7
No class register on usb device
8
USB Host 鼠标键盘枚举无法获取HID设备描述符
9
RT-Thread Studio USBHOST
10
USB host设备调试问题
推荐文章
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
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
MicroPython
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
15
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
13
个答案
1
次被采纳
本月文章贡献
程序员阿伟
9
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
RTT_逍遥
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部