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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程
/ k/ ~  g3 N1 A2 l
2 j6 L: D5 n1 w. D9 g$ k+ V' N
最全USB HID开发资料,悉心整理一个月,亲自测试* ]9 _: S- T9 m/ k; m- S( D' w

9 D0 P. m1 S5 a7 t# v4 s6 q) V9 ]
通过STM32CUbeMX建立USB HID的双向通讯实验成功
9 a- \7 }. \5 G: Q. A" t# j' q# ^3 X
# B* Y! U, ^6 g& O/ i
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。
9 X, L/ K! q5 G7 u, b

, S3 w$ b5 D6 w# l( w) lHID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在
' v! b9 Z) T# ~( H1 I) t2 @; `0 w2 g+ F6 i& v2 d$ C9 V
一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。
8 O& Q. n) O8 O, p
7 `! u& f: W% n# z二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。
4 }( }8 R' z  I) |& j; D
0 m1 b! }  j' Q! a9 J8 |- Ju8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的) }( s: C2 s6 a1 K: U2 B7 r
0x83, /*idVendor (0x0483)*/
. J# v! O; ~9 a4 R0x04,3 m  l# O8 ]) i2 {0 ^, ]1 T
0x50, /*idProduct = 0x5750*/
0 s* q/ h6 r; t3 }. D0x57,
7 `2 m6 q1 P& E1 t, l( x; l% N/ W//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。8 [1 a8 i, H4 n. ^( G1 l' z. T% ?
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
0 c5 _2 \; D: U  A) `0 I6 c: M' _//注意小端模式,低字节在先。& [' q' n9 R& c" L# ~) x1 z
//idProduct字段。产品ID号,我们这里取0x5750。+ o9 @* A8 \5 k3 {8 C
//注意小端模式,低字节应该在前。
" c* F$ c) |, f" m+ a  Q; O- s  E: }9 U. _2 {# e6 ^9 v# E+ Y+ k

- X9 x( E0 j9 d" f, |6 x( n/ Jconst u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下
1 Q2 ^5 s+ w" c% U
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    ( U- w* B5 v4 E, I' L
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */0 m; n! J' R" M2 `( q
  3. 0x03, /* bmAttributes: Interrupt endpoint */$ R( x0 a' }& ?9 U1 C
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */: K8 `" j( c$ s  S9 `/ n
  5. 0x00,
    ' _$ @" l* |5 }0 |' z3 Z! y5 E+ D
  6. 0x20, /* bInterval: Polling Interval (32 ms) */
    " i3 d. e* f3 d+ O* z
  7. /* 34 */* a) q8 @4 S) k: p( d
  8. 0x07, /* bLength: Endpoint Descriptor size */! E7 h1 R* O3 ~5 l- M
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    $ ]; ^/ d0 d6 w. F7 g" u- C
  10. /* Endpoint descriptor type */9 S. w# u# I5 f0 ?& H( Y7 ~
  11. 0x01, /* bEndpointAddress: */
    5 B- v% N( ~, s1 y2 b6 X
  12. /* Endpoint Address (OUT) */
      M+ |$ o6 S) C5 @7 _7 k3 H
  13. 0x03, /* bmAttributes: Interrupt endpoint */0 b) W" d% ~* P$ a2 T4 i. [
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */  N) l; P% K: Z) H/ N# v' z
  15. 0x00,
    & p& F5 I; ]; F; w) E& r3 A0 x
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码

