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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程! ~9 V; S% Y' q# n) h0 ^2 k
& {" X- O& q% t2 ^% f& \$ r
最全USB HID开发资料,悉心整理一个月,亲自测试
$ u& n6 L7 C  u) c
- p6 W2 W1 V  z6 \4 a  M
通过STM32CUbeMX建立USB HID的双向通讯实验成功2 C* ]! P. f/ u. l0 E3 L

0 _% W. H- ?# u' {2 n
: G+ h9 h! n, Q9 D$ A- W3 S8 o
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。
0 u  e8 F- v* F8 c7 `3 P8 l, n
* h& Y9 z4 y3 X2 W- t
HID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在9 o0 V7 \* }9 y- a  Z  K$ i! G/ L
1 q9 j' C5 T; O
一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。% |/ S2 C; v- l3 _

( f: m  x5 ^" {$ q. c' x/ U9 o二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。
+ c3 `/ [% B% x
* [: s( X/ A! a3 v5 Gu8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的. y7 I% ^  F+ Z. }  l: a
0x83, /*idVendor (0x0483)*/
/ M& l  v/ l6 T  A. f! @0x04,- Z$ |  u6 C  I3 o+ ~3 c
0x50, /*idProduct = 0x5750*/
3 m, Y2 e" [+ ]# U, u$ P0x57,
! c% D1 M& I5 [$ v$ P! H$ U) t//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。7 X" H! q5 ]9 L! B- ?. J" a7 n
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
* Y! E8 X5 z9 O9 v- N//注意小端模式,低字节在先。
2 z+ P/ R9 F8 D; G9 p//idProduct字段。产品ID号,我们这里取0x5750。
8 F4 b. R( l0 J; a" p4 j2 r//注意小端模式,低字节应该在前。
7 U$ c/ ~& x+ B
, v% W: K. K0 n  e7 w$ t9 U$ N( R4 X# e  R% @# H4 M+ `- t' V
const u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下: k, Q' m- ?6 K3 N3 K! C& W
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    5 E  N% c7 i# n. g7 a8 ^8 R
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */
    , v; X! H/ F1 }  Q/ B3 a
  3. 0x03, /* bmAttributes: Interrupt endpoint */
    9 R/ @" C* g9 j5 r' t7 N
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */
    , s" d) g" W8 g
  5. 0x00,
    9 k% N" h- l- l) p0 x8 C
  6. 0x20, /* bInterval: Polling Interval (32 ms) */4 x9 P* g" M1 V
  7. /* 34 */7 ?: h( E* N+ t
  8. 0x07, /* bLength: Endpoint Descriptor size */- U+ `" d4 @3 Z$ @& x8 `  R5 Q
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */+ P5 }# e6 |2 l! b& L! z) ?
  10. /* Endpoint descriptor type */, \% n  m" T/ |6 ?
  11. 0x01, /* bEndpointAddress: */6 V0 y+ J! j5 n# ]* A
  12. /* Endpoint Address (OUT) */
    ' a5 ~% m& G9 ]6 D
  13. 0x03, /* bmAttributes: Interrupt endpoint */+ N' _2 u; |& c, h; O4 k( w4 B
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */8 S# E: X" A, `
  15. 0x00,3 m5 o0 E8 \% e8 N" Z% ?9 j
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码
! j- K7 L: E0 {/ T9 J9 A
上面包含了“输入端点描述符”和“输出端点描述符”。
# C( \4 `9 i! \8 K% i* Z//wMaxPacketSize字段。该端点的最大包长。
5 \2 Y  p( l% K; X' [//bInterval字段。端点查询的时间,; N- g) m- ~+ e
7 M& B: C* H1 h* g; N; o
为了实现更高速的通信我们修改如下:
" T6 X' \; J+ N1 Q
  1. /******************** Descriptor of endpoint ********************/
    1 S4 z  ]7 F3 p* a
  2. /* 27 */
    4 w4 g/ c4 u& V$ r9 a% G
  3. 0x07, /*bLength: Endpoint Descriptor size*/
    % f  t/ j# K" Y- J# Z( w5 \
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    : s, B7 ^2 M' V1 \2 X; a
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/
      b6 l/ W: J1 F7 f0 l8 J* }+ w0 V' m
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    ) `3 v2 T7 h* |3 M0 V0 c: {- A
  7. 0x40, /*wMaxPacketSize: 64 Byte max */
    6 h* o% v9 g) K: V+ @; F
  8. 0x00,' m1 b! i  ?9 a7 j& A
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/+ E( t$ P! X2 V
  10. /* 34 */5 C$ X) l$ X! @/ d& C
  11. /******************** Descriptor of endpoint ********************/
    3 v7 q' D9 b. T# \
  12. /* 27 */6 o4 B! _( D. `: s$ B: t
  13. 0x07, /*bLength: Endpoint Descriptor size*/) J* h) l' K+ Z; q  h& j
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    5 Q( H3 g& N% ~7 V$ V
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/
    $ X9 B  [. {# n' F  q) Q# p
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    $ p0 |9 }. H- A5 `4 |
  17. 0x40, /*wMaxPacketSize: 64 Byte max */' z; f* b2 U; u# u* ^
  18. 0x00,
    5 I7 J# a+ K! J: R5 o/ j4 K0 N
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/4 b% r! d( B7 R& [' T  g! A

  20. 7 e/ k  K0 A/ j' |
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。, U( ~: b. I0 L; r3 R8 {5 G
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =6 E" F, B* x; J" H
  23. {
    , g, \5 x8 {9 ^6 E# T7 a( u( n# O
  24. 0x05, 0xFF, // USAGE_PAGE(User define)4 s+ e1 X0 a: A3 E
  25. 0x09, 0xFF, // USAGE(User define)
    ! y" P: D9 ~7 j8 O; f, U
  26. 0xa1, 0x01, // COLLECTION (Application). A* f% i8 U7 \# d' d
  27. 0x05, 0x01, // USAGE_PAGE(1); c1 g: H5 }) M$ R" K6 e  p- |1 h
  28. 0x19, 0x00, // USAGE_MINIMUM(0)7 T+ A  V1 f: M
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255): q2 z6 Q) y6 M, S
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    2 u6 _3 R: J2 \  t( X6 J7 x
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    - }9 ?+ l6 l0 V" w7 z' ]' C
  32. 0x75, 0x08, // REPORT_SIZE (8)
    * [+ ?( s( {( g+ l) I
  33. 0x95, 0x40, // REPORT_COUNT (64)
    7 @! ^3 I) D/ r- L
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)+ @# s" d# w+ F# p: q
  35. 0x05, 0x02, // USAGE_PAGE(2)# L9 l9 x9 U# @( K
  36. 0x19, 0x00, // USAGE_MINIMUM (0)
    1 ]  W/ Q5 y6 j
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)
    ! p$ c6 i) t9 Q; D- z4 C  m$ A- B
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    ' b  s$ d( f; y( }0 D- \; q0 v
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    : \! q  J  O" U+ `
  40. 0x95, 0x08, // REPORT_COUNT (8)6 b# Z, ~6 j. }# ~* }- R' {
  41. 0x75, 0x40, // REPORT_SIZE (64)* t# `) u1 e, f# c: D9 m8 v
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)$ g# \# g. Y& m/ U1 s4 e+ z# t& I$ B
  43. 0xc0 // END_COLLECTION& y6 ~( d9 V; t
  44. }; /* ReportDescriptor */
    ; U4 r2 M& M6 v8 g% c

  45. + Y, o# Y$ ]3 U0 f- S/ V/ V# ?; M
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]0 y4 W) O% S3 a! w& A
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]
    . }5 D- k' O2 I+ s( O
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码
, i0 C4 h! b1 g6 A' x+ |& i
分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。
" x! D" h2 p. c# [/ {: U5 G9 {- B: }& h7 }" Q
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39
    $ i# G0 r1 D6 g1 m/ l( u# G6 {3 l% N- H
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64
    0 b/ }: w' I4 ~* V, \9 A8 b
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 286 I& o2 V! S- `2 }2 r2 o/ s
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码
, [, }- f' \  A) O, s1 D; [
三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数7 U& S# R5 ?/ P0 D9 n
a) Set_System(void)
$ Z) K4 l+ e$ @# C0 n. @9 _b) void Set_USBClock(void)
. E- |0 B8 @: Gc) void USB_Interrupts_Config(void)
1 |! t9 u, U; J( F2 ~d) void USB_Cable_Config (FunctionalState NewState)
9 U9 B* ^$ Y  ~# ]3 }5 g4 u. W4 w9 F特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。9 d: Y( P0 J; v- |( p" x# H

4 Z0 ^1 ]6 H( d% v* |* k$ ~
, q6 }( `% m) H8 D9 k# ~; Y/ S四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。
6 ~6 P: _$ U) i* _6 G打开usb_prop.c文件,修改如下:
* |2 @8 l4 d# u% r
  1. void CustomHID_Reset(void). j( C" W7 S! n9 H$ V' A% [* a  Q
  2. {
    ' @& X% [+ ~& }; Z* `
  3. /* Set Joystick_DEVICE as not configured */
    # k* H8 _6 k( q, m
  4. pInformation->Current_Configuration = 0;
      s) O4 `. F7 j( `: W% X
  5. pInformation->Current_Interface = 0;/*the default Interface*/
    6 B8 [! `# Z" X4 T' L) z
  6. SetBTABLE(BTABLE_ADDRESS);
    6 Z; b  M% C! I

  7. 4 g' M4 {1 o% J( H! E' o+ I9 Q
  8. /* Initialize Endpoint 0 */2 ~5 |( o, m2 c0 m# C3 D
  9. SetEPType(ENDP0, EP_CONTROL);& C. _) o0 o) C6 C
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);
    0 C1 s2 f# a; o& `2 E
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);
    / L) T% t' Y5 a. P. U. g
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    * M4 g# L0 _: K: C: k
  13. Clear_Status_Out(ENDP0);
    . u" B+ \! n- @! J" {9 K' z
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);# H. V0 G  V! T; h6 R8 s7 a) L
  15. SetEPRxValid(ENDP0);$ h. x% N$ M! K0 E. v4 l
  16. ! B! O; A. c6 K# ~, |
  17. /* Initialize Endpoint 1 */
    , I  D, Y- [: q. [
  18. SetEPType(ENDP1, EP_INTERRUPT);
    + P' E4 {4 [3 Y+ Y3 E# u$ ?6 }
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);% m# C2 l. M- T( i& Z" J
  20. SetEPTxCount(ENDP1, 64);% S9 w. q$ r4 r+ X9 i8 ]- l( y
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);# z  T1 u- ]  y( M+ C( G4 C
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);5 {* ]# {8 ]6 b9 z

  23.   X0 Z9 J5 S) v: o
  24. /* Initialize Endpoint 1 */
    $ u2 `3 d" g! _
  25. // SetEPType(ENDP1, EP_INTERRUPT);! ]9 P8 d: J* f0 w( t* }# b3 k4 R5 |1 C
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);
    , U* v! B: c3 w$ P
  27. SetEPRxCount(ENDP1, 64);
    ' `8 w( D) a, q- y
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);
    / [0 c3 D) c$ Y" P9 z
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);7 G: \% q! O! }) S' W& j* Y
  30. /* Set this device to response on default address */# e4 h0 ]* c9 l! e0 K) c
  31. SetDeviceAddress(0);
    ' r; s9 V9 M* j' J3 k, |8 i, h' Q) Y
  32. }
