久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 9227|回復(fù): 4
收起左側(cè)

VCP例程用于數(shù)據(jù)傳輸時(shí)丟失數(shù)據(jù)的處理

[復(fù)制鏈接]
ID:98618 發(fā)表于 2015-12-19 14:53 | 顯示全部樓層 |閱讀模式
本文作者:Miler Shao
幾天前有個(gè)工程師在使用STM32F4芯片的VCP功能時(shí),發(fā)現(xiàn)數(shù)據(jù)接收異常,反映每次上位機(jī)發(fā)送過(guò)來(lái)的數(shù)據(jù)只要超過(guò)了64個(gè)字節(jié)就只收到后面的64字節(jié)數(shù)據(jù),小于64字節(jié)正常。因?yàn)橹皇峭ㄟ^(guò)網(wǎng)絡(luò)隨便的一個(gè)留言,其它信息不詳,但我懷疑其原因可能跟下面的案例類似。

下面的案例是之前ST MCU FAE跟客戶一起經(jīng)歷過(guò)的,并將相關(guān)過(guò)程和解決方案形成一份文檔并發(fā)布出來(lái)了。我覺得該案例可能具有一定代表性,并把它轉(zhuǎn)載過(guò)來(lái)分享給大家。該方面的文檔在ST MCU中文官網(wǎng)[www.stmcu.com.cn]的設(shè)計(jì)資源本地化資料部分可以找到。

************************************************************

VCP 例程用于數(shù)據(jù)傳輸時(shí)丟失數(shù)據(jù)的處理


問(wèn)題回顧:

某客戶工程師在其產(chǎn)品的設(shè)計(jì)中,使用了STM32F205VET6。據(jù)其工程師講述:他使用 ST 的 USB 固件庫(kù) 中的 VCP 例程來(lái)實(shí)現(xiàn)虛擬串口的功能,但是他發(fā)現(xiàn)虛擬串口一次輸出的數(shù)據(jù)(從串口到上位機(jī))如果 超過(guò) 2Kbytes 就會(huì)造成數(shù)據(jù)丟失,只輸出尾部的 2Kbytes。客戶工程師檢查代碼發(fā)現(xiàn) USB 的 FIFO 大小 由宏定義 APP_RX_DATA_SIZE決定,而 APP_RX_DATA_SIZE 的大小剛好為 2Kbytes。所以他認(rèn)為此FIFO 設(shè)計(jì)太小而造成的,于是他將 FIFO 的大小改成 5Kbyte,不過(guò)修改后并不能解決問(wèn)題。

調(diào)研:

1.打開“STM32F105/7, STM32F2 and STM32F4 USB on-the-go Host and devicelibrary (UM1021)” 庫(kù)里邊的 VCP 例程,位于“...\STM32_USB-Host- Device_Lib_V2.1.0\Project\USB_Device_Examples\VCP”中。對(duì)其進(jìn)行測(cè)試,并沒有出現(xiàn)所說(shuō)的問(wèn) 題,APP_RX_DATA_SIZE 的大小仍然為 2Kbytes,不管傳輸?shù)臄?shù)據(jù)是2Kbytes 還是 5Kbytes 甚至是 25Kbytes,完全沒有問(wèn)題。


2.了解客戶程序UART 所設(shè)置的波特率,為 115200,與原 VCP 例程一致。USB 采用的是 FullSpeed, 全速 USB 總線的幀周期為 1ms。


3.在 usbd_conf.h 中可以看到 APP_RX_DATA_SIZE 的定義在這里,為2048,它定義了 APP_Rx_Buffer 的大小。APP_Rx_Buffer 其實(shí)是一個(gè)循環(huán)緩沖區(qū),APP_Rx_ptr_in 指明了其數(shù)據(jù)進(jìn)來(lái)的位置,當(dāng)USART 接收到數(shù)據(jù)時(shí),將數(shù)據(jù)存儲(chǔ)于 APP_Rx_ptr_in 指定的位置;APP_Rx_ptr_out指明其數(shù)據(jù)取出 的位置,當(dāng) USB 到 FIFO 中取出數(shù)據(jù)時(shí),起始地址由 APP_Rx_ptr_out 決定。