+ }& O# ^0 a- @# @6 B上面包含了“输入端点描述符”和“输出端点描述符”。
$ n% o6 y  R4 L1 W  I//wMaxPacketSize字段。该端点的最大包长。# L9 K* x& k* X$ C4 D
//bInterval字段。端点查询的时间,
- ?( N/ E, [" S7 B8 F5 _5 _3 R
; a8 }+ e9 B0 z/ O) A$ Y- j+ L为了实现更高速的通信我们修改如下:  K) H; n4 s2 L( f
  1. /******************** Descriptor of endpoint ********************/
    0 P2 R$ \( b3 T6 e
  2. /* 27 */
    9 d1 t) h1 j! J% X
  3. 0x07, /*bLength: Endpoint Descriptor size*/8 G: |$ L* o2 Q7 q4 m$ w
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/3 U1 H5 H# x; ]" h
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/
      j9 s; G8 g3 O4 i$ f6 [) S
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    5 c& y' ?  Q) [
  7. 0x40, /*wMaxPacketSize: 64 Byte max */5 J, q/ K: E$ u1 H$ n1 n% ^* u8 {
  8. 0x00,
    7 w6 v6 Y( t' K! Q( \1 p$ H
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/! k6 x% |+ W" i8 B( W3 N
  10. /* 34 */, |. r0 n4 Q6 g* U4 I" B% N
  11. /******************** Descriptor of endpoint ********************/1 R9 w6 s! X! V- t' d& z9 n
  12. /* 27 */+ W: k/ C2 k9 ]% z0 z8 P6 x  Q
  13. 0x07, /*bLength: Endpoint Descriptor size*/9 |4 t3 t& A( }6 w& M
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    8 H0 P  g. A2 c" T
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/
    7 ~7 i# Y$ R; ?7 x: ^! Y+ y0 r  C
  16. 0x03, /*bmAttributes: Interrupt endpoint*/! U4 T$ V4 D; J4 F) v6 M: }& v
  17. 0x40, /*wMaxPacketSize: 64 Byte max */
    ; N7 Q+ Y6 ~4 T$ Y9 b& n
  18. 0x00,1 |6 J0 u/ T, V& B, c% T
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    ' Q9 ]# t- J0 F- u. @- {, e
  20. 7 R. |8 P5 Z) w
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。
    # R5 n6 n' I" ]. {- d& p
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =3 l6 H  p% a1 t3 I6 B
  23. {
    * ~2 `( P1 t7 j
  24. 0x05, 0xFF, // USAGE_PAGE(User define)
    8 b/ F1 I: `8 f% x( P% ]" z
  25. 0x09, 0xFF, // USAGE(User define)
    : U/ U5 p- d/ }
  26. 0xa1, 0x01, // COLLECTION (Application)4 J' u8 j& V, H3 k* N* x
  27. 0x05, 0x01, // USAGE_PAGE(1)
    9 p5 ]9 X% D$ ^( n! K
  28. 0x19, 0x00, // USAGE_MINIMUM(0)9 @/ i; `, c; @  p
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)
    ' D5 v3 \8 A+ @
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    3 C- Z2 H9 S* b2 [# U
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    , z! w* C$ G) V  N# m1 ^: z9 ~' E5 N
  32. 0x75, 0x08, // REPORT_SIZE (8)
    6 i4 x' F2 D& D2 f0 ^
  33. 0x95, 0x40, // REPORT_COUNT (64)
    , F' b9 ~- J. k
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)
    5 F5 c' U* q7 f* A( H) [
  35. 0x05, 0x02, // USAGE_PAGE(2)
    . k( X8 {3 o) n. A
  36. 0x19, 0x00, // USAGE_MINIMUM (0)
    " X. q4 O& u: Z2 j- w* k$ t
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)2 N$ F/ [' |2 h7 s8 G6 v3 y7 l
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)4 J7 J% k& \' H
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    ( O8 I9 Q% ?9 r1 x  l
  40. 0x95, 0x08, // REPORT_COUNT (8)
    % F$ t/ K+ d8 s& F$ v# T
  41. 0x75, 0x40, // REPORT_SIZE (64)+ [" i! R, L. E. \) o- m! t
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)
    # ~: U* _: w* r' b2 i6 Y
  43. 0xc0 // END_COLLECTION
    $ O3 g1 n6 X8 D
  44. }; /* ReportDescriptor */
    5 q8 h- k6 [* {7 R: }6 R/ F# N
  45. " p& A4 a6 K  P- P" ~  s2 s5 ^
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]
    + K/ X) i. b% P$ l
  47. const u8 StringProduct[SIZ_STRING_PRODUCT], Y/ ^9 R& Z& v7 p, L  S+ q5 @
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码
* G7 E/ C6 B1 X! ~9 c7 C
分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。/ s. R2 a. W# |  L
, ?8 Q2 R& R* i5 {5 g, \
  1. #define CUSTOMHID_SIZ_REPORT_DESC 395 j& w* Q+ n4 S( [) B  {/ J
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64( n0 y! G9 i% F2 w) c
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28& {+ c8 X% l" L' `3 _( |) ?7 _$ O
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码

) F' Z: Y, [' |/ w/ r* @% t7 z; Q三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数
6 p( T1 q; V1 U9 N( ~a) Set_System(void)
1 z* M" x/ \" P/ Z8 _* L/ Nb) void Set_USBClock(void) 3 U( }0 g) g) l& Y; Z$ _6 j
c) void USB_Interrupts_Config(void)6 K( d: X( B  u$ T  X  ?7 j
d) void USB_Cable_Config (FunctionalState NewState)4 ]0 ^, P; A9 f5 j
特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。; j$ O7 V: B8 z

# G7 w. S5 t1 A* u% p" \# m0 I- z6 n3 N8 o$ V
四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。6 D; N  s/ n' }
打开usb_prop.c文件,修改如下:/ A0 O% J* l* Y  {4 ]. A
  1. void CustomHID_Reset(void)
    5 L+ X( x3 p- Z2 d
  2. {7 g' m) A& i5 k( V. i& |3 R4 A
  3. /* Set Joystick_DEVICE as not configured */
    $ K- V+ r6 ^/ `; L% t1 v
  4. pInformation->Current_Configuration = 0;
    3 Z: K- K- Y) ^
  5. pInformation->Current_Interface = 0;/*the default Interface*/
    , I$ o. {, j. ^- l; N* a, T
  6. SetBTABLE(BTABLE_ADDRESS);7 H" ?! @$ }! }4 i2 E7 m6 p! H: X
  7. 1 z% E& W$ l: |% G0 `7 O
  8. /* Initialize Endpoint 0 */
    3 Y( K6 W. r( R' ^
  9. SetEPType(ENDP0, EP_CONTROL);
    . X2 G2 k  u) |2 U0 x( B# I) L
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);
    6 Z( A6 t# t& S! @
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);+ m5 \8 }( R3 o# X7 Y5 [
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    6 l3 I) R, b5 [9 Z/ I* ]9 S
  13. Clear_Status_Out(ENDP0);
    # \1 V; f+ o# |
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);. E' P3 S4 w9 Z% F2 s
  15. SetEPRxValid(ENDP0);- C3 S5 d5 |3 O" Z& ^, U( y

  16. 4 J0 X! Z( h5 T7 y
  17. /* Initialize Endpoint 1 */4 M' f5 t) H* v1 j# }7 s- h0 {
  18. SetEPType(ENDP1, EP_INTERRUPT);+ I. {% z# l3 r& w  A
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);4 j# K, ^" `4 a2 n+ j
  20. SetEPTxCount(ENDP1, 64);
    7 V% n+ e7 ^+ u% f5 ^  c
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);% L! @* Y4 A8 u/ H, a4 G! h
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);
    + u3 e* a# I5 C1 T
  23. 4 T4 P  |+ j6 q
  24. /* Initialize Endpoint 1 */
    " h4 W1 S. J( q5 k& j$ @, y, T) @
  25. // SetEPType(ENDP1, EP_INTERRUPT);
    " `/ w# c7 R% u1 |+ \: |
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);& v) c: ]6 j, y6 V' [
  27. SetEPRxCount(ENDP1, 64);
    $ B0 [6 |! f, L
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);
    ! q* M: w3 I, u. D1 P$ F  ~4 V
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);
    7 `, f2 Y  T: @+ M
  30. /* Set this device to response on default address */$ u  a7 Q( M! G3 T' s
  31. SetDeviceAddress(0);
    ! {" J% |" _7 n" W: j* A
  32. }
复制代码
: u( u7 o6 j8 b; a& ?4 W
五、usb_endp.c文件
* G3 O: S1 v" T( P6 }. M8 S  T
  1. void EP1_OUT_Callback(void)
    - r, W8 i! t; D6 a% w( s! Q; K
  2. {
    % c6 T# N3 j9 Z* a/ I! x
  3. 这些写接收代码
    0 ?2 u; ~& j( U0 ]; D  d6 P
  4. }
复制代码
( ~) N; N3 u( t3 k* I# t
六、数据发送和接收,举例说明
( f" {4 p8 i- v* r6 D1、数据接收3 @6 K' K+ o( Y& D6 s+ W, Z4 g
  1. u8 DataLen;
    1 L, H# o# ]7 \6 X% P
  2. DataLen = GetEPRxCount(ENDP1);
    ' Y9 t5 a; O5 B) n4 z
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);) ^, Q6 i7 {# A
  4. SetEPRxValid(ENDP1);8 `/ M9 \, j4 [4 c" D( ~
  5. USART1_Send(DataLen);5 e0 Z" {4 D' c# X% G( V- X$ G
  6. count_out = 1;
复制代码

0 S: g' Y' ~; ]2、数据发送
7 `  W; }" E- S/ ?) s8 ~# v$ ^0 b
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
    ' k- A3 b% }4 s2 c! X5 |! d* [
  2. SetEPTxCount(ENDP1, 64); + D" j' c  ?7 X+ w6 y! q
  3. SetEPTxValid(ENDP1);
复制代码

: k  K: b- E! j" b如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。* Q0 h7 q9 X$ e0 u: A- u) k8 ~7 `

2 d, m" G1 x( [4 j
2 |: D$ p1 h& s* a: y& l3 ^8 ~7 w
(by xidongs)$ ]8 R# q5 W0 a! v. X, t8 |
收藏 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 手机版