你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【实战经验】进行USB CDC类开发时,无法发送64整数倍数据(续)

[复制链接]
zero99 发布时间:2017-5-5 09:39
在进行 USB CDC类开发时,
无法发送64整数倍的数据(续)
0 b* `5 y8 @% `) s7 B  V, p
1 前言3 n( Z1 d& y' @+ {- ]
       此文延续之前相同文章的话题,是对上篇文章的补充,之所以会有此文,主要是之前发现问题是在STM32F4上,解决方案也是基于CubeF4,但是,当相同问题出现在STM32F0上时,使用之前的代码修改并不能适用,这也就是本文的目的所在。9 n/ ]$ a) M6 A: Q
       注:需要读懂此文的内容,请先了解上篇文章的内容。

+ o' n9 ~( T7 {9 C2 分析+ u; T. n" L, L2 A; d3 _! O
2.1 问题的本质原因0 v, a# `) C1 `* |) ^
       在进行USB CDC类开发时,无法从设备端向主机端发送64整数倍数据,最本质的原因就是,当发送数据长度恰好是Data In端点的最大包长整数倍时,最后一包数据必须是零长度的数据包(ZLP)。这是由于在USB标准中,接收端并不是通过已经接收的数据长度来判断是否接收完成,且发送端也并没有给出将要发送多长的数据,因此,接收端在接收数据前,并不知道将要接收的数据是多少,那么,问题就来了,接收端又是如何判断当前的数据已经全部接收了呢?有两点:
; d8 J; P! P# e# S
●若接收到的数据包长不足最大包长时,则认为当前传输完成。
5 ~9 \$ Q& I# F; K% i4 U ●如接收到的数据包长为零时,则认为当前传输完成。

5 y( r& o5 C$ Q/ k9 M
正式由于上述两种判断,当传输的数据刚好是端点的最大包长时,当发送完最后一包(比如64个字节)时,接收端无法判断是否传输结束,进而继续等待下一包数据。这个就是问题本质所在。
# i, F! e* a' S$ h
( F/ l+ M) A( ]& Y! C& f+ f
2.2 之前的解决方案% d2 V) K6 H! r( P* p
知道问题原因后,解决的方法也就变得简单了,总的原则就是,在发送完最后一包数据后,判断发送的包长是否为端点最大包长的整数倍,如是,则补发一个零长度的数据包(ZLP)。7 {) x" X0 p- E9 b* r. o' S
* ~, z7 t. Z+ @+ B" n6 d
现在来看看上篇文章的修改代码:/ _$ O4 d/ X' K% g/ c0 B7 a
在usbd_cdc.c文件中:
11-12.png
% F- L5 _. T2 a. O" s* k9 B. [; p
如上代码,程序使用if(ep->xfer_len >0 &&ep->xfer_len%ep->maxpacket ==0)来判断当前发送包长是否为端点的最大包长整数倍。这个放在CubeF4中是没有问题的,但是,如放在CubeF0中是有问题的:
● CubeF0中是没有类型USB_OTG_EPTypeDef的端点,对应的,只有PCD_EPTypeDef类型的端点。
) k" u: z, `2 U● 在CubeF4中ep->xfer_le表示当前的传输长度,而在CubeF0中ep->xfer_le表示的是剩余需要发送的数据长度。& o" V0 d0 Y' B# U
因此,此方法并不能很好的兼容CubeF4和F1,究其原因,本质上还是,STM32F4采用的USB IP核为USB_OTG_FS,USB_OTG_HS两种IP核,而STM32F0上采用的是USB IP核(STM32F1也是)。因此,本来IP核就不一样,不兼容完全就是正常现象了。

