前言
rtt 里的 usbhost 驅(qū)動(dòng)有問(wèn)題這是眾所周知的事情了,很多人在論壇上提問(wèn),也出現(xiàn)了各種解決方案。這里做個(gè)匯總,同時(shí)把我最終的解決方法說(shuō)一下。
平臺(tái)環(huán)境:STM32F429 正點(diǎn)原子阿波羅
usbhost 驅(qū)動(dòng)相關(guān)疑問(wèn)
第一個(gè)疑問(wèn)
https://club.rt-thread.org/ask/question/430499.html
關(guān)于這里的延時(shí),好像 stm32 官方的某個(gè)手冊(cè)或者 usb 規(guī)范里有講,因?yàn)檫@里 https://bbs.21ic.com/icview-106567-1-1.html 也提到了這個(gè) 1ms 延時(shí)是必需的。
我曾經(jīng)去掉過(guò)這個(gè)延時(shí),去掉是有嚴(yán)重問(wèn)題的。識(shí)別不出 U盤(pán)還是小事兒,還可能?chē)?yán)重的搞壞系統(tǒng),這一點(diǎn)兒下面細(xì)講。
第二個(gè)疑問(wèn)
https://club.rt-thread.org/ask/question/425072.html
出現(xiàn)死循環(huán)的原因只有一個(gè),usb 控制器出現(xiàn) nak 并且自己不可恢復(fù)。原驅(qū)動(dòng)中相關(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)重新提交請(qǐng)求。
其它疑問(wèn)
不識(shí)別,枚舉設(shè)備失敗,無(wú)法掛載。。。
其它各種問(wèn)題都和 drv_usbh.c 的 `drv_pipe_xfer` 函數(shù)有千絲萬(wàn)縷的聯(lián)系。
調(diào)試記錄
刪 `drv_pipe_xfer` 中的延時(shí)
這個(gè)延時(shí)應(yīng)該是硬件的硬性要求,去掉它會(huì)很容易 nak,然后進(jìn)入上面的死循環(huán)里面。我去掉了延時(shí),所以在 nak 死循環(huán)了。接下來(lái)去掉 nak 的死循環(huán)。
修改一個(gè) bug
原代碼是這么寫(xiě)的 `else if (HAL_HCD_HC_GetState(&stm32_hhcd_fs, pipe->pipe_index) == URB_ERROR)` ,`HAL_HCD_HC_GetState` 返回值是 `HCD_HCStateTypeDef` 類(lèi)型,而 `URB_ERROR` 是 `HCD_URBStateTypeDef` 類(lèi)型的,明顯調(diào)用的函數(shù)和比較值類(lèi)型不一樣。
修改后 `else if (HAL_HCD_HC_GetURBState(&stm32_hhcd_fs, pipe->pipe_index) == URB_ERROR)`。
重寫(xiě)任何可能引起**死循環(huán)**的代碼
死循環(huán)可不是什么好東西,如果有這個(gè)可能,就必須改掉它,比如通過(guò)計(jì)數(shù),重試有限次之后退出嘗試。
nak retry
把 `continue` 改成重試 10 ,感覺(jué)把錯(cuò)誤轉(zhuǎn)移了,也沒(méi)見(jiàn)有多少改善。
nak 退出
去掉 `continue` ,去掉 retry,直接返回錯(cuò)誤退出 `drv_pipe_xfer` 函數(shù)。那么,問(wèn)題來(lái)了。
首先說(shuō)明,`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ù)過(guò)程沒(méi)有多大問(wèn)題,當(dāng)它扮演接收數(shù)據(jù)緩存時(shí)就存在潛在的隱患。
因?yàn)榻邮諗?shù)據(jù)的內(nèi)存緩存多半不是全局的,而是臨時(shí)申請(qǐng)的內(nèi)存,或者是從棧上分配的。假如 `drv_pipe_xfer` 函數(shù)“假”失敗返回,而 stm32 的 usb 控制器還在工作。比如上面的 nak,當(dāng)我直接錯(cuò)誤返回退出 `drv_pipe_xfer` 函數(shù)后,開(kāi)始發(fā)現(xiàn)各種內(nèi)存異常修改。比如返回上層調(diào)用函數(shù)的過(guò)程中發(fā)現(xiàn)局部變量(棧)莫名變成其它隨機(jī)值。從這里我猜測(cè)雖然是 nak,但是并不一定表示有什么嚴(yán)重的問(wè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)比較的錯(cuò)誤,添加上 `drv_pipe_xfer` 中的 1ms 延時(shí),同時(shí)添加了 nak 狀態(tài)等待。去掉任何死循環(huán)操作,如果 nak 等待超時(shí)后還是 nak 就錯(cuò)誤退出。到此,U 盤(pán)失敗和 sd 卡識(shí)別已經(jīng)有明顯的改觀了,大多時(shí)候可以走到 df_mount 之前(修改前走到 rt_udisk_run 就很難,rt_udisk_run 和 df_mount 中間有幾個(gè)地方都可能出錯(cuò)終止)。
下面繼續(xù) debug。
上層操作 retry
`drv_pipe_xfer` 里 retry 的操作嘗試過(guò)了,經(jīng)過(guò)測(cè)試才知道,底層的工作原理不允許這么暴力 retry,這樣可能引起 usb 控制器工作異常。
換個(gè)思路 retry,把 retry 往上層移動(dòng),找最容易出錯(cuò)誤的函數(shù)調(diào)用路徑中的某個(gè)接口,比如 `rt_usbh_storage_read10` 或 `rt_usbh_storage_write10` ,讀操作最多返回錯(cuò)誤,應(yīng)用層操作失敗。寫(xiě)操作失敗意味著可能寫(xiě) U 盤(pán)的任意節(jié)點(diǎn)出現(xiàn)問(wèn)題了,這個(gè)時(shí)候放任不管可能會(huì)丟失數(shù)據(jù)的。
于是 `rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout);` 返回值不是 OK 就重試。這種嘗試好像有效果,但是還是有多次重試后失敗的。
繼續(xù)查找原因
最底層的和比較上層的部分都使用各種方法嘗試過(guò)了,問(wèn)題還是存在。而且,讓人頭疼的是出現(xiàn)問(wèn)題后 U 盤(pán)的文件系統(tǒng)有被損壞的概率。經(jīng)過(guò)多次格式化 U 盤(pán)后發(fā)現(xiàn)也只有 `rt_usbh_storage_write10` -> `rt_usb_bulk_only_xfer` 出錯(cuò)概率最大,寫(xiě)壞 U 盤(pán)的操作也出現(xiàn)在這里。
那么,進(jìn)入 `rt_usb_bulk_only_xfer` 函數(shù)尋找機(jī)會(huì)。
rt_usb_bulk_only_xfer
這個(gè)函數(shù)大概率出錯(cuò),肯定有它自己的獨(dú)特的操作。 `rt_usbh_storage_read10` 也調(diào)用了這個(gè)函數(shù)但是不出錯(cuò),由此,我把可疑范圍縮小到
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í)候,也就是說(shuō)讀寫(xiě)數(shù)據(jù)量大的時(shí)候,`rt_usb_hcd_pipe_xfer` 函數(shù)執(zhí)行就容易出錯(cuò)。
為什么呢?數(shù)據(jù)量多少直接影響了出錯(cuò)概率。其它地方調(diào)用 `drv_pipe_xfer` 能正常工作,這里出錯(cuò),能說(shuō)明硬件配置有問(wèn)題嗎?
`rt_usb_hcd_pipe_xfer` 里面把大數(shù)據(jù)分包,分成 64 字節(jié)的小包一次次發(fā)送,這個(gè)包能改大一點(diǎn)兒?jiǎn)??發(fā)大包會(huì)有影響嗎?
多處判斷包大小和 wMaxPacketSize 的關(guān)系, wMaxPacketSize 能修改大一點(diǎn)嗎?
加延時(shí),還是 retry ?哪種解決方法更高效?
最終方案
調(diào)用 `rt_usb_hcd_pipe_xfer` 之前加個(gè)短延時(shí),比如 3ms 的延時(shí)。因?yàn)槠渌椒ǘ紘L試過(guò),暫時(shí)沒(méi)找到能解決問(wèn)題的。
如果對(duì)延時(shí)引起的性能降低比較在意,先判斷發(fā)送的包大小有多大,如果一包處理不完,延時(shí)一下;如果一包就可以處理完不需要延時(shí)。
讀和寫(xiě)延時(shí)也不一樣,寫(xiě)操作要求延時(shí)時(shí)間長(zhǎng),讀操作可能不延時(shí)也沒(méi)問(wèn)題。
當(dāng)這里加延時(shí)后,手頭的 U 盤(pán)和讀卡器識(shí)別和讀寫(xiě)文件都正常了。
因?yàn)檫@里的延時(shí)是經(jīng)驗(yàn)值,有的需要延時(shí)時(shí)間短,有的需要延時(shí)時(shí)間長(zhǎng)。不確定延時(shí)多少怎么辦?可以如下動(dòng)態(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è)算不算問(wèn)題,無(wú)論從理論上還是實(shí)際應(yīng)用中,讀寫(xiě) U 盤(pán)的時(shí)候是禁止拔掉 U 盤(pán)的。
不說(shuō)寫(xiě) U 盤(pán),假設(shè)讀 U 盤(pán)的時(shí)候拔掉 U 盤(pán),原驅(qū)動(dòng)有一定的機(jī)率在讀操作過(guò)程中清理掉了對(duì)應(yīng)通道的設(shè)備。因?yàn)?,監(jiān)測(cè) usb 端口在一個(gè)獨(dú)立線(xiàn)程,然后讀寫(xiě)接口被文件系統(tǒng)調(diào)用,肯定是另外的應(yīng)用層線(xiàn)程操作的了。兩個(gè)不同的線(xiàn)程,一個(gè)使用 usb 通道時(shí),另一個(gè)清理掉了它!
所以,在 `rt_usbh_hub_port_change` `rt_udisk_read` `rt_udisk_write` 等接口出添加互斥操作避免上述情況出現(xiàn)。這個(gè)對(duì)讀寫(xiě)都是有效的。
> 本文所有提到的更改已經(jīng)提交到 gitee ,歡迎大家測(cè)試 https://gitee.com/thewon/rt_thread_repo
審核編輯:湯梓紅
-
調(diào)試
+關(guān)注
關(guān)注
7文章
589瀏覽量
34083 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1306瀏覽量
40434 -
STM32F429
+關(guān)注
關(guān)注
0文章
40瀏覽量
10808 -
USBHost
+關(guān)注
關(guān)注
0文章
2瀏覽量
1703
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
有獎(jiǎng)?wù)骷疭TM32點(diǎn)滴調(diào)試記錄
STM32單片機(jī)和ESP8266模塊調(diào)試過(guò)程分享
【原創(chuàng)精選】RT-Thread征文精選技術(shù)文章合集
記錄總結(jié)一下基于RK3128平臺(tái)的LCD驅(qū)動(dòng)調(diào)試步驟
MTK驅(qū)動(dòng)調(diào)試相關(guān)總結(jié)
基于智能USBHost控制器IC在數(shù)據(jù)記錄中的應(yīng)用
KE02芯片調(diào)試記錄
![KE02芯片<b class='flag-5'>調(diào)試</b><b class='flag-5'>記錄</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
QSIP驅(qū)動(dòng)W25Q256調(diào)試記錄
![QSIP<b class='flag-5'>驅(qū)動(dòng)</b>W25Q256<b class='flag-5'>調(diào)試</b><b class='flag-5'>記錄</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
關(guān)于從機(jī)SPI通信調(diào)試記錄
![關(guān)于從機(jī)SPI通信<b class='flag-5'>調(diào)試</b><b class='flag-5'>記錄</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
STM32單片機(jī)和ESP8266模塊調(diào)試過(guò)程記錄
![STM32單片機(jī)和ESP8266模塊<b class='flag-5'>調(diào)試</b>過(guò)程<b class='flag-5'>記錄</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
關(guān)于調(diào)試DS18B20溫度傳感器-延時(shí)相關(guān)問(wèn)題等-記錄
![關(guān)于<b class='flag-5'>調(diào)試</b>DS18B20溫度傳感器-延時(shí)<b class='flag-5'>相關(guān)</b>問(wèn)題等-<b class='flag-5'>記錄</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
DSP2837x PWM調(diào)試(BLDC無(wú)刷電機(jī)驅(qū)動(dòng))
![DSP2837x PWM<b class='flag-5'>調(diào)試</b>(BLDC無(wú)刷電機(jī)<b class='flag-5'>驅(qū)動(dòng)</b>)](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
接口測(cè)試?yán)碚摗?b class='flag-5'>疑問(wèn)收錄與擴(kuò)展相關(guān)知識(shí)點(diǎn)
![接口測(cè)試?yán)碚摗?b class='flag-5'>疑問(wèn)</b>收錄與擴(kuò)展<b class='flag-5'>相關(guān)</b>知識(shí)點(diǎn)](https://file1.elecfans.com/web1/M00/F5/18/wKgZoWc2qDuACbyVAAAX95Ea1vY915.png)
評(píng)論