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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程5 j# i5 p3 C4 k8 O

8 U% M3 G% i, y9 j9 w; r
最全USB HID开发资料,悉心整理一个月,亲自测试
3 P2 h; `- {5 k5 Z- `. I
0 g3 n8 x' a4 ?, L6 @
通过STM32CUbeMX建立USB HID的双向通讯实验成功+ Z: i: C3 b  R8 i: s

0 _8 K* r2 K6 ?7 ~% E

: B8 L$ b' L# n: Z; N发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。

3 R# r8 ~8 M" P+ F% T
# t+ k) S0 A2 ]% J  W7 ^' LHID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在
% q# F. f' |- }
/ @0 z' @6 g5 t! ~; h一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。
; n  \; X/ u8 Z9 E3 x0 q% h+ M, y7 [; o, o. i; O7 w5 k' N
二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。: p" ^0 j9 }9 z/ C8 W
! s+ P1 A5 X: C& C
u8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
* l; B" H% _) e- x0x83, /*idVendor (0x0483)*/
7 j7 Y0 m2 f0 `' x$ E' X  @5 H7 S) Y0x04,
) N5 f, z' Q! y* b9 E0x50, /*idProduct = 0x5750*/
* J9 f$ Q  A/ R- S0x57,
" U3 W+ g$ Z$ |7 j//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。3 ]. n8 ^: Q0 m3 F
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
! w2 c$ k4 h$ U  Q3 P//注意小端模式,低字节在先。
' T5 k1 C# O4 ]! i' X//idProduct字段。产品ID号,我们这里取0x5750。
% t# M( t' n" W& n7 \//注意小端模式,低字节应该在前。' b* D9 L5 a! o( W

- i. q+ h% |6 `9 S. U7 ?1 {/ Y+ |) O
const u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下
/ G) n  ~* ]- ~3 |
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */% o1 \$ G& }4 m, e" H  ^4 L
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */4 t) x1 w* a. [5 B# @8 Z. ~* n
  3. 0x03, /* bmAttributes: Interrupt endpoint */
    3 S* U& c: d& {* u5 f( a  f+ Z6 E
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */
    5 ^! U' v2 {; V
  5. 0x00,
    ) ?1 h6 G" _  [6 ]. y% p0 S# i& y
  6. 0x20, /* bInterval: Polling Interval (32 ms) */
    : a! _3 x8 b' d9 `+ B- D
  7. /* 34 */
    % v: y) _9 }) p/ d6 I# y4 ~  Y
  8. 0x07, /* bLength: Endpoint Descriptor size */
    : a6 B1 z4 w: O/ U
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */: I* E; s" l# z; `% `- l, f1 n
  10. /* Endpoint descriptor type */2 K7 Z0 ~3 Y) J0 {. f! e& `
  11. 0x01, /* bEndpointAddress: */
    ( e7 W9 ]* O/ [; M# u* Q
  12. /* Endpoint Address (OUT) *// S0 a9 \2 i1 O- R9 v- x
  13. 0x03, /* bmAttributes: Interrupt endpoint */, i  f+ j; }( h0 C3 H, o
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */! V7 f, \; |2 p1 K! v9 F5 b
  15. 0x00,3 ]! |  a5 d2 g
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码
# L! d8 z* L# o  {- ~  `3 q
上面包含了“输入端点描述符”和“输出端点描述符”。
  w8 s# ~6 A( I! M2 d/ _//wMaxPacketSize字段。该端点的最大包长。
! g; ?0 Q8 C: ]2 [+ V% C" C2 j" o3 C. b//bInterval字段。端点查询的时间,% @# e0 R" W8 z5 Y8 R" m/ ?1 h5 N, C
/ g; \/ C7 g6 h/ e0 X
为了实现更高速的通信我们修改如下:
! G/ r" z  N+ m9 |$ X: V
  1. /******************** Descriptor of endpoint ********************/4 }% ]: @* b9 _$ M! |8 Z
  2. /* 27 */
    9 }; d( z3 T9 a  c- a
  3. 0x07, /*bLength: Endpoint Descriptor size*/' c4 x5 K) `% `& s" {2 x
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/4 E& U. L1 t" y6 C7 i5 e6 z
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/' P) t$ c" d& X$ g6 L5 @" d, ^8 q
  6. 0x03, /*bmAttributes: Interrupt endpoint*/( x2 |) P/ }) H+ i$ f- q; b
  7. 0x40, /*wMaxPacketSize: 64 Byte max */
    8 \# L, P1 ^  T4 A- t, a
  8. 0x00,
    : u, u% r* P0 \' h% q
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    2 A  W: r4 F& [: m# v8 u
  10. /* 34 */6 m: W: w9 H/ a/ Z: y5 F- h3 Q
  11. /******************** Descriptor of endpoint ********************/
    0 o1 W7 s, D- g: R( j
  12. /* 27 */7 s2 E! ^  F* F2 I$ H; _+ L
  13. 0x07, /*bLength: Endpoint Descriptor size*/6 z  d4 v, [5 b1 N/ N
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    ; W# C2 f. Z0 N2 j- o' d
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/  R+ m, L6 f: y
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    : m! E" r8 g3 l) [
  17. 0x40, /*wMaxPacketSize: 64 Byte max */2 r$ k$ h# P' [# x3 ^8 }$ d" j9 F
  18. 0x00,
    - X' V4 A+ ~$ U7 w% D" h
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    9 F9 P- @: K, x8 |, e

  20. # m( Y. v2 d  W
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。4 H0 ~1 {0 _- V3 T$ F* n& s
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
    , }6 V9 h) v- O1 u/ @$ m
  23. {
    2 v) D9 i# g+ t, Y& d" k4 l
  24. 0x05, 0xFF, // USAGE_PAGE(User define)- X9 B; [* Z  _3 ?0 Q) W. k
  25. 0x09, 0xFF, // USAGE(User define)
    0 H/ }- X$ z% r4 P
  26. 0xa1, 0x01, // COLLECTION (Application)
    , G1 z( F4 J1 O2 g$ n' r
  27. 0x05, 0x01, // USAGE_PAGE(1)
    : e+ H  y4 o) B. p: B* {
  28. 0x19, 0x00, // USAGE_MINIMUM(0)) m8 n5 |6 P" D% J# e4 x4 g
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)* ?+ j) M# @" e% M2 b, _4 D! x5 ?
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    0 z$ i: o* E, d* n# M. D5 o
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    $ D$ [8 ^# a& A
  32. 0x75, 0x08, // REPORT_SIZE (8)
    3 ]( p3 ~2 F% n1 s  }6 @+ N# o
  33. 0x95, 0x40, // REPORT_COUNT (64)& w" M8 g. b3 |. \6 _" ]1 U0 o
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)
    $ d* y- t  @+ e7 x2 j
  35. 0x05, 0x02, // USAGE_PAGE(2)
      G# l/ \3 A  j& D" A& z
  36. 0x19, 0x00, // USAGE_MINIMUM (0)
    0 F! f. P! A" b: [& R! t& p
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255); N/ [! b6 ^, Z3 Y: P% S
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    ! b) T* q9 K0 f% Y' N" V
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    4 H& d7 ~4 J# P3 D( M: ]- ?* Y
  40. 0x95, 0x08, // REPORT_COUNT (8)
    1 ~3 Q! j* |" o  C5 M' Z7 i
  41. 0x75, 0x40, // REPORT_SIZE (64)
    + f0 `0 W# J& z$ ~3 T
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs); Q8 E- @# T" R% g8 j
  43. 0xc0 // END_COLLECTION7 i. T$ B- @- E& G9 j& ~' r* }  q! ~% p
  44. }; /* ReportDescriptor */
    # w0 a5 A- V  }0 J' @: v/ u% z
  45. 0 n5 C/ V/ A0 v6 D! I
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]% q, m! m: z! C
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]
    ; `# a! o, r% o; a0 g; e2 w
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码
2 `4 g' d0 r  T2 @  a( i: }* `
分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。
1 V# c0 g7 G1 `/ g& O! n
; Y7 u  J3 }& @
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39) P! [. p% a9 z; i) b4 C
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 645 S8 y( e7 s4 t# ?4 ^
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28, @* a# C/ m  O3 }8 G3 k
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码

7 I7 ^1 E0 b. n; ?三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数
7 F( l0 F" p/ [7 V+ F( h$ Ma) Set_System(void)
5 t; E: P% v) i: x1 R) Z9 `1 C8 {b) void Set_USBClock(void) 8 T8 |# L& n4 Q  Q' K) z+ H
c) void USB_Interrupts_Config(void)
- x5 [/ P* m6 e. b+ G% jd) void USB_Cable_Config (FunctionalState NewState)4 \( i8 T" ^" I! J' P3 F
特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。1 e3 F6 a" A0 S5 e1 F

5 j$ S( q* S3 l' B0 N! Z' H: y
四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。
. r: C4 O* F( U7 ^打开usb_prop.c文件,修改如下:
+ L& d$ O8 Q7 i) q1 N( R
  1. void CustomHID_Reset(void)
    8 S8 _- b* E6 {& ^4 ]. ]
  2. {
    * P/ t# G2 @* {) M$ c
  3. /* Set Joystick_DEVICE as not configured */) g/ v' U5 O3 o9 N4 C
  4. pInformation->Current_Configuration = 0;
    ! [/ |* B9 A* z7 c% s" U9 Z9 H
  5. pInformation->Current_Interface = 0;/*the default Interface*/# U( d6 `" \6 J+ ^# i! `
  6. SetBTABLE(BTABLE_ADDRESS);! t$ _: N2 m  e8 D, u& f) B% G, e# `

  7. ) O2 o: u4 a' ^1 B
  8. /* Initialize Endpoint 0 */
    ; D3 d; @1 Y. o' [' W
  9. SetEPType(ENDP0, EP_CONTROL);
    3 Z! n. Q& k4 A3 b7 h
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);
    ; F$ O. M, \$ T
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);) ]% ]# c8 `9 k9 _9 j
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    5 `/ M" ~/ Z3 E
  13. Clear_Status_Out(ENDP0);9 D6 {" l4 O0 j' h8 }
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);) z" T6 Z+ |8 w' s: s4 }; a' D0 b
  15. SetEPRxValid(ENDP0);
    / k, b. J6 D2 e* K
  16. $ t/ r- Y: S* x1 r" p7 F. K$ v
  17. /* Initialize Endpoint 1 */
    / P" Q) t  R: q! q% Z  x
  18. SetEPType(ENDP1, EP_INTERRUPT);" t5 Z- U: K1 ]6 \9 F  w: b
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);! n* z3 X- K) Y$ {/ |, X
  20. SetEPTxCount(ENDP1, 64);2 O; |; w) ~* d3 i! S. C
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);9 y2 f* I6 c, ]" J0 m  v
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);
    6 b6 q5 L6 N5 Y

  23. ' [. K# ~8 x% G6 N2 _+ e
  24. /* Initialize Endpoint 1 */
    % H; g0 T1 j( D! b6 C9 _3 e  r' b
  25. // SetEPType(ENDP1, EP_INTERRUPT);
    + A8 {9 i/ Q; ^6 J+ v/ j
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);7 a/ J! E; z& D1 |( X
  27. SetEPRxCount(ENDP1, 64);
    : ?0 p( o. Z5 D. e& B; F6 i" R
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);2 B% S% d: D, {& Q% L2 h) f
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);
    $ e3 |% c( m4 U+ e$ t$ }+ p3 O( P& G
  30. /* Set this device to response on default address */- r  y+ Q" ], Q3 R& D4 p
  31. SetDeviceAddress(0);
    5 y9 X. ]8 c( W; t" v1 S& I& y
  32. }
复制代码
* p6 G9 M; s. e: k" ?  B2 V; Q) c& E
五、usb_endp.c文件
8 a9 h: y3 i0 s- B( W/ m
  1. void EP1_OUT_Callback(void)
    : F& l) k& m$ Q- y' I8 a5 E
  2. {
    $ }$ r! n: e- E2 x$ s+ U3 \! N
  3. 这些写接收代码
    - q* q' s/ P; z) i
  4. }
复制代码
% v" {7 {) m1 @
六、数据发送和接收,举例说明  ~( q7 y9 ]# Q
1、数据接收- B( M' S5 X" o* f' O2 H: Z4 R3 ?7 G: z
  1. u8 DataLen;1 q' e! H. k1 s1 V% z. _( R
  2. DataLen = GetEPRxCount(ENDP1);7 z7 W; R  f8 n8 j
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);& X$ K5 |+ C% y" G3 @: B" ^7 ]3 {6 V9 D
  4. SetEPRxValid(ENDP1);
    ; _: g/ H  T1 N* F7 j
  5. USART1_Send(DataLen);  J& w3 X% w) n  x& M6 A
  6. count_out = 1;
复制代码
( e4 p; V* o& L4 B( `" I
2、数据发送
' l% c2 Q( i* l) T, e
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
    8 z% }. \$ T4 Y4 q
  2. SetEPTxCount(ENDP1, 64);
    3 O9 \0 M- l! K! e5 a
  3. SetEPTxValid(ENDP1);
复制代码
8 ^! l7 A! h# |9 Q' m" z5 J6 z
如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。
9 }0 ]( Y$ W& L5 \# N# P# p7 d3 r% F: o1 I6 |, o

- {( q' A4 K$ D2 d(by xidongs)' k% u- t; b) i  C- F
收藏 24 评论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 手机版