0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

usbhost驱动相关疑问与调试记录

出出 来源:出出 作者:出出 2022-06-20 15:24 次阅读

前言

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 并且自己不可恢复。原驱动中相关代码如下:

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 的可能性很大,直接交给下面处理。

       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` 也调用了这个函数但是不出错,由此,我把可疑范围缩小到

   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 盘和读卡器识别和读写文件都正常了。
因为这里的延时是经验值,有的需要延时时间短,有的需要延时时间长。不确定延时多少怎么办?可以如下动态调整延时时间。

   ...
   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

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 调试
    +关注

    关注

    7

    文章

    527

    浏览量

    33625
  • RT-Thread
    +关注

    关注

    31

    文章

    1148

    浏览量

    38868
  • STM32F429
    +关注

    关注

    0

    文章

    40

    浏览量

    10322
  • USBHost
    +关注

    关注

    0

    文章

    2

    浏览量

    1663
收藏 人收藏

    评论

    相关推荐

    有奖征集STM32点滴调试记录

    /jishu_463708_1_1.html 调试记录:利用上位机控制STM点亮LED灯https://bbs.elecfans.com/jishu_463257_1_1.html调试记录
    发表于 11-05 20:26

    STM32单片机和ESP8266模块调试过程分享

    ,这篇文章也就是记录调试过程中遇到的问题和小心得的分享。希望大家积极指正,积极交流,如果有疑问或者需要指正的地方可以添加我的微信,sunkaiwz,备注CSDN,还有一些常用的WiFi模块相关
    发表于 02-21 06:08

    【原创精选】RT-Thread征文精选技术文章合集

    系列rt-thread心法系列(一)那些你必须知道的几类apirt-thread 心法系列(二) 使用宝典usbhost驱动相关疑问调试
    发表于 07-26 14:56

    记录总结一下基于RK3128平台的LCD驱动调试步骤

    1、rk3128 lcd驱动调试记录  最近刚调试了基于rk3128平台的lcd驱动,顺便记录
    发表于 09-23 16:28

    LPC2368_USBHost源代码

    LPC2368_USBHost源代码,又需要的下来看看
    发表于 08-15 17:55 27次下载

    MTK驱动调试相关总结

    MTK驱动调试相关总结
    发表于 03-19 11:47 5次下载

    KEIL5和PROTEUS8调试记录的详细资料说明

    本文档的主要内容详细介绍的是KEIL5和PROTEUS8调试记录的详细资料说明。1、GPIOA口驱动一个led用proteus仿真产生错误:Access to register
    发表于 04-01 08:00 13次下载
    KEIL5和PROTEUS8<b class='flag-5'>调试</b><b class='flag-5'>记录</b>的详细资料说明

    基于智能USBHost控制器IC在数据记录中的应用

    在嵌入式系统中应用USB设备需要性能相对较强的硬件,要带有USBHost控制器接口、RTOS以及USB软件驱动,结果因USBHost功能实施成本的原因设计工程师一直都不太愿意在小型8位或16位MCU系统
    的头像 发表于 05-03 11:17 2173次阅读

    单片机串口调试丢包验证过程记录 已解决

    单片机串口调试丢包验证过程记录 已解决
    发表于 11-16 15:36 80次下载
    单片机串口<b class='flag-5'>调试</b>丢包验证过程<b class='flag-5'>记录</b> 已解决

    KE02芯片调试记录

    KE02芯片调试记录前言一、关于配置问题1.1 UART串口配置1.2二、其他1.2.总结前言该文章主要记录KE02系列芯片在使用过程中遇到的问题,为了防止时间长了,会形成遗忘,特此记录
    发表于 12-03 12:51 6次下载
    KE02芯片<b class='flag-5'>调试</b><b class='flag-5'>记录</b>

    QSIP驱动W25Q256调试记录

    QSIP驱动W25Q256调试记录发现异常初始化配置初始化要点指令时序分析与驱动函数W25Q256JV 数据手册纠错点:正点原子中,完全没有使用Table 4的 Dual/Quad S
    发表于 12-04 17:06 14次下载
    QSIP<b class='flag-5'>驱动</b>W25Q256<b class='flag-5'>调试</b><b class='flag-5'>记录</b>

    关于从机SPI通信调试记录

    此笔记用来记录调试过程遇到的一下问题,错无大小,记录下来,提醒自己。Q:SPI从机通信,采用中断发送,在调试时发现主机收到的报文经常比我发送的报文多上一到两个字节,导致传输失败A:经过
    发表于 12-22 19:19 3次下载
    关于从机SPI通信<b class='flag-5'>调试</b><b class='flag-5'>记录</b>

    STM32单片机和ESP8266模块调试过程记录

    程序和应用程序,这篇文章也就是记录调试过程中遇到的问题和小心得的分享。希望大家积极指正,积极交流,如果有疑问或者需要指正的地方可以添加我的微信,sunkaiwz,备注CSDN,还有一些常用的WiFi模块
    发表于 12-24 19:20 2次下载
    STM32单片机和ESP8266模块<b class='flag-5'>调试</b>过程<b class='flag-5'>记录</b>

    AN034 基于USBHost的IAP例程

    AN034 基于USBHost的IAP例程
    发表于 02-27 18:30 0次下载
    AN034 基于<b class='flag-5'>USBHost</b>的IAP例程

    DSP2837x PWM调试(BLDC无刷电机驱动)

    )的驱动。 提示:以下是本篇文章正文内容,下面调试记录供参考 一、初始化 1.main()初始化 主程序中初始化代码如下: //初始化PWMEALLOW ;CpuSysRegs . PCLKCR0 .
    发表于 03-20 14:25 9次下载
    DSP2837x PWM<b class='flag-5'>调试</b>(BLDC无刷电机<b class='flag-5'>驱动</b>)