/ S' Y. p$ O7 |$ T! q4 l+ h      还有一点,上述代码修改使用了与底层相关的端点类型,这就局限了其适用范围,在切换成另一个USB IP核后就不一定再适用,且USB协议栈是属于中间件件层,原则上与底层要完全抽象分离出来,保持其硬件无关性。这也是我们对中间件代码进行修改的方向,只有这样,才能保证中间件层软件的模块化和通用性。3 g/ B2 f3 N8 P. ~% R
7 C7 M2 q& k4 K; F( b9 ?
3 新的解决方法* j" [* M* M5 f, e# X; Z
下面我们就来做一个通用性的解决方法,虽然不一定是最佳方法,但碰到此类问题时不失为一种值得参考的方法。新的方法避免了使用底层数据,完全保持了原先从底层分离的原则。如下:
13.jpg
14.jpg

; f& {7 V% W5 P如上代码,代码使用了pdev->ep_in[epnum]的数据成员rem_length(剩余数据长度)和total_length(数据总长度)来判断是否需要再发送ZLP。其实不使用rem_length也是可以的。
9 T- p/ V6 ]# t" c8 p
接下来,我们就需要保证rem_length和total_length的准确性即可。在这里,我们模仿端点0中对应值的设置,原则上只修改到usbd_conf.c和usbd_cdc.c文件,并未修改usb核的三个源码文件(usbd_core.c,usbd_ioreq.c,usbd_ctlreq.c)。从而保证其影响范围控制在USB CDC类的范围内。

* u: c1 n. L% o2 A( ]9 [' |Total_length是在usb reset的回调函数中设置:, y0 h6 ^' c) m" n5 }
//usbd_conf.c usbd_conf.c是用户文件
15.jpg
. s+ [6 F: v0 Y$ `, q% Z) m3 n6 o
如上,在USB复位中断回调函数中实现了对非0端点的其他端点的最大包长赋值,并存储在dev->ep_in[xx]中,注意这里是dev的成员ep_in[xx]中,并不是PCD中的端点中。

0 T7 F! ?* t$ E2 F* Z; v6 F3 I- C7 {同样在usbd_conf.c文件中:
16.png

8 W& e1 J! {5 w$ ?% R9 |: g  @
如上代码,在USBD_LL_Transmit函数中,实现了对pev->ep_in[xx]端点的发送长度赋值。0 O1 \( \4 S; A
+ Y1 B$ t* A, [5 j& z
最后就差rem_length赋值了:2 A# c* v/ Z- d
在usbd_cdc.c文件中:
17.jpg

9 {* _: E% O, P0 w& K- R) ^如上,在发送时,dev->ep_in[xx]端点的rem_length剩余长度会初始为发送总长度,在发送完成中断中,rem_length会即使更新,见之前的USBD_CDC_DataIn函数,这样就基本修改完成了。
. g7 n! H9 \; u6 C
8 t2 l. a& N9 [% W5 U7 b
4 测试验证4 C# ?- Z3 l2 ^. Z1 R5 t3 P
修改的代码是与底层分离的,因此原则上使用与STM32全系列带USB的MCU,但这里我们只验证了STM32F0与STM32F4,本文给出的示例代码分别对应了STM32F072B-Discovery和STM32F4-Discovery(STM32F407)板,且在device与host端双向无限发送数据的情况还均能稳定,因此测试结果是通过的。! F! U- C4 b: `, }: [, m

2 h; k8 J' Z  f3 V/ U& F
5 注意事项9 x2 T' @6 i9 n
● 发送完成中断是指将所有要发送的数据都发送完后产生的中断,USB 外设并不会自动根据包长情况决定是否发送ZLP;且这个中断一般是用作通知(Notification)的,可以在此回调中执行少数工作,比如状态更新等等,但原则上不要做其他大量繁重工作,避免影响通讯的稳定性能。
% e0 m5 v: X! a9 d+ r  ]3 @5 s● 而接收中断却不相同,不管有没有达到最大包长,只要接收到一包数据就会产生一次中断,进而回调到USBD_CDC_DataOut回调函数。这个是与Data_In不一样的地方。
5 U2 V3 L* S/ F+ ?- k% T. |6 S! C● 若Host端向Device端发送64个字节,按标准USB,host端也应该发送ZLP,但实际上Host可能并没有发送ZLP,在这种情况下,STM32依然可以正常接收,这是由于不管有没有等到ZLP,MCU端依然会产生接收完成中断,最终回调到DataOut函数中。+ y8 r, G' F; Z4 u+ E# {, c0 Q# o3 @
● 无限从device向Host端发送数据时,需要在Host端打开串口并接收,不然在发送若干条Data_In数据后,Host端会NACK拒绝.这个由于Host端的接收缓存有限,在缓存满了后无法再接收,因此只能NACK拒绝。在打开出口后,缓存中的数据被移走,腾出新的接收空间后才能继续接收数据。

5 R/ ~) A  _4 v: o) T. C
# K! H, M+ r( F: X% ]; S
在进行USB CDC类开发时,无法发送64整数倍的数据(ç»­).pdf (371.01 KB, 下载次数: 117)
赞 1 收藏 3 评论6 发布时间:2017-5-5 09:39

举报

6个回答
原来是他 回答时间:2017-6-28 08:40:12
谢谢楼主分享~
valetang-126879 回答时间:2017-6-28 09:19:55
谢谢楼主分享
wofei1314 回答时间:2017-8-27 10:31:42
好贴~
" w) [: k. g7 m2 A7 s  J" P! b1 Q& k' o% z% C% `3 `# H! E- U1 K
学习来了,谢谢分享~
shuichengwen 回答时间:2018-7-27 10:20:31
多谢分享
tankyhai 回答时间:2020-11-24 10:29:29
多谢分享
dhb1109 回答时间:2021-6-10 09:10:01
多些分享,我还以为低于我测试低于1024就行,还以为高于1024不行呢。
3 q$ [! D7 E- e( o2 k! z3 K0 y6 {2 k+ z/ w

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版