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

STM32 USB-HID通信移植步骤STM32 USB HID键盘例程  

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程
+ a% V$ s  n' ^8 p$ e
) I2 \4 R; A7 b3 o, P6 k
最全USB HID开发资料,悉心整理一个月,亲自测试
& }1 q0 m/ K# |* i4 E
" X" n7 l$ }; z) J' S) K: O
通过STM32CUbeMX建立USB HID的双向通讯实验成功' {" `- M7 b8 k  G) V/ V* i. W) s
9 y" Q" P# U+ X' x4 `: B

' `: u5 `( C$ ]发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。
8 ?2 A* |, }  o
$ \, J$ J5 l$ Z
HID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在4 b" D* ?- z! z$ D, w# T$ v
& ]5 j7 M4 `" o
一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。& }% f# G8 \2 d9 y* t$ ]
. p; p6 n+ v& V& C
二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。
3 ~7 B/ s, b$ ~3 V) t2 w: y
" m2 ^( s3 Y: f5 ^' _/ |( b" ]' lu8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
  S; U2 W8 a  b2 X: H$ F0x83, /*idVendor (0x0483)*/' M4 v$ F  q7 L9 \1 R" z+ U2 h
0x04,5 }. m; Y$ J0 I0 d4 e
0x50, /*idProduct = 0x5750*/& U' ]4 z" _/ U! P0 _
0x57,2 _: H, }& J6 N+ W
//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。* X9 D- E. o" M4 w
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。5 X$ V3 K+ k5 E6 j  p( A
//注意小端模式,低字节在先。5 r2 y& e! |+ P9 Y6 `' F
//idProduct字段。产品ID号,我们这里取0x5750。, s1 X* S/ M5 ?  D  I5 \4 [  R
//注意小端模式,低字节应该在前。
" A) ?1 ^1 C9 }2 N; D1 q+ y5 m% V1 i, S/ O9 X+ Y& n
4 |: c6 p3 N8 d" s9 Z+ {2 G+ ^
const u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下
0 J8 s9 V/ a# e3 U
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */# U, `: a, j% d5 j& g
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */. i4 V0 W* X" `! n
  3. 0x03, /* bmAttributes: Interrupt endpoint */' i6 B1 Y; v2 F0 F( R. U
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */' L. ^* v/ I2 K3 Y4 K
  5. 0x00,
    ! V9 @) L9 a; G, g* e
  6. 0x20, /* bInterval: Polling Interval (32 ms) */8 {0 e3 a# r2 G3 L9 @$ n
  7. /* 34 */
    $ |/ v6 j) p2 T) }8 E
  8. 0x07, /* bLength: Endpoint Descriptor size */
    : R2 e5 ^4 m8 F7 s3 {$ Q
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    1 d) S$ E$ q5 g, s& n
  10. /* Endpoint descriptor type */
    " M, |; v( h1 p3 y0 f6 i$ y; {
  11. 0x01, /* bEndpointAddress: */- |6 B( G/ J6 x0 P. E. |7 w3 K
  12. /* Endpoint Address (OUT) */
    & i9 a; V7 ?% M! I
  13. 0x03, /* bmAttributes: Interrupt endpoint */2 D5 B: G6 ]# v
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */
    ' E9 K9 t  p8 E/ W1 a
  15. 0x00,* E, H9 |% {7 J" H/ L1 `$ k" r1 e
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码

2 Y2 i, {5 k% a& |1 C上面包含了“输入端点描述符”和“输出端点描述符”。
8 t4 |/ t: `- j//wMaxPacketSize字段。该端点的最大包长。0 P2 ^4 s! p, C) o& U
//bInterval字段。端点查询的时间,
! s8 M2 I3 R  S9 n# }3 G' g7 g
1 \" O0 Y2 T' J' |8 J5 q+ e为了实现更高速的通信我们修改如下:; h8 [2 R& i5 Q
  1. /******************** Descriptor of endpoint ********************/) p# E2 _# |- @. d5 d/ V
  2. /* 27 */
      e5 y5 N% }6 J/ ?+ F
  3. 0x07, /*bLength: Endpoint Descriptor size*/) {3 V0 [6 A/ u/ Y
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
      }  O. J0 C! K9 o7 h' F1 p& X2 E$ p
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/
    , f" I2 n; _. ?0 o- O3 T! G
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    7 I5 Q8 l* U0 U
  7. 0x40, /*wMaxPacketSize: 64 Byte max */
    5 I3 I* b. U! t( ?$ Z5 h. k" o/ ?
  8. 0x00,
    - {' l& {4 W0 T$ K2 V9 O3 d# j
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    $ f+ S8 x5 _6 w( T1 a
  10. /* 34 */2 a3 z; \' |: o0 |# @. i, {
  11. /******************** Descriptor of endpoint ********************/
    / F$ T4 S, _9 N4 E2 f
  12. /* 27 */
    ; g8 u) b5 U6 A  h$ K# h/ F
  13. 0x07, /*bLength: Endpoint Descriptor size*/
    : L% b& G' Q3 g# {4 _, M5 R
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/' b" ?1 f4 R: a# Q4 _6 v' _! D
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/; z" @& B9 u# h; D
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    % b6 {3 M. P; _" {9 G% T( I
  17. 0x40, /*wMaxPacketSize: 64 Byte max */1 E) n3 C  O, t6 P2 F! L; |0 ^
  18. 0x00,* X$ V  U3 A( z! z7 b
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/! ]# U. b0 Z) G9 E$ j! E
  20. " p* K( v/ g& n- A! x% b/ r
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。
    * P* H0 q0 Y, S5 L
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
    : u" y$ C" [8 r6 ~
  23. {) \+ @/ H, V0 O. ]9 ?. A! E( v
  24. 0x05, 0xFF, // USAGE_PAGE(User define)
    ) I: [8 ^! }% n7 r1 u% _
  25. 0x09, 0xFF, // USAGE(User define)
    . k1 W/ E/ ]) G- F) R( Z9 s
  26. 0xa1, 0x01, // COLLECTION (Application)# B5 B0 H8 r0 ]6 ~) m" _" n
  27. 0x05, 0x01, // USAGE_PAGE(1)
    0 B  j; V0 [* r
  28. 0x19, 0x00, // USAGE_MINIMUM(0)
    5 U) q7 ?- C( e7 R& T1 J. _
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)/ }9 q  E& i2 _# F8 G; N: k1 U
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)- H/ `# W' _7 W$ N, S
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)7 r7 B& x+ I8 [6 {; V
  32. 0x75, 0x08, // REPORT_SIZE (8)1 O1 a, Y. H1 p6 r  m, g# w4 Q
  33. 0x95, 0x40, // REPORT_COUNT (64)
    / q5 r/ \, g3 j% s' t: @
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)- ?, c/ W/ h" K7 \
  35. 0x05, 0x02, // USAGE_PAGE(2)
    4 p9 h; B6 w( g3 f+ S
  36. 0x19, 0x00, // USAGE_MINIMUM (0); H1 \5 I! O5 |' h% Q
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)8 E& X& C+ f5 l$ |" t8 J2 A8 Q+ o
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    0 }' f# N# o4 `/ ]/ ~
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255); P! |: s2 ^/ C2 W. ^. |9 q
  40. 0x95, 0x08, // REPORT_COUNT (8)7 t  U( q# t/ e6 S- m* t% m
  41. 0x75, 0x40, // REPORT_SIZE (64)5 W& b% j( U& K
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)  ]* k0 Y: \, r) k
  43. 0xc0 // END_COLLECTION
    2 ]8 c3 W! G3 r' }! i6 R5 A9 p, h2 V
  44. }; /* ReportDescriptor */! x9 [9 z/ U1 L: J+ @

  45. 9 j" h# R$ p- q
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]  |% A: Z0 e* X9 ]& Q# d
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]5 k' d+ x7 a3 D! B2 R" N
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码
1 Q3 \  P2 A$ p8 r8 m- j* [
分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。; A: l  ]/ y$ `* D  s' c3 ~+ f
$ T' \- L$ |; w# u( x- t" |
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39. `6 I* [6 V4 R9 V! a* x: C+ |) e
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 642 b. O& o3 q7 b* v' U; V
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 286 ]2 D% b6 \8 M: s
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码