4.打開 VCP 項(xiàng)目,觀察其程序通信部分。當(dāng) USART 每接收到字節(jié)時(shí),進(jìn)入 EVAL_COM_IRQHandler 函數(shù),

調(diào)用 VCP_Tx(0,0)函數(shù),將收到的字節(jié)存儲(chǔ)于 APP_Rx_Buffer[APP_Rx_ptr_in]中,在APP_Rx_Buffer 中的位置由 APP_Rx_ptr_in 指定。在VCP_Tx 函數(shù)中,

if(APP_Rx_ptr_in == APP_RX_DATA_SIZE)

{

APP_Rx_ptr_in = 0;

}

可以看到當(dāng)APP_Rx_ptr_in 達(dá)到 APP_RX_DATA_SIZE 時(shí),將其置0,也就是在循環(huán)緩沖區(qū)中繞了一圈回到緩沖區(qū)起始地址。


5. 再來(lái)看 APP_Rx_Buffer是如何被 USB 取走,并送到上位機(jī)的。在 usbd_cdc_core.c中,我們?cè)?usbd_cdc_SOF 函數(shù)中看到:

if (FrameCount++ == CDC_IN_FRAME_INTERVAL)

{

FrameCount = 0; Handle_USBAsynchXfer(pdev);

}

可以看到,USB每CDC_IN_FRAME_INTERVAL個(gè)幀調(diào)用一次 Handle_USBAsynchXfer 到 APP_Rx_Buffer 中去取數(shù)據(jù)。CDC_IN_FRAME_INTERVAL 同樣定義在 usbd_conf.h 中,全速的時(shí)候,其值為5。在定義的這邊,我們可以看到:

APP_RX_DATA_SIZE*8/MAX_BAUDARATE*1000should be > CDC_IN_FRAME_INTERVAL

其目的是在于告訴我們APP_RX_DATA_SIZE、MAX_BAUDARATE 和 CDC_IN_FRAME_INTERVAL的關(guān)系,以 保證 APP_Rx_Buffer 是夠用不發(fā)生溢出。


6.接著看 Handle_USBAsynchXfer函數(shù),同樣,我們可以看到:

if (APP_Rx_ptr_out == APP_RX_DATA_SIZE)

{

APP_Rx_ptr_out = 0;

}

也就是當(dāng)APP_Rx_ptr_out 達(dá)到 APP_RX_DATA_SIZE 時(shí),將其置0,也就是在循環(huán)緩沖區(qū)中繞了一圈回到緩沖區(qū)起始地址。

if (APP_Rx_prt_out == APP_Rx_ptr_in)

{

USB_Tx_State = 0; return;

}

當(dāng) APP_Rx_prt_out 趕上 APP_Rx_ptr_in 時(shí),證明 Buffer 里邊的數(shù)據(jù)已經(jīng)發(fā)送完畢,返回。再往下看:

if(APP_Rx_ptr_out > APP_Rx_ptr_in) /* rollback */

{

APP_Rx_length= APP_RX_DATA_SIZE - APP_Rx_ptr_out; //①

}

else

{

APP_Rx_length= APP_Rx_ptr_in - APP_Rx_ptr_out; //②

}

第1種情況為APP_Rx_ptr_out 比 APP_Rx_ptr_in 大,也就是說(shuō)APP_Rx_ptr_in 已經(jīng)繞了一圈,而 APP_Rx_ptr_out 還沒有繞一圈,比如下面情況:


這種情況下:APP_Rx_length 設(shè)置為 APP_Rx_ptr_out 當(dāng)前圈里還剩下數(shù)據(jù)長(zhǎng)度;