复制代码
- L9 V2 X6 A$ t# Q% k$ {' p* O1 k( Z
五、usb_endp.c文件7 w9 u6 v- D! ?1 D6 h
  1. void EP1_OUT_Callback(void). u8 w  D( k" [2 S% T+ O
  2. {, J$ p- m, z" H
  3. 这些写接收代码
    * \9 D# r6 A( N. O
  4. }
复制代码

! U* V# ]4 K$ j. g六、数据发送和接收,举例说明
9 Z' K  Y! N5 D. h1、数据接收
+ N$ t) D+ v0 R8 E0 c- ^
  1. u8 DataLen;, ]- L% R* I& N1 b! @. u8 t
  2. DataLen = GetEPRxCount(ENDP1);' l2 V! d2 w8 w! R3 `5 L
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);' V/ H7 V$ b1 _+ P
  4. SetEPRxValid(ENDP1);) L4 Z, w& k9 O8 [2 Y
  5. USART1_Send(DataLen);
    9 a5 e0 i) b) c# K" ?
  6. count_out = 1;
复制代码

2 E5 j1 f' T' k7 M' V# M2、数据发送
: c- h" Z2 u6 j6 a& g7 `' y9 S
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
    ! E9 N+ S4 [7 m3 I
  2. SetEPTxCount(ENDP1, 64);
    7 W) D( a/ W4 D. K3 w
  3. SetEPTxValid(ENDP1);
复制代码

9 z- B, v8 n% g" @% [如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。
* p- Y/ c* Q4 i; C3 s& C4 V. f; y0 F) t3 Q
2 U9 o. G& {0 c4 F0 \
(by xidongs)
5 ^+ I% {: G  ?. n, D4 A' U
收藏 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 手机版