+ U9 t8 r+ o2 o0 Z, L4 T三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数
( Z. i, Q% w- j6 \7 `  La) Set_System(void)
$ f( l. A+ p. ~; ~$ Sb) void Set_USBClock(void)
. a* G; T, k5 k1 d; @3 m) xc) void USB_Interrupts_Config(void); ~7 V& f3 W/ t+ Q
d) void USB_Cable_Config (FunctionalState NewState)9 L- q' P' R2 {# N6 f: K& T
特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。$ \5 s1 y; K& P% I

1 X5 c8 O0 G9 J
) G7 s+ c' I9 G$ z! b, ?四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。& Q  d6 A; C7 R/ R' E/ E
打开usb_prop.c文件,修改如下:1 }; P( M* A- K% t
  1. void CustomHID_Reset(void)
    " D; f- `" Q  |+ v6 O% `
  2. {
    5 r% V) ]3 U- _3 y' D% @6 l
  3. /* Set Joystick_DEVICE as not configured */* }$ @0 z9 @/ u, l. @* y- R; ]
  4. pInformation->Current_Configuration = 0;
    $ _6 g. `, v  R# X9 y4 x! l1 n
  5. pInformation->Current_Interface = 0;/*the default Interface*/
    ; e! t1 v0 A5 P3 j' p6 W, Z( t
  6. SetBTABLE(BTABLE_ADDRESS);1 f" D' K. U2 Q. t, }1 C

  7. * ~/ H( G! ?4 t3 `( m/ o4 Y1 A. i
  8. /* Initialize Endpoint 0 */
    % T8 k- ], C% d: C( d0 V
  9. SetEPType(ENDP0, EP_CONTROL);8 X" D/ I  y# W; j
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);4 H2 ]6 K3 G% v% q6 b! q! T7 K0 V8 A7 {
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);
    5 @+ K& y, D8 h6 b
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    " f: b* q, F6 B3 F
  13. Clear_Status_Out(ENDP0);
    - G1 U5 `2 h7 c  H+ j* C+ K/ ?) |
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);  x) Y/ H) p, q, a1 h7 `
  15. SetEPRxValid(ENDP0);: l0 d8 T( ^% G( m

  16. * m9 t/ `% P5 i" Y
  17. /* Initialize Endpoint 1 */
    , M+ S% ]7 s5 _& r0 m( j
  18. SetEPType(ENDP1, EP_INTERRUPT);
    # Q! f) {: C* h2 ~( F; n7 r
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);5 P) e% [8 N# K
  20. SetEPTxCount(ENDP1, 64);
    + I2 f/ {8 A; R# t
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);
    9 z) b: z) o' T; ]: N7 d
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);! i- x& m! k; T9 Z* r: ^& X& D
  23. * K" i; W( K7 D' T+ G7 T5 l
  24. /* Initialize Endpoint 1 */
    3 t; R, u1 X5 i, p! Q2 w
  25. // SetEPType(ENDP1, EP_INTERRUPT);
    / n; V' L8 Z) v5 o
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);
    3 ?( ?2 H: q& o& f
  27. SetEPRxCount(ENDP1, 64);
    8 p. [' g- }. P( F, E$ F( i/ p
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);, _# W8 m0 L; X& {8 C" s. R
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);/ ^# |$ X/ U  \% o  Y
  30. /* Set this device to response on default address */: h$ _4 p0 }) H) ?
  31. SetDeviceAddress(0);9 l% f- Y1 Z4 f# q) `/ D7 T' z1 K1 e
  32. }
复制代码
8 G6 }& e: f( D, b5 `; {
五、usb_endp.c文件9 V1 G7 v/ N6 D, z
  1. void EP1_OUT_Callback(void)
    9 Z- ]5 \5 p# t0 P4 r
  2. {
    & q. `4 r9 [- t. s$ Z/ N# A
  3. 这些写接收代码
    # y, X( T, X/ Q
  4. }
复制代码
! X& v6 R# m# S
六、数据发送和接收,举例说明
7 G! Z1 O5 ^& _7 z+ ~6 e1、数据接收( u% _( _' V4 Z
  1. u8 DataLen;
    3 V7 B- n& `- m) `; Y* H# ?
  2. DataLen = GetEPRxCount(ENDP1);
    ( X- d8 l% {2 [, d
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);1 E8 ~) k% b) `* }! J4 e' c
  4. SetEPRxValid(ENDP1);
    / P0 q) H1 ^8 k3 L5 p
  5. USART1_Send(DataLen);% U2 O: I$ c- Y" z" s3 b' S
  6. count_out = 1;
复制代码

. v: E& T1 n. N+ d; K& ~( V) Q2、数据发送) v' H6 D5 z/ @: W, T
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
    0 C2 ?- J9 {4 r, {; }) {; c* r
  2. SetEPTxCount(ENDP1, 64); # _0 a# E8 _* `& O) _
  3. SetEPTxValid(ENDP1);
复制代码

- f% T& R9 y1 L6 _! v5 x; @如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。, a# G2 R) j! L( {  Q* y) U2 e: h  L2 o

& S" g! \3 C; C7 `, v

% b5 i) @4 X2 f1 Q7 F(by xidongs)
! z5 B) D7 o0 [9 p: P  v5 D  d
收藏 23 评论61 发布时间:2016-7-24 16:52

举报

61个回答
ct1991 回答时间:2016-8-10 22:48:08
谢谢,已经移植可以识别,但是上位机软件怎么写啊
5440sim 回答时间:2020-2-6 22:19:02
没有看到你最后写的 发送数据 写在哪个函数啊
xiaoxiaolinlin 回答时间:2020-10-30 15:47:07
有没有上位机软件的编写方法
anny 回答时间:2016-7-25 08:02:01
谢谢分享,标记一下
stary666 回答时间:2016-7-25 10:18:29
bijibenbenq 回答时间:2016-7-25 15:20:24
谢谢分享,标记一下
lzts 回答时间:2016-7-25 23:16:14
谢谢分享. 学习了
那就地方 回答时间:2016-7-26 08:37:29
谢谢,分享
kingchou2 回答时间:2016-7-26 14:45:23
支持下,标记下!
luvisen 回答时间:2016-8-7 12:24:50
感谢分享
zlo007 回答时间:2016-8-9 16:32:26
谢谢分享,标记一下
a小小菜籽 回答时间:2017-1-5 14:59:59
很不错很不错啊
quanmengmengzi 回答时间:2018-3-11 23:19:50
正好在学习这一块,感谢分享~
两zzl 回答时间:2018-3-13 09:57:15
牛逼了啊,谢谢
jcyhzls 回答时间:2018-3-13 12:10:08
好东西
liaoyuyu1 回答时间:2018-3-14 15:39:04
啊士大夫随风倒
12345下一页
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版