0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

usbhost驅(qū)動相關(guān)疑問與調(diào)試記錄

出出 ? 來源:出出 ? 作者:出出 ? 2022-06-20 15:24 ? 次閱讀

前言

rtt 里的 usbhost 驅(qū)動有問題這是眾所周知的事情了,很多人在論壇上提問,也出現(xiàn)了各種解決方案。這里做個(gè)匯總,同時(shí)把我最終的解決方法說一下。

平臺環(huán)境:STM32F429 正點(diǎn)原子阿波羅

usbhost 驅(qū)動相關(guān)疑問

第一個(gè)疑問

https://club.rt-thread.org/ask/question/430499.html

關(guān)于這里的延時(shí),好像 stm32 官方的某個(gè)手冊或者 usb 規(guī)范里有講,因?yàn)檫@里 https://bbs.21ic.com/icview-106567-1-1.html 也提到了這個(gè) 1ms 延時(shí)是必需的。

我曾經(jīng)去掉過這個(gè)延時(shí),去掉是有嚴(yán)重問題的。識別不出 U盤還是小事兒,還可能嚴(yán)重的搞壞系統(tǒng),這一點(diǎn)兒下面細(xì)講。

第二個(gè)疑問

https://club.rt-thread.org/ask/question/425072.html

出現(xiàn)死循環(huán)的原因只有一個(gè),usb 控制器出現(xiàn) nak 并且自己不可恢復(fù)。原驅(qū)動中相關(guān)代碼如下:

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;
}

即便這里有初始化操作,但是實(shí)際上并不能恢復(fù),也不能 continue 實(shí)現(xiàn)重新提交請求。

其它疑問

不識別,枚舉設(shè)備失敗,無法掛載。。。
其它各種問題都和 drv_usbh.c 的 `drv_pipe_xfer` 函數(shù)有千絲萬縷的聯(lián)系。

調(diào)試記錄

刪 `drv_pipe_xfer` 中的延時(shí)

這個(gè)延時(shí)應(yīng)該是硬件的硬性要求,去掉它會很容易 nak,然后進(jìn)入上面的死循環(huán)里面。我去掉了延時(shí),所以在 nak 死循環(huán)了。接下來去掉 nak 的死循環(huán)。

