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

【经验分享】STM32F103 上 USB 的端点资源灵活使用

[复制链接]
STMCU小助手 发布时间:2022-2-11 22:31
前言
2 D% t8 \. w( {/ n% s( j0 k理解 STM32F103 上 USB 模块的端点资源,灵活在应用中的配置。6 ]' C$ Z5 ^6 q8 P# M
4 R  i; V7 G$ Y  _% A1 h
问题
8 K  w' l% y. o; O1 a某客户使用 STM32F103 的 USB 模块做设备时和上位机 PC 连接时碰到一个问题:PC 端驱动已经固. y) s  G8 c5 r# J# p
定好,是对下位机 USB 设备上的地址编号为 0x0A 和 0x0B 的两个端点通信,从 0x0A 端点读取数据,
1 ^: Q4 P3 k$ ~2 s+ [7 T; ^向 0x0B 端点写数据。而 STM32F103 的 USB 模块只有 8 个双向端点,能否支持这样的寻址。
( r% R' Q& Z& `9 _! ^2 }6 G# f9 @$ x3 O  X
1. 问题调研
, V5 h6 Y& S3 ]' Q* H   我们先来看看 STM32F103 上的 USB 端点资源。从 STM32F103 参考手册(RM0008)可知,一共有8 个双向端点,对应 8 个寄存器来控制其属性和表征其状态。如下图,可知每一对端点必须配置成相同的端点地址,这个地址位域是 4 位,取值从 0x0 到 0x0F 范围。# t& j; Z, [7 |& r6 a+ @

. q& \6 s1 c6 g ~{FA[HY@EV72{SAB@39H){2.png
- d/ ?$ ^0 q/ k; J8 U0 K: d; G3 b& X+ y7 p+ \( P' q
   和以下摘录的 USB 规范符合:
& b* c; H0 \; }' W" G3 ^
1 {! k8 m: `3 L+ o. f 4Q4IJ]Y_LY]IH~MN6TUMTKP.png
/ z1 L# V# b1 ?% j+ U% N- }
; D: E/ H3 p0 Y' H- i: P4 [9 g1 U   客户使用的是 STSW-STM32121(STM32F10x, STM32L1xx and STM32F3xx 全速 USB 设备库),那么应该修改哪些代码呢?
4 a- \% `# W, ]8 x# j, _+ t  u2. 问题分析
: W" W- p4 D  O3 o0 L, M   首先,USB 设备通过端点描述符向主机 PC 报告它所使用的端点有哪些:每个端点的地址(即 USB 规范里,以及参考手册的寄存器中规定的那 4 位地址域)、传输方向、传输类型、最大包长等。以STSW-STM32121 库中的 Mass_Storage 例程为例,需要把中的端点描述符做如下修改:0x0A 地址的端点作为 IN 端点(PC 从它读取数据),0x0B 地址的端点作为 OUT 端点(PC 向它写据)。% K# m# F: ?, x, `& ~
  1. const uint8_t MASS_ConfigDescriptor[MASS_SIZ_CONFIG_DESC] =
      k1 x) [. p( ]) l( u7 J$ H
  2. {
    + b0 [4 v& J7 |. ?4 i# z
  3. 。。。。。。
    7 _9 ~1 S7 x9 M7 A
  4. /* 18 */+ y5 w6 n% x- ]! T% c0 P
  5. 0x07, /*Endpoint descriptor length = 7*/! t  U7 P9 k' x/ ^* O1 m
  6. 0x05, /*Endpoint descriptor type */
    5 Z; |3 l5 f; g0 g% B4 i
  7. 0x8A, //0x81, /*Endpoint address (IN, address 1) */
    / g3 E5 A0 N8 I( c+ d
  8. 0x02, /*Bulk endpoint type */
    # A! n- Z. p' Q1 u% k3 u
  9. 0x40, /*Maximum packet size (64 bytes) */9 T6 g, ]% r/ Z5 I3 T1 ]$ o
  10. 0x00,' M  e% {6 y# i% I! k% `# C) z
  11. 0x00, /*Polling interval in milliseconds */
    / ]/ U8 }7 ~8 J/ O0 ?
  12. /* 25 */( ]8 z6 z$ N4 k# ]
  13. 0x07, /*Endpoint descriptor length = 7 */
    1 T* j8 A9 O6 K, G3 a
  14. 0x05, /*Endpoint descriptor type */
    4 M3 `0 P' d, o: M$ a8 Q, H
  15. 0x0B, //0x02, /*Endpoint address (OUT, address 2) */* U1 v7 Q2 ?3 s) t1 t6 K8 d+ p2 W
  16. 0x02, /*Bulk endpoint type */& {& S# [0 B7 s# q
  17. 0x40, /*Maximum packet size (64 bytes) */
    $ u( |5 ~& n& c
  18. 0x00,$ S6 p8 d5 T  m! b/ V" Z, R$ D6 W
  19. 0x00 /*Polling interval in milliseconds*/
    6 B0 p) t( X& c, J- H
  20. /*32*/$ p+ y5 B# H1 d# T$ `" Z& m* @
  21. };
    / u2 m9 y& U6 @; r: R- v
复制代码
! f# M2 K5 V3 ?' k0 K4 o
接下来就是考虑使用 STM32F103 USB 模块提供的 8 个双向端点的哪个端点了。我们刚才从参考手册
4 ]" k; o9 l' k0 X- T, B( g; U关于寄存器描述的截图中看到,每一对端点具有相同的地址。在库函数里,对端点寄存器的地址位域& i" |' G' q5 D5 T2 U0 @$ c1 {
的操作在这里:
4 g8 R7 Y3 e8 ~( ]* k& U, T4 D
  1. void SetDeviceAddress(uint8_t Val)- p: H1 D. K, [* R
  2. {
    + u: t2 j9 G" g5 |
  3. uint32_t i;
    4 q1 `5 j9 ~6 h2 U! ]' W
  4. uint32_t nEP = Device_Table.Total_Endpoint;% I  w: ^' w! z* l% I5 W8 b( m& a
  5. /* set address in every used endpoint */1 B! k; ~9 @- T: F& W7 e" v8 g
  6. for (i = 0; i < nEP; i++)* k2 P# }! u0 q, r
  7. {5 U/ E! i9 M9 G: G
  8. _SetEPAddress((uint8_t)i, (uint8_t)i);
    / V# z$ Y, L# x* T
  9. } /* for */
    " f1 V* P& ]9 y
  10. _SetDADDR(Val | DADDR_EF); /* set device address and enable function */& i% _" t! l2 ]/ q" _; O
  11. }& P0 ]0 ^) q) r* o6 Z9 i2 Z
复制代码
这个函数的名称是“设置(USB)设备地址”,但是其中除了最后一句是在设置 USB 设备的地址,前面2 Z0 f- i3 d% `7 z+ W3 g5 S- T
的 for 循环是在设置该设备内的端点地址。3 s1 m5 i8 {" k, J8 d) y

: u- \) y) A& K   从以上绿色标注的代码段可以看到,库代码固定给 1 号端点”0x01”这个地址,2 号端点”0x02”这个地址,以此类推。这里的”1 号”、”2 号”指的是端点的编号,对应的就是之前提到的 8 个寄存器的编号,即下图中的 n=0~7。n 在这里就是端点的编号。
8 c$ ^5 p; N. X: x3 O
* |% Q: c  ~! P' H7 ~/ s VHN6%WI7UEW4X5`{5LMJYDM.png
/ R+ e, I  v- u9 b, B  Q+ C- K1 Y( n; d! S+ }) s
   那么在这个应用中,需要用到地址为 0x0A 和 0x0B 的两个端点,但是端点编号最多只能到 7,因此需要修改库代码中关于端点地址设置的地方如下:这里,我们使用编号为 1 和 2 的两个端点。# b# B+ T* r. |6 f- `. ^1 z* p. L
   为啥不用编号 0?因为编号 0 默认给双向 0 端点,即用于控制传输的 0 端点。
6 j1 ~& X$ }6 k# b; X   为啥用 2 个编号?因为这里需要 2 个不同的端点地址,必须用 2 个编号。一个编号对应的 2 个端点必须共享同样的端点地址。
/ B, y- ~9 A3 r0 K( R( x
  1. void SetDeviceAddress(uint8_t Val)! V. R' q; P$ P* a, h  w) r# }& K
  2. {
    6 C: F. y& R! Z3 O& M
  3. /* set address in every used endpoint */9 L+ ~( W! P& l. b" V
  4. _SetEPAddress((uint8_t)0, (uint8_t)0);
    3 p' u9 N7 o& A7 Y& t9 o# }
  5. _SetEPAddress((uint8_t)1, (uint8_t)0x0A); // 1 号端点是 0x8A,即地址为 A 的 IN 端点
    : `) L) L9 p$ k+ f1 ^# z# R3 d# {
  6. _SetEPAddress((uint8_t)2, (uint8_t)0x0B); // 2 号端点是 0x0B,即地址位 B 的 OUT 端点
    8 @. e2 p7 y' `4 s: o5 P& ~( ~
  7. _SetDADDR(Val | DADDR_EF); /* set device address and enable function */
    % Y* u. a  `; F. F  ~
  8. }
复制代码
  既然这里指定了使用编号 1 和编号 2 的端点,那么需要在中设置这两个端点的硬件收发缓冲区地址
8 g: r' S9 R; x! N# e+ O: u9 g
  1. /* EP0 */: Q! s; o7 }( |7 V
  2. /* rx/tx buffer base address */
    7 c) W7 N$ k! e9 E) ~& [! V1 @6 a
  3. #define ENDP0_RXADDR (0x18)
    ) M6 |" J, W. Z( ?8 |
  4. #define ENDP0_TXADDR (0x58)' C) m5 t2 l& D# ]0 I2 P9 @0 }
  5. /* 1 号端点,IN 端点,发送缓冲区如下 */+ }. i" L# h8 E$ O9 ]% a
  6. #define ENDP1_TXADDR (0x98)
    * b+ Z- E3 [) W, G: ^* u
  7. /* 2 号端点,OUT 端点,接收缓冲区如下 */
复制代码
& b: g; C- w# }) s2 U
   当然如果你很任性,一定要使用编号为 6 和 7 的端点,也可以,那么代码就如下修改:3 g# B/ J' R& F6 h/ Z, Y3 t) P
  1.    void SetDeviceAddress(uint8_t Val)- j. Y9 B6 C' q& M# j9 Q% `# K
  2.    {
    / U. e0 t2 E# N
  3.    /* set address in every used endpoint */
    ' F& F6 D+ G: P0 K
  4.    _SetEPAddress((uint8_t)0, (uint8_t)0);
    7 T& o: x$ R, K; K  O% Q
  5.    _SetEPAddress((uint8_t)6, (uint8_t)0x0A);
    ; k3 y0 n8 P" D8 d- z0 G6 k
  6.    _SetEPAddress((uint8_t)7, (uint8_t)0x0B);
    * U& N% I3 G' k6 U. g3 ~% z$ a0 I
  7.    _SetDADDR(Val | DADDR_EF); /* set device address and enable function */% a% `0 m- ]2 u& H9 i7 u
  8.    }
复制代码
, f( K% B6 ?! v
   相应地,需要在中指明编号为 6 和 7 的这两个端点的硬件收发缓冲区地址。那么如法炮% Z6 [8 [5 s: m4 S4 b8 ^* j( n) v
   制做如下修改,就可以了吗?就可以了吗?就可以了吗?+ A/ g! E, m) F5 h+ `4 `
  1.    /* EP0 */
    + x$ X: n& P) N0 D3 j; a  F! B
  2.    /* rx/tx buffer base address */
    * ]" O* E4 k1 v) u. p. h
  3.    #define ENDP0_RXADDR (0x18)- V1 n1 T6 B0 I/ c3 B( Q
  4.    #define ENDP0_TXADDR (0x58)
    5 f: |6 P7 Q! v; F. E
  5.    /* 6 号端点,IN 端点,发送缓冲区如下 */
    7 T/ }7 X1 [' a4 |, m1 \* e" R+ ~
  6.    #define ENDP6_TXADDR (0x98)8 u* J5 [! K' [5 r% L1 n2 r& h& w
  7.    /* 7 号端点,OUT 端点,接收缓冲区如下 */" ]4 e% T, s- J6 }. E
  8.    #define ENDP7_RXADDR (0xD8)
复制代码

6 T7 w9 I- s0 I$ d( k   答案是否定的!以下的代码才 OK。欲知详情,请参考下一条应用技巧《STM32F103 上 USB 模块的$ N- M* @9 H: B/ D+ [* q
   包缓冲区详解》. G3 s) ]) {9 N9 G
  1.    /* EP0 */1 ~- H* i- S* l6 P& ?9 }  Q  R
  2.    /* rx/tx buffer base address */9 L0 u. c7 Q" k$ {/ ^; @1 J. v
  3.    #define ENDP0_RXADDR (0x40)
    7 d2 ^) m" F+ |
  4.    #define ENDP0_TXADDR (0x80)  }+ P0 X& Q, s4 \
  5.    /* 6 号端点,IN 端点,发送缓冲区如下 */! G) K5 f# Q, A. C( |% f
  6.    #define ENDP6_TXADDR (0xC0)
    5 D. Y/ w3 `; G; _1 H/ c  S" N/ D& l
  7.    /* 7 号端点,OUT 端点,接收缓冲区如下 */
复制代码

3 [- z* K: ]# V
$ H) v" T, ~, G0 p
收藏 评论0 发布时间:2022-2-11 22:31

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版