第②種情況為 APP_Rx_ptr_in 比APP_Rx_ptr_out 大,也就是 APP_Rx_ptr_in 和APP_Rx_ptr_out 處 于同一圈,于是數(shù)據(jù)情況比如下面情況:

這種情況下:APP_Rx_length 設(shè)置為 APP_Rx_ptr_in 減去 APP_Rx_ptr_out,即當(dāng)前所有數(shù)據(jù)長(zhǎng)度。程序的后面就是對(duì)USB 包的設(shè)置,然后發(fā)送數(shù)據(jù)。

7.結(jié)合 EVAL_COM_IRQHandler和 Handle_USBAsynchXfer 函數(shù)對(duì) APP_Rx_Buffer的處理,我們可以發(fā)現(xiàn), 有一種情況在程序中是沒有做處理的:

當(dāng) APP_Rx_ptr_in 繞了一圈回來(lái),并追上 APP_Rx_ptr_out 時(shí),這個(gè)時(shí)候,若 USART 繼續(xù)接收到數(shù)據(jù),APP_Rx_ptr_in 指針繼續(xù)增長(zhǎng), 就會(huì)造成新來(lái)的數(shù)據(jù)沖掉還沒被 USB 取走的舊數(shù)據(jù)。但是,目前ST官方提供的USB 庫(kù)的 VCP 例程是實(shí)時(shí)的。USART 收進(jìn)來(lái),USB 就會(huì)取走送到上位機(jī)。而 且USB 將數(shù)據(jù)送往上位機(jī) 的速率是大于 USART 接收數(shù)據(jù)的速率的。換言之,它是不會(huì)發(fā)生這種情況的.

之前所分析的:

APP_RX_DATA_SIZE*8/MAX_BAUDARATE*1000should be > CDC_IN_FRAME_INTERVAL 這個(gè)要求,正是保證APP_Rx_Buffer 安全的重要條件。

8.至此,我們懷疑客戶并不是照搬VCP 例程,而是對(duì)其做了修改。拜訪客戶,了解到確實(shí)如此。客戶由于其應(yīng)用需要,將 VCP 程序拆成兩部分,先是用 USART 把所有的數(shù)據(jù)接收進(jìn)來(lái),放到 RAM 中,然 后再將數(shù)據(jù)送到 APP_Rx_Buffer,由 USB 將數(shù)據(jù)送往上位機(jī)。這樣,問(wèn)題就來(lái)了,由于客戶 USART 先從外部接收到并保存于RAM 的數(shù)據(jù)大于 5Kbytes,而 CPU 將 RAM 中的數(shù)據(jù)搬往 APP_Rx_Buffer的 速率遠(yuǎn)大于 USB 將 APP_Rx_Buffer 送往上位機(jī)的速率。這樣就造成了APP_Rx_ptr_in 繞了一圈回來(lái) 追上 APP_Rx_ptr_out并造成溢出的情況,甚至可能是 APP_Rx_ptr_in 繞了幾圈,而 APP_Rx_ptr_out 卻還未開始動(dòng),因?yàn)?USB 每 CDC_IN_FRAME_INTERVAL*1ms 才送一次數(shù)據(jù)。

9.建議客戶修改其程序,在將數(shù)據(jù)從 RAM 中搬往 APP_Rx_Buffer 的時(shí)候,不能采用原 VCP 例程中 USART 一樣的操作方式,也就是存一個(gè)字節(jié)到 APP_Rx_Buffer,APP_Rx_ptr_in 指針加 1,并繞圈。 新的程序需要在此基礎(chǔ)上判斷,當(dāng) APP_Rx_ptr_in 指針加1 后,若等于 APP_Rx_ptr_out,置 “APP_Rx_Buffer 滿”標(biāo)志位,并停止將 RAM 中的數(shù)據(jù)搬往 APP_Rx_Buffer。等 USB 從 APP_Rx_Buffer 中取走數(shù)據(jù),再清“APP_Rx_Buffer 滿”標(biāo)志位,允許將 RAM 中的數(shù)據(jù)繼續(xù)搬往 APP_Rx_Buffer。

