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

【经验分享】OTG主机库在不同传输类型上对NAK的处理

[复制链接]
STMCU小助手 发布时间:2022-2-11 18:28
问题:某客户使用 STM32F4 的 OTG 库做 USB 主机控制 Wifi 网卡。使用 BULK 传输类型时,从数据读取数据时,如果设备返回需要把设备返回的 NAK 状态告知上层应用,该如何修改 OTG 库。. u+ _3 m: x! ?7 J8 i
调研:
/ {! p: G/ ^& z0 Z先来看看 OTG 库当前对 BULK 类型传输,IN 和 OUT 方向上的 NAK 的处理方式:5 M1 j+ T7 ?- ]
  1. @ BULK IN 通道
    . r  U7 V" t8 @4 h# ^
  2. If (nak); ?. s" w% Q" F" t2 g% k& j/ o; j; I  B
  3. 重新使能该通道: CHENA=1, CHDIS=04 u( ~5 e, }8 h) B: a
  4. 记录通道状态: Pdev->hostHC_Status[num] = HC_NAK. k. L4 s5 C$ a8 V7 `6 |  r5 h
  5. 清零 NAK 位
    6 ]' B* t. D, m# q( U6 E
  6. 退出该中断处理程序(ISR)9 Y3 d0 J3 z) T; x
复制代码

9 F0 |( R% ~$ V' }6 M7 L1 Q( h
一旦重新使能该通道,主机硬件又自动发送 IN 令牌来企图从设备获取数据,直到设备准备好数据后,不再回复 NAK 握手,而是回复主机要获取的数据,然后主机硬件回复 ACK 来结束本次 transfer。
, p$ O1 `$ T: n# W# D8 aBULK IN 通道对 NAK 的处理和 CTRL IN 通道对 NAK 的处理,在 ISR 中是一样的;但是在驱动库里,对 CTRL IN 有超时限制,而对 BULK IN 没有。就是说对于常用来做枚举传输的 CTRL 传输,当启动从设备获取信息,但是久久未得的情况下,会走到 Timeout 的处理分支。从代码里我们可以看到
+ U) W1 l8 H( h; e
  1. <usbh_core.c>9 L' X$ O" |/ J. [8 C1 G% q% k
  2. USBH_HandleControl()
    4 y# I6 C; H: _, G8 L
  3. {! c2 G2 H0 R. s5 r* L
  4. case CTRL_DATA_IN : 发出 IN 令牌
    , q) |9 l" w2 p% Q. Y8 w
  5. case CTRL_DATA_IN_WAIT :! o- `! H0 i2 d
  6. get_urb_state9 L$ Y. M% l7 u9 A
  7. if (done)2 S+ K' z( o" l0 K' ^- Q; D5 D8 U# w
  8. if (stall)
    9 |9 [0 I+ n4 P2 L
  9. if (error)9 C- ~1 T9 b" @. N3 t- w9 o( z
  10. if (超时) USBH Fail
复制代码
  T7 x7 C& i! p. \
( l$ F: m" C! i$ L
但是 BULK IN 通道对 NAK 的接收没有超时控制,因为 BULK 传输本身的性质就是不保证带宽的,即如果主机上有很多其他优先级更高的周期类型的传输(同步 ISO 传输和中断 INT 传输),则在 BULK传输有可能无限延迟。
% n( y+ D0 u& j7 [/ YCTRL IN 对 NAK 有超时处理,那么 CTRL OUT 对 NAK 是如何处理的呢?
: S1 ~3 R$ d$ e4 O$ H从代码里可以看到 CTRL OUT 收到 NAK 后会把该状态上传 APP,如图1 x$ P+ c5 ~( ?% ^
  1. @ CTRL OUT 通道
    % g5 W- @+ k/ f
  2. If (nak)+ ^& a+ X, j0 O. u. `
  3. 打开通道 halt 中断: CHMM @ OTG_FS_HCINTMSK
    - v) U# ~" h6 Z1 q" r7 `. o
  4. Halt 该通道: CHENA=0, CHDIS=1
    + E. o7 A# K0 g: Z
  5. 记录通道状态:Pdev->hostHC_Status[num] = HC_NAK( }5 m. w4 D. @
  6. 错误计数清零
    : e- w2 x+ p7 x: z
  7. 清零 NAK 标志:@HCINTx% U. |1 v) w1 ]/ ]' g# a
  8. 退出该中断处理程序(ISR),又进入通道 halt 中断+ f! J; b/ s1 ~* }& u$ u. s6 N
  9. If (halt)
    ) ]7 i  C: a8 |2 Y+ }0 l
  10. 关闭通道 halt 中断  E9 f! C* b! }1 [; g; C6 F
  11. 记录通道状态以上传 APP:Pdev->host.URB_State[num] = URB_NOTREADY
复制代码
然后在库代码处理控制传输时,如果检测到这个状态,就会重新发送 OUT 令牌和数据包,如图
( z7 O. i" x4 l- j; B
  1. <usbh_core.c>4 _  O8 z% f1 C3 ~+ B. O/ q( F
  2. USBH_HandleControl()) U; m( j5 U! X2 [9 z& x" q! I
  3. {5 A8 W, K! G( z; A; d
  4. case CTRL_DATA_OUT : 发出 OUT 令牌和数据包
    8 K* ^; j9 F3 J
  5. case CTRL_DATA_OUT_WAIT :9 g% m( ?: F% G) i
  6. get_urb_state
    ! V6 u$ ~& `$ ?% I
  7. if (done)2 f. p! K) M1 v2 o5 o! n
  8. if (stall)
    & ]; v& o& P5 D' g. l
  9. if (error)
    : W6 p9 m9 f$ d# e  m4 [
  10. if (URB_NOTREADY) 回到之前的 case CTRL_DATA_OUT
复制代码

  o! }/ |8 B% }2 U+ e因此,当 CTRL IN 收到 NAK 后,如果想把状态上传给 App,则可以模仿 CTRL OUT 对 NAK 的处理。首先, ISR 中的处理可以模仿 CTRL OUT,在 BULK 传输的处理中,对每次发送 IN 令牌的地方(USBH_BulkReceiveData)查询传输状态,如果 URB_NOTREADY 就由 App 来决定如何处理。
/ l: o' w& Z8 L  Y
- @7 u4 y0 H$ a- }' z" o处理: " D7 \- Y: E- O; `4 U7 H
基于 U 盘读写的例程,在每次 USBH_BulkReceiveData 之后检查状态,如果是 URB_NOTREADY 就重新发送 IN 令牌。于是全项目搜索 USBH_BulkReceiveData,有三个地方,且都在USBH_HandleBOTXfer()中调用,即在 BOT 传输中若干次读数据阶段(多次数据包整数长度读取和最后一次的尾巴数据读取)和 CSW 阶段的读取,如图:
* r- f) i/ Q2 H
  1. <usbh_core.c>, d0 ~0 G& a! g$ C6 ?& ]. r. e6 _) d
  2. USBH_MSC_HandleBOTXfer()
      X. D( `! N9 j8 P& I6 y- X  |9 [
  3. {
    $ V% z1 a2 {% D/ C
  4. case USBH_MSC_BOT_DATAIN_STATE:: l+ H& f$ K; |: {
  5. if (URB_DONE)
    ) y  A9 a' D! R* u  R% p
  6. {
    * k$ N- }( i$ D* b0 S7 x+ p" h
  7. if (剩余数据大于端点的 MPZ) USBH_BulkReceiveData(MPZ)1 L5 H0 p$ {  d1 b0 V' z7 Y
  8. if (剩余数据小于端点的 MPZ) USBH_BulkReceiveData(剩余数据量)7 Z6 X8 g2 }! d2 {# ~3 {
  9. }: o& ^2 v! N% g% C1 f
  10. case USBH_MSC_RECEIVE_CSW_STATE :% @( Q: d) }9 H: S  X
  11. USBH_BulkReceiveData(13)
复制代码
需要对每次 BULK IN 传输后检测状态,如果收到 NAK 则重新发起刚才的那次 BULK IN 传输,如图:/ k1 s3 i+ C: f( I) C
  1. <usbh_core.c>
    ! l& _# i5 T( d
  2. USBH_MSC_HandleBOTXfer()
    * P' I4 T* X" p: ~/ X
  3. {  k5 S: O, q$ P9 X% E7 g
  4. case USBH_MSC_BOT_DATAIN_STATE:
    ' A. L: K- }0 `: L
  5. if (URB_DONE)
    4 F. \+ {" C3 C6 i& e
  6. {
    . u1 }2 d2 b, k  \. u
  7. if (剩余数据大于端点的 MPZ) USBH_BulkReceiveData(MPZ)
    * p- [$ j0 M8 Q, Q
  8. if (剩余数据小于端点的 MPZ) USBH_BulkReceiveData(剩余数据量)! f. r0 d, h0 w. b
  9. }7 \1 i7 ]4 l, C; |. S
  10. If (URB_NOTREADY)
    6 H6 u4 }3 ^" x
  11. {重新发起刚才的 BULK IN 传输}
    / s# m/ g8 t" u3 @
  12. case USBH_MSC_RECEIVE_CSW_STATE :
    : \  C/ Q1 m0 d# w; b  ?# G
  13. USBH_BulkReceiveData(13)
    $ i: \5 H$ R1 T& O# l
  14. If (URB_NOTREADY)
    % O% a! V! A; s
  15. {重新发起刚才的 BULK IN 传输}
复制代码
经过以上修改,以 FS 和 HS 都能对 U 盘正确读取。" G0 g5 `. ~( B/ C1 O- p
修改后的固件代码,请参看附件压缩包。  S8 L% l; ]8 j
* k; Y0 p  h, w! R( `: L3 {

) ~/ W+ D, c) ~" t6 \# i
赞 收藏 评论0 发布时间:2022-2-11 18:28

举报

0个回答

所属标签

相似技术帖

官网相关资源

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