1. 程式人生 > >STM32 USB 程式將BULK EP改成雙緩衝機制後,接收OUT資料的速度從原先的500KB/S,

STM32 USB 程式將BULK EP改成雙緩衝機制後,接收OUT資料的速度從原先的500KB/S,

前天測試自己編寫的USB驅動程式時候發現從主機到STM32的OUT傳輸(主機到裝置)速率竟然只有最高33KB/S,實在是暈死了。經過研究後發現是驅動程式中設定的PIPE MaxTransferSize引數的關係,原先設定64只能33KB/S,後參考其他USB裝置驅動程式的值,設定成了65535,再測試USB OUT的速度,達到了500KB/S,終於解決了驅動程式的瓶頸。不過算下USB 2.0全速的通訊速率是12Mb/S,排除掉CRC、令牌、SOF等等開銷怎麼也應該不止最大500KB/S啊。到網上看了看,基本上應該能達到600KB/S~700KB/S以上,我現在的速度應該還有很大的提升才是。
  看看程式,發現
void EP3_OUT_Callback(void)//EP3 OUT的回撥函式,當EP3接收到資料時候中斷呼叫該函式
{
count_out = GetEPRxCount(ENDP3);//獲得接收到的資料長度
PMAToUserBufferCopy(buffer_out, ENDP3_RXADDR, count_out);//將資料從USB EP3 RX的緩衝區拷貝到使用者指定的陣列中
SetEPRxValid(ENDP3); //完成拷貝後置有效狀態,從而EP3傳送ACK主機可以進行下一個資料包的傳送
}
  試著將PMAToUserBufferCopy這句註釋掉(這樣STM32就不處理接收到的資料了)後再測試速度,驚奇地發現速度竟然達到了997KB/S!晚上仔細想了想,資料肯定是要使用的,這個資料拷貝的過程的時間消費總是少不了的;由於通常情況下USB裝置BULK資料接收的步驟就是:接收到資料,置NAK->將緩衝區資料拷貝到使用者區(使用者處理過程)->發ACK通知主機完成了完整的接收可以傳送下一個->主機發送下一個,按照以上的步驟USB接收一步步的進行,只要STM32不完成資料處理,狀態就一直是NAK,主機就會不停地傳送該資料包,浪費了頻寬,因此就會導致我上面最大速度500KB/S難以再增加的情況!不甘心啊~~
  昨天晚上又仔細研究了STM32的技術參考手冊的USB章節內容,裡面提到BULK可以採用雙緩衝機制(PING-PONG)進行處理,正好可以解決上面的情況。雙緩衝機制的原理就是分配2塊接收緩衝,STM32的使用者處理和USB介面可以分別交替佔用2個緩衝區,當USB端點接收資料寫其中一個緩衝區的時候,使用者的應用程式可以同時處理另一個緩衝區,這樣緩衝區依次交換佔有者,只要使用者處理程式在USB端點接收的時間片段內完成處理,就能夠完全不影響USB的通訊速度!
  程式部分修改
一、EP3_OUT的設定修改,
//ZYP:修改EP3為BULK雙緩衝方式-------------------------
SetEPType(ENDP3, EP_BULK);
SetEPDoubleBuff(ENDP3);
SetEPDblBuffAddr(ENDP3, ENDP3_BUF0Addr, ENDP3_BUF1Addr);
SetEPDblBuffCount(ENDP3, EP_DBUF_OUT, VIRTUAL_COM_PORT_DATA_SIZE);
ClearDTOG_RX(ENDP3);
ClearDTOG_TX(ENDP3);
ToggleDTOG_TX(ENDP3);
SetEPRxStatus(ENDP3, EP_RX_VALID);
SetEPTxStatus(ENDP3, EP_TX_DIS);
//------------------------------------------------------
二、EP3_OUT回撥函式的修改
void EP3_OUT_Callback(void)
{
//ZYP:以下是修改成EP3雙緩衝OUT後的處理函式
if (GetENDPOINT(ENDP3) & EP_DTOG_TX)//先判斷本次接收到的資料是放在哪塊緩衝區的
{
  FreeUserBuffer(ENDP3, EP_DBUF_OUT); //先釋放使用者對緩衝區的佔有,這樣的話USB的下一個接收過程可以立刻進行,同時使用者並行進行下面處理
  count_out = GetEPDblBuf0Count(ENDP3);//讀取接收到的位元組數
  PMAToUserBufferCopy(buffer_out, ENDP3_BUF0Addr, count_out);
}
else
{
  FreeUserBuffer(ENDP3, EP_DBUF_OUT);
  count_out = GetEPDblBuf1Count(ENDP3);
  PMAToUserBufferCopy(buffer_out, ENDP3_BUF1Addr, count_out);
}
}
  經過上面的修改,終於解決了STM32在處理接收資料時導致主機等待的情況,用BUS HOUND軟體測試了下
  哈哈,這下終於爽了。



PS:上面的FreeUserBuffer(ENDP3, EP_DBUF_OUT); 這句話的上下位置是關鍵,如果放到函式的後面,則仍舊會有主機等待STM32處理資料的情況,速度仍然是500KB/S!
  把這句話放在拷貝函式的前面的話就真正把雙緩衝PING-PONG機制用起來了。大致算了下PMAToUserBufferCopy(buffer_out, ENDP3_BUF1Addr, count_out);這句話當count_out為最大值64的時候STM32執行需要302個週期,72MHZ情況下約4.2微秒執行時間,而USB傳輸按照12Mb/s的線速度傳輸64位元組的資料至少也得40微秒,因此只要PMAToUserBufferCopy的時間不超過40微秒,就不會導致緩衝區競爭的情況。