10.客戶修改程序,問(wèn)題解決。

結(jié)論:

修改 VCP 例程時(shí),沒有對(duì) APP_Rx_Buffer 的操作有足夠的了解,造成在其特定應(yīng)用中產(chǎn)生了數(shù)據(jù)溢出問(wèn)題。

處理:

在將數(shù)據(jù)存入APP_Rx_Buffer 時(shí),對(duì) APP_Rx_ptr_in 指針在循環(huán)緩沖區(qū)中繞一圈回來(lái)后,是否會(huì)追上APP_Rx_ptr_out 指針進(jìn)行監(jiān)控,以避免數(shù)據(jù)溢出。

建議:

在對(duì)例程進(jìn)行修改用來(lái)供自己的應(yīng)用使用的時(shí)候,我們不僅需要把接口看清楚,更需要把參考例程的原理流程應(yīng)用背景看明白。

=============================================================

后記:上面文檔分析得詳細(xì),看懂了原理就好。整個(gè)過(guò)程可以抽象為一個(gè)生活實(shí)例。有個(gè)池子,一邊給池子里進(jìn)水,一邊取水,做到不溢出就好。如果進(jìn)水快,取水慢,中途不做任何監(jiān)控處理的話,池子再大也有溢出的時(shí)候。結(jié)合上面案例,存放數(shù)據(jù)指針折回并追上取出數(shù)據(jù)指針時(shí)就是溢出臨界點(diǎn),此時(shí)緩沖區(qū)里全是待取走的數(shù)據(jù),如果此時(shí)又來(lái)新數(shù)據(jù)自然出現(xiàn)覆蓋問(wèn)題

回復(fù)

使用道具 舉報(bào)

ID:185829 發(fā)表于 2017-4-3 20:12 | 顯示全部樓層
注意的要點(diǎn)問(wèn)題很重要
回復(fù)

使用道具 舉報(bào)

ID:185829 發(fā)表于 2017-4-3 20:13 | 顯示全部樓層
臨界點(diǎn)的判斷是非0?
回復(fù)

使用道具 舉報(bào)

ID:224453 發(fā)表于 2019-8-23 09:25 | 顯示全部樓層
最終是如何解決的
回復(fù)

使用道具 舉報(bào)

ID:224453 發(fā)表于 2019-8-23 14:02 | 顯示全部樓層
該問(wèn)題已經(jīng)解決 保證out始終不與in相等,假如套圈那就while等待一下,確保out 發(fā)送成功 再去填充buff
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 色资源在线 | 国产精品美女久久久久aⅴ国产馆 | 一级黄色毛片a | 毛片在线视频 | 福利av在线 | 欧美大片久久久 | 亚洲精品久久久一区二区三区 | 动漫www.被爆羞羞av44 | 人人人干| 亚洲性综合网 | 国产丝袜一区二区三区免费视频 | 一级毛片在线播放 | 欧美久久一区 | 九九精品热| 日本欧美在线视频 | 欧美综合国产精品久久丁香 | 免费性视频 | 日本三级电影免费 | 精品国产一区二区三区久久 | 久久精品国产一区二区三区不卡 | 自拍偷拍第一页 | 欧美二区在线 | 在线亚洲免费 | av网站免费在线观看 | 国产精品久久久久久久免费大片 | 亚洲国产欧美91 | 成人午夜在线 | 日本中文字幕一区 | 国产91成人 | 亚洲福利视频一区二区 | 久久久国产一区二区三区 | 欧美a级成人淫片免费看 | 午夜电影一区二区 | 婷婷国产一区二区三区 | 成人在线精品视频 | 玖玖玖在线 | 国产精品久久福利 | 午夜激情一区 | 免费成人高清在线视频 | 中文字幕1区2区 | 国产精品久久久久久久久久 |