|
前天测试自己编写的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以上,我现在的速度应该还有很大的提升才是。 * I3 U* h0 J. x' t- q& n/ e 看看程序,发现 void EP3_OUT_Callback(void)//EP3 OUT的回调函数,当EP3接收到数据时候中断调用该函数 / _" H+ x5 E9 l& U { count_out = GetEPRxCount(ENDP3);//获得接收到的数据长度 3 x6 m3 {. _. K7 {; b PMAToUserBufferCopy(buffer_out, ENDP3_RXADDR, count_out);//将数据从USB EP3 RX的缓冲区拷贝到用户指定的数组中 : [/ R- {! A w; A! N7 ] SetEPRxValid(ENDP3); //完成拷贝后置有效状态,从而EP3发送ACK主机可以进行下一个数据包的发送 ) O0 W( j9 t' \2 |+ P9 c } 8 n8 J3 r6 l+ ~* O# u8 ~) E 试着将PMAToUserBufferCopy这句注释掉(这样STM32就不处理接收到的数据了)后再测试速度,惊奇地发现速度竟然达到了997KB/S!晚上仔细想了想,数据肯定是要使用的,这个数据拷贝的过程的时间消费总是少不了的;由于通常情况下USB设备BULK数据接收的步骤就是:接收到数据,置NAK->将缓冲区数据拷贝到用户区(用户处理过程)->发ACK通知主机完成了完整的接收可以发送下一个->主机发送下一个,按照以上的步骤USB接收一步步的进行,只要STM32不完成数据处理,状态就一直是NAK,主机就会不停地发送该数据包,浪费了带宽,因此就会导致我上面最大速度500KB/S难以再增加的情况!不甘心啊~~: I8 t, n2 G; K* o3 B/ r! v 昨天晚上又仔细研究了STM32的技术参考手册的USB章节内容,里面提到BULK可以采用双缓冲机制(PING-PONG)进行处理,正好可以解决上面的情况。双缓冲机制的原理就是分配2块接收缓冲,STM32的用户处理和USB接口可以分别交替占用2个缓冲区,当USB端点接收数据写其中一个缓冲区的时候,用户的应用程序可以同时处理另一个缓冲区,这样缓冲区依次交换占有者,只要用户处理程序在USB端点接收的时间片段内完成处理,就能够完全不影响USB的通讯速度!% `- Q8 R# U: i6 c2 G! i/ C5 @( U 程序部分修改 & h1 f7 v$ R' R8 O 一、EP3_OUT的设置修改, //ZYP:修改EP3为BULK双缓冲方式------------------------- SetEPType(ENDP3, EP_BULK); 2 \/ G- D$ k. e: c SetEPDoubleBuff(ENDP3); SetEPDblBuffAddr(ENDP3, ENDP3_BUF0Addr, ENDP3_BUF1Addr); SetEPDblBuffCount(ENDP3, EP_DBUF_OUT, VIRTUAL_COM_PORT_DATA_SIZE); 8 H! X, O& E* n. c ClearDTOG_RX(ENDP3); ClearDTOG_TX(ENDP3); ! p5 `$ p g$ y# y5 y$ r ToggleDTOG_TX(ENDP3); SetEPRxStatus(ENDP3, EP_RX_VALID); SetEPTxStatus(ENDP3, EP_TX_DIS); 3 _! _: k: b! G0 e' f" A //------------------------------------------------------ ; X. A' `/ w6 ~7 _ 二、EP3_OUT回调函数的修改 void EP3_OUT_Callback(void) % {0 i$ S2 A% g$ L4 t* D& Q { 6 Y' K' D$ H& S8 L( b- T9 A //ZYP:以下是修改成EP3双缓冲OUT后的处理函数 4 ^* E: h0 I/ H' p" V1 G. a. i if (GetENDPOINT(ENDP3) & EP_DTOG_TX)//先判断本次接收到的数据是放在哪块缓冲区的 { FreeUserBuffer(ENDP3, EP_DBUF_OUT); //先释放用户对缓冲区的占有,这样的话USB的下一个接收过程可以立刻进行,同时用户并行进行下面处理 9 ]7 a: \ p/ S% m. ~( M+ X count_out = GetEPDblBuf0Count(ENDP3);//读取接收到的字节数 PMAToUserBufferCopy(buffer_out, ENDP3_BUF0Addr, count_out); } $ _7 N, t. A: S3 @9 K3 S* q3 p else i8 V" b3 Q, N* ~4 L0 I9 K2 Q { 5 \, m& r# |- ^* N$ n$ [" f FreeUserBuffer(ENDP3, EP_DBUF_OUT); ! }. d$ S, }1 f, ~0 U count_out = GetEPDblBuf1Count(ENDP3); , z- z8 t u8 s1 q: z PMAToUserBufferCopy(buffer_out, ENDP3_BUF1Addr, count_out); } } ( G! G; Z+ X3 ^( L- G 3 J, U* t( ~% c7 e! B- r 经过上面的修改,终于解决了STM32在处理接收数据时导致主机等待的情况,用BUS HOUND软件测试了下 + j$ U! N3 [) Y 哈哈,这下终于爽了。 * ^8 N& q; m0 |3 X; M- ?+ u 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微秒,就不会导致缓冲区竞争的情况。 4 V8 E1 H/ ~. j8 ?* {; Y9 y; d 出处:alien2006 |
最全USB HID开发资料,悉心整理一个月,亲自测试
【源码】STLINK-V3MINI 高速USB仿真器,成功改刷【高速CMSIS-DAP】
STM32 USB CDC 虚拟多串口
实战经验 | 选择USBX模块生成USB CDC ACM无PD的项目
STM32 USB HID键盘例程
刘氓兔的杂谈【001】-片上USB 高速PHY
【经验分享】在进行 USB CDC 类开发时,无法发送 64整数倍的数据
在线直播|无需编写任何代码即可在STM32上实现USB-C Power Delivery
圈圈发布USB图书第二版有感,以及分享一些我学习USB过程...
USB Audio设计与实现
微信公众号
手机版
RE:STM32 USB改双缓冲后,STM32的OUT接收速度到了1MB/S!
{% _6 e/ x2 {; }" k
}
RE:STM32 USB改双缓冲后,STM32的OUT接收速度到了1MB/S!
回复:STM32 USB改双缓冲后,STM32的OUT接收速度到了1MB/S!
能否再快一些?