修改一個(gè) 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` 類型的,明顯調(diào)用的函數(shù)和比較值類型不一樣。
修改后 `else if (HAL_HCD_HC_GetURBState(&stm32_hhcd_fs, pipe->pipe_index) == URB_ERROR)`。

重寫任何可能引起**死循環(huán)**的代碼

死循環(huán)可不是什么好東西,如果有這個(gè)可能,就必須改掉它,比如通過計(jì)數(shù),重試有限次之后退出嘗試。

nak retry

把 `continue` 改成重試 10 ,感覺把錯誤轉(zhuǎn)移了,也沒見有多少改善。

nak 退出

去掉 `continue` ,去掉 retry,直接返回錯誤退出 `drv_pipe_xfer` 函數(shù)。那么,問題來了。

首先說明,`drv_pipe_xfer` 函數(shù)被 `rt_usb_hcd_setup_xfer` 和 `rt_usb_hcd_pipe_xfer` 兩個(gè)函數(shù)直接調(diào)用,然后被其它幾十個(gè)函數(shù)間接調(diào)用。
`drv_pipe_xfer` 函數(shù)的第三個(gè)參數(shù) `void *buffer`,用于傳遞輸入輸出數(shù)據(jù)緩存地址指針。輸出數(shù)據(jù)過程沒有多大問題,當(dāng)它扮演接收數(shù)據(jù)緩存時(shí)就存在潛在的隱患。

因?yàn)榻邮諗?shù)據(jù)的內(nèi)存緩存多半不是全局的,而是臨時(shí)申請的內(nèi)存,或者是從棧上分配的。假如 `drv_pipe_xfer` 函數(shù)“假”失敗返回,而 stm32 的 usb 控制器還在工作。比如上面的 nak,當(dāng)我直接錯誤返回退出 `drv_pipe_xfer` 函數(shù)后,開始發(fā)現(xiàn)各種內(nèi)存異常修改。比如返回上層調(diào)用函數(shù)的過程中發(fā)現(xiàn)局部變量(棧)莫名變成其它隨機(jī)值。從這里我猜測雖然是 nak,但是并不一定表示有什么嚴(yán)重的問題。既然 usb 控制器仍然使用傳遞給他的寄存器地址,如果再稍微等待一下是不是變成完成狀態(tài)了?

nak 超時(shí)

處理 HC 狀態(tài)和 URB 狀態(tài)前先判斷是否是 nak,如果是 nak 就等待,等待 timeout 個(gè) tick 超時(shí),然后交給下面處理;不是 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 少多了,但是還是有,而且一經(jīng)出現(xiàn)就很難再繼續(xù)。

階段小結(jié)

修改了狀態(tài)比較的錯誤,添加上 `drv_pipe_xfer` 中的 1ms 延時(shí),同時(shí)添加了 nak 狀態(tài)等待。去掉任何死循環(huán)操作,如果 nak 等待超時(shí)后還是 nak 就錯誤退出。到此,U 盤失敗和 sd 卡識別已經(jīng)有明顯的改觀了,大多時(shí)候可以走到 df_mount 之前(修改前走到 rt_udisk_run 就很難,rt_udisk_run 和 df_mount 中間有幾個(gè)地方都可能出錯終止)。

下面繼續(xù) debug。

上層操作 retry

`drv_pipe_xfer` 里 retry 的操作嘗試過了,經(jīng)過測試才知道,底層的工作原理不允許這么暴力 retry,這樣可能引起 usb 控制器工作異常。
換個(gè)思路 retry,把 retry 往上層移動,找最容易出錯誤的函數(shù)調(diào)用路徑中的某個(gè)接口,比如 `rt_usbh_storage_read10` 或 `rt_usbh_storage_write10` ,讀操作最多返回錯誤,應(yīng)用層操作失敗。寫操作失敗意味著可能寫 U 盤的任意節(jié)點(diǎn)出現(xiàn)問題了,這個(gè)時(shí)候放任不管可能會丟失數(shù)據(jù)的。
于是 `rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout);` 返回值不是 OK 就重試。這種嘗試好像有效果,但是還是有多次重試后失敗的。

繼續(xù)查找原因

最底層的和比較上層的部分都使用各種方法嘗試過了,問題還是存在。而且,讓人頭疼的是出現(xiàn)問題后 U 盤的文件系統(tǒng)有被損壞的概率。經(jīng)過多次格式化 U 盤后發(fā)現(xiàn)也只有 `rt_usbh_storage_write10` -> `rt_usb_bulk_only_xfer` 出錯概率最大,寫壞 U 盤的操作也出現(xiàn)在這里。
那么,進(jìn)入 `rt_usb_bulk_only_xfer` 函數(shù)尋找機(jī)會。

rt_usb_bulk_only_xfer

這個(gè)函數(shù)大概率出錯,肯定有它自己的獨(dú)特的操作。 `rt_usbh_storage_read10` 也調(diào)用了這個(gè)函數(shù)但是不出錯,由此,我把可疑范圍縮小到

   if(cmd->xfer_len != 0)
   {
       ...
       size = rt_usb_hcd_pipe_xfer(pipe->inst->hcd, pipe, (void*)buffer,
           cmd->xfer_len, timeout);
       ...
   }

`cmd->xfer_len` 比較大的時(shí)候,也就是說讀寫數(shù)據(jù)量大的時(shí)候,`rt_usb_hcd_pipe_xfer` 函數(shù)執(zhí)行就容易出錯。

為什么呢?數(shù)據(jù)量多少直接影響了出錯概率。其它地方調(diào)用 `drv_pipe_xfer` 能正常工作,這里出錯,能說明硬件配置有問題嗎?

`rt_usb_hcd_pipe_xfer` 里面把大數(shù)據(jù)分包,分成 64 字節(jié)的小包一次次發(fā)送,這個(gè)包能改大一點(diǎn)兒嗎?發(fā)大包會有影響嗎?

多處判斷包大小和 wMaxPacketSize 的關(guān)系, wMaxPacketSize 能修改大一點(diǎn)嗎?

加延時(shí),還是 retry ?哪種解決方法更高效?

最終方案

調(diào)用 `rt_usb_hcd_pipe_xfer` 之前加個(gè)短延時(shí),比如 3ms 的延時(shí)。因?yàn)槠渌椒ǘ紘L試過,暫時(shí)沒找到能解決問題的。
如果對延時(shí)引起的性能降低比較在意,先判斷發(fā)送的包大小有多大,如果一包處理不完,延時(shí)一下;如果一包就可以處理完不需要延時(shí)。
讀和寫延時(shí)也不一樣,寫操作要求延時(shí)時(shí)間長,讀操作可能不延時(shí)也沒問題。
當(dāng)這里加延時(shí)后,手頭的 U 盤和讀卡器識別和讀寫文件都正常了。
因?yàn)檫@里的延時(shí)是經(jīng)驗(yàn)值,有的需要延時(shí)時(shí)間短,有的需要延時(shí)時(shí)間長。不確定延時(shí)多少怎么辦?可以如下動態(tài)調(diào)整延時(shí)時(shí)間。

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

其它

不知道這個(gè)算不算問題,無論從理論上還是實(shí)際應(yīng)用中,讀寫 U 盤的時(shí)候是禁止拔掉 U 盤的。
不說寫 U 盤,假設(shè)讀 U 盤的時(shí)候拔掉 U 盤,原驅(qū)動有一定的機(jī)率在讀操作過程中清理掉了對應(yīng)通道的設(shè)備。因?yàn)椋O(jiān)測 usb 端口在一個(gè)獨(dú)立線程,然后讀寫接口被文件系統(tǒng)調(diào)用,肯定是另外的應(yīng)用層線程操作的了。兩個(gè)不同的線程,一個(gè)使用 usb 通道時(shí),另一個(gè)清理掉了它!
所以,在 `rt_usbh_hub_port_change` `rt_udisk_read` `rt_udisk_write` 等接口出添加互斥操作避免上述情況出現(xiàn)。這個(gè)對讀寫都是有效的。

> 本文所有提到的更改已經(jīng)提交到 gitee ,歡迎大家測試 https://gitee.com/thewon/rt_thread_repo

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 調(diào)試
    +關(guān)注

    關(guān)注

    7

    文章

    589

    瀏覽量

    34040
  • RT-Thread
    +關(guān)注

    關(guān)注

    31

    文章

    1305

    瀏覽量

    40330
  • STM32F429
    +關(guān)注

    關(guān)注

    0

    文章

    40

    瀏覽量

    10764
  • USBHost
    +關(guān)注

    關(guān)注

    0

    文章

    2

    瀏覽量

    1695
收藏 人收藏

    評論

    相關(guān)推薦

    有獎?wù)骷疭TM32點(diǎn)滴調(diào)試記錄

    /jishu_463708_1_1.html 調(diào)試記錄:利用上位機(jī)控制STM點(diǎn)亮LED燈https://bbs.elecfans.com/jishu_463257_1_1.html調(diào)試記錄
    發(fā)表于 11-05 20:26

    STM32單片機(jī)和ESP8266模塊調(diào)試過程分享

    ,這篇文章也就是記錄調(diào)試過程中遇到的問題和小心得的分享。希望大家積極指正,積極交流,如果有疑問或者需要指正的地方可以添加我的微信,sunkaiwz,備注CSDN,還有一些常用的WiFi模塊相關(guān)
    發(fā)表于 02-21 06:08

    【原創(chuàng)精選】RT-Thread征文精選技術(shù)文章合集

    系列rt-thread心法系列(一)那些你必須知道的幾類apirt-thread 心法系列(二) 使用寶典usbhost驅(qū)動相關(guān)疑問調(diào)試
    發(fā)表于 07-26 14:56

    記錄總結(jié)一下基于RK3128平臺的LCD驅(qū)動調(diào)試步驟

    1、rk3128 lcd驅(qū)動調(diào)試記錄  最近剛調(diào)試了基于rk3128平臺的lcd驅(qū)動,順便記錄
    發(fā)表于 09-23 16:28

    LPC2368_USBHost源代碼

    LPC2368_USBHost源代碼,又需要的下來看看
    發(fā)表于 08-15 17:55 ?27次下載

    MTK驅(qū)動調(diào)試相關(guān)總結(jié)

    MTK驅(qū)動調(diào)試相關(guān)總結(jié)
    發(fā)表于 03-19 11:47 ?5次下載

    基于智能USBHost控制器IC在數(shù)據(jù)記錄中的應(yīng)用

    嵌入式系統(tǒng)中應(yīng)用USB設(shè)備需要性能相對較強(qiáng)的硬件,要帶有USBHost控制器接口、RTOS以及USB軟件驅(qū)動,結(jié)果因USBHost功能實(shí)施成本的原因設(shè)計(jì)工程師一直都不太愿意在小型8位或16位MCU系統(tǒng)
    的頭像 發(fā)表于 05-03 11:17 ?2492次閱讀

    KE02芯片調(diào)試記錄

    KE02芯片調(diào)試記錄前言一、關(guān)于配置問題1.1 UART串口配置1.2二、其他1.2.總結(jié)前言該文章主要記錄KE02系列芯片在使用過程中遇到的問題,為了防止時(shí)間長了,會形成遺忘,特此記錄
    發(fā)表于 12-03 12:51 ?6次下載
    KE02芯片<b class='flag-5'>調(diào)試</b><b class='flag-5'>記錄</b>

    QSIP驅(qū)動W25Q256調(diào)試記錄

    QSIP驅(qū)動W25Q256調(diào)試記錄發(fā)現(xiàn)異常初始化配置初始化要點(diǎn)指令時(shí)序分析與驅(qū)動函數(shù)W25Q256JV 數(shù)據(jù)手冊糾錯點(diǎn):正點(diǎn)原子中,完全沒有使用Table 4的 Dual/Quad S
    發(fā)表于 12-04 17:06 ?25次下載
    QSIP<b class='flag-5'>驅(qū)動</b>W25Q256<b class='flag-5'>調(diào)試</b><b class='flag-5'>記錄</b>

    關(guān)于從機(jī)SPI通信調(diào)試記錄

    此筆記用來記錄調(diào)試過程遇到的一下問題,錯無大小,記錄下來,提醒自己。Q:SPI從機(jī)通信,采用中斷發(fā)送,在調(diào)試時(shí)發(fā)現(xiàn)主機(jī)收到的報(bào)文經(jīng)常比我發(fā)送的報(bào)文多上一到兩個(gè)字節(jié),導(dǎo)致傳輸失敗A:經(jīng)過
    發(fā)表于 12-22 19:19 ?3次下載
    關(guān)于從機(jī)SPI通信<b class='flag-5'>調(diào)試</b><b class='flag-5'>記錄</b>

    STM32單片機(jī)和ESP8266模塊調(diào)試過程記錄

    程序和應(yīng)用程序,這篇文章也就是記錄調(diào)試過程中遇到的問題和小心得的分享。希望大家積極指正,積極交流,如果有疑問或者需要指正的地方可以添加我的微信,sunkaiwz,備注CSDN,還有一些常用的WiFi模塊
    發(fā)表于 12-24 19:20 ?2次下載
    STM32單片機(jī)和ESP8266模塊<b class='flag-5'>調(diào)試</b>過程<b class='flag-5'>記錄</b>

    關(guān)于調(diào)試DS18B20溫度傳感器-延時(shí)相關(guān)問題等-記錄

    關(guān)于調(diào)試DS18B20溫度傳感器-記錄敘述元器件要點(diǎn)步驟其它問題結(jié)尾代碼鏈接敘述最近,調(diào)試DS18B20這個(gè)數(shù)字傳感器,關(guān)于如何調(diào)試DS18B20的網(wǎng)上資料非常多,但是通過親身嘗試,還
    發(fā)表于 01-18 10:01 ?4次下載
    關(guān)于<b class='flag-5'>調(diào)試</b>DS18B20溫度傳感器-延時(shí)<b class='flag-5'>相關(guān)</b>問題等-<b class='flag-5'>記錄</b>

    AN034 基于USBHost的IAP例程

    AN034 基于USBHost的IAP例程
    發(fā)表于 02-27 18:30 ?0次下載
    AN034 基于<b class='flag-5'>USBHost</b>的IAP例程

    DSP2837x PWM調(diào)試(BLDC無刷電機(jī)驅(qū)動)

    )的驅(qū)動。 提示:以下是本篇文章正文內(nèi)容,下面調(diào)試記錄供參考 一、初始化 1.main()初始化 主程序中初始化代碼如下: //初始化PWMEALLOW ;CpuSysRegs . PCLKCR0 .
    發(fā)表于 03-20 14:25 ?11次下載
    DSP2837x PWM<b class='flag-5'>調(diào)試</b>(BLDC無刷電機(jī)<b class='flag-5'>驅(qū)動</b>)

    接口測試?yán)碚摗?b class='flag-5'>疑問收錄與擴(kuò)展相關(guān)知識點(diǎn)

    本文章使用王者榮耀游戲接口、企業(yè)微信接口的展示結(jié)合理論知識,講解什么是接口測試、接口測試?yán)碚摗?b class='flag-5'>疑問收錄與擴(kuò)展相關(guān)知識點(diǎn)的知識學(xué)院,快來一起看看吧~
    的頭像 發(fā)表于 11-15 09:12 ?374次閱讀
    接口測試?yán)碚摗?b class='flag-5'>疑問</b>收錄與擴(kuò)展<b class='flag-5'>相關(guān)</b>知識點(diǎn)