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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程+ x* ?* g, D4 l0 C

$ ?" k. s- O- P  Z4 D- s2 p$ j
最全USB HID开发资料,悉心整理一个月,亲自测试4 M9 g# @2 f; D4 R
- L& T# x/ L* w2 l+ [0 k+ }
通过STM32CUbeMX建立USB HID的双向通讯实验成功
/ D5 `9 y* y+ u2 n" l$ W, h7 S% A

) K& ?& B/ P+ o发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。
9 v6 s$ M( ~0 K* `8 f4 I

7 l' E% f# p" u1 U- z" P# n, oHID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在0 N+ c4 U9 s- w% }- p2 b( ]

% F9 U4 s* I0 J9 F- L一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。, i. I$ ?  E% Z1 {, Q3 C

. H3 x5 T: n0 W6 [& F+ p. @二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。
( u; }: ^! O! k6 L
0 W2 U& u6 s7 N. bu8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
: Y. h6 h! g2 r9 O7 P0x83, /*idVendor (0x0483)*/2 O/ ?/ [2 T  s/ X+ @
0x04,
* A0 b1 B7 M8 ?; h3 i0x50, /*idProduct = 0x5750*/
8 i" h+ A( s3 r+ d0x57,1 _: q, D: M0 _, M: V; D
//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。
. y; x4 r" V0 k( f3 }8 ~$ _  o//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。7 f1 n8 }$ }: l$ {# e6 T% S( a7 |
//注意小端模式,低字节在先。- \- |# s0 h. C; g& C' G/ z% C
//idProduct字段。产品ID号,我们这里取0x5750。) ^& l! a" Q( E" b  G7 i+ ]1 o9 r* d0 F
//注意小端模式,低字节应该在前。
& F! {8 x& K) f5 W6 S- p8 m; Q8 e
9 [+ I) i# B+ X" Q! H  K( {+ u- g  U8 _1 ^6 P- w5 R
const u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下& u" ^, }/ V( n6 P3 o; ^
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    . d8 [4 \6 U# b; \6 E; x
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */! O; \1 \6 C$ w: U# E& D8 ]5 d* X
  3. 0x03, /* bmAttributes: Interrupt endpoint */$ G; C+ ]! B/ n, @' X, Q
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */+ Q$ b8 |2 U8 ^) d2 ]8 Z! I$ r1 K
  5. 0x00,. x  D; ^; A2 Z
  6. 0x20, /* bInterval: Polling Interval (32 ms) */
    1 S% W# N; l6 J, p) [
  7. /* 34 */6 L& u. T6 }" C7 K$ F2 m3 m
  8. 0x07, /* bLength: Endpoint Descriptor size */7 C' s# Y) P3 f" y& Y% g
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: *// @4 B' w' J- m. O
  10. /* Endpoint descriptor type */
    3 Q, J( f& f9 ^0 i9 c  A2 O' k7 R3 m
  11. 0x01, /* bEndpointAddress: */
    - m4 R0 F7 M/ u) F  P
  12. /* Endpoint Address (OUT) */, M1 w& M; f* f6 u% A
  13. 0x03, /* bmAttributes: Interrupt endpoint */
    2 D: z8 q* e$ u/ @2 ?
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */
    ) {# B( n/ y1 [+ h% \1 y7 B7 p
  15. 0x00,$ \' ?0 m! w- K
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码

# o7 R, ^/ M1 ~" W& X$ Z, r上面包含了“输入端点描述符”和“输出端点描述符”。
( Q  m' w$ i  ~4 Q! e* @1 e//wMaxPacketSize字段。该端点的最大包长。" l6 `2 M9 m1 H$ e
//bInterval字段。端点查询的时间,
) M* j  ?; h" Y4 Q  s
8 p2 b* |* @' T" {% `8 ]  `1 q; s为了实现更高速的通信我们修改如下:6 u: y: S% \) D
  1. /******************** Descriptor of endpoint ********************/
    ! {9 H( ~  U# r. }; O- e
  2. /* 27 */5 v( K" _- [$ |5 }2 b6 o9 K+ _+ J
  3. 0x07, /*bLength: Endpoint Descriptor size*/
    1 |% |4 s% ?6 o# o3 `
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/- g6 M' }0 j  ?
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/
    * d( j1 y. V  j6 y# }& @; E
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    4 Q" V( _9 ~* Y& p& ^+ n
  7. 0x40, /*wMaxPacketSize: 64 Byte max */
      c' Z& m3 `" e- R
  8. 0x00,3 v- e5 k2 B: f
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/! @! _3 w2 ]* F
  10. /* 34 */' H8 B* `1 t4 ~6 K
  11. /******************** Descriptor of endpoint ********************/
    + D, Q/ b0 a, f9 R. Y! D
  12. /* 27 */
      y1 S0 E  g* f# x4 n6 t" p
  13. 0x07, /*bLength: Endpoint Descriptor size*// q6 s' Q3 O9 [' Q0 x' W. b( p' a
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/: Q5 J- o2 N4 ]/ ?8 @9 o
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/5 y& ~7 {. e8 J$ W; t+ o/ Q+ `
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    8 a/ K! r7 e3 U0 `5 B8 ^
  17. 0x40, /*wMaxPacketSize: 64 Byte max */: g( N1 B7 l; F8 a
  18. 0x00,% p6 J5 ^8 V2 Q" F" Z0 r
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/  t* H' p  d$ W% T) C3 I# Q3 p: a
  20. % C7 N+ }3 F% B/ M, o2 q, H
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。2 ?6 F- _5 B7 c* a. I3 ]! T# ~' B+ s+ _
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =( a( z8 B2 q8 I0 a+ @
  23. {
    . g) A6 A4 d* t& }- n) I4 V8 w
  24. 0x05, 0xFF, // USAGE_PAGE(User define)+ ]& s* ~2 ?: e0 Z+ U$ _4 r" D
  25. 0x09, 0xFF, // USAGE(User define). U' N6 i! O( w& C, I% c4 B% \5 g
  26. 0xa1, 0x01, // COLLECTION (Application)2 I) b5 T% ]0 n4 q
  27. 0x05, 0x01, // USAGE_PAGE(1)
    3 a1 D2 G9 C. a1 Z( V4 N
  28. 0x19, 0x00, // USAGE_MINIMUM(0)
    + q1 K% f0 x6 _, X& a9 A3 S
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255), B4 M1 X3 b) N" d( D! V
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)' r* ]/ I; E  [. x5 Q: c: O
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
      \0 d3 q# _9 i/ |1 y( i4 @
  32. 0x75, 0x08, // REPORT_SIZE (8)/ G  y4 z4 J8 T+ k
  33. 0x95, 0x40, // REPORT_COUNT (64)
    . r; D0 Z& [- r  q3 \+ B2 {
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)1 f- g) |9 O" @- q# s3 N
  35. 0x05, 0x02, // USAGE_PAGE(2)1 R" ^4 U% W5 J' E) O& g
  36. 0x19, 0x00, // USAGE_MINIMUM (0)
    3 p6 w/ I$ a/ B. w
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)$ ~7 c) I* p# u
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    ' J" S0 v6 D9 H& Z3 c6 R
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)$ @. V0 W: S+ e
  40. 0x95, 0x08, // REPORT_COUNT (8)( d% M  w$ a5 e4 N7 a/ C: V, C
  41. 0x75, 0x40, // REPORT_SIZE (64)
    ! i- X7 [# F. w' M2 C
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs); ?% e, b; O' J& l, k
  43. 0xc0 // END_COLLECTION
    # P6 _' a0 i2 t  ?
  44. }; /* ReportDescriptor */7 }6 n" D8 s; ?

  45. ; _1 `3 i' X* x' }" @5 M+ u* K$ y
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]8 z" P- P5 \4 h4 d% U/ W+ t
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]
    4 w8 Z7 q1 v- \& y, S1 z& \
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码

( ~0 U9 Z3 I  t  r% K/ V- I- p3 [分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。/ e4 [5 E1 R( Z

/ S; A( {/ H* z1 l. T
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39
    ) j3 E4 [+ Q& K; j
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64: p2 _1 l( U- a/ ~4 g9 I
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28
    $ z1 y2 K7 Q! k% N3 U! r* L  b4 v
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码
' o) ~* @  n% ~" q$ }5 }5 d
三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数
" S! j. `9 w4 V+ n0 E4 Ta) Set_System(void)0 W7 Z5 E4 t. O4 W2 r+ _
b) void Set_USBClock(void)
$ b1 p* q" @7 g4 N: j+ Kc) void USB_Interrupts_Config(void)
0 g& {8 J. K) X  Y# T5 l; g& d& ~d) void USB_Cable_Config (FunctionalState NewState)* m6 {# E& x" h* f
特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。# u. t8 z  _, g

5 ~$ [. L8 H0 U7 ]1 i6 z4 I1 A6 m! y  @! f$ c% J0 k* o5 f3 T
四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。
$ B8 s. ~6 N4 }9 f9 q打开usb_prop.c文件,修改如下:/ \) X+ j* E, _# `) U
  1. void CustomHID_Reset(void)3 a5 t0 x3 o; A& f8 \2 a! `
  2. {
    % a! O+ F- ]: ~- _6 _( z
  3. /* Set Joystick_DEVICE as not configured */
    + p) ^( K, n& {8 J: ?7 k% x
  4. pInformation->Current_Configuration = 0;
    : q, ^: ^3 U& f' o8 X9 s( O% Y
  5. pInformation->Current_Interface = 0;/*the default Interface*/
    9 k' t' V+ J! |$ e5 x7 e
  6. SetBTABLE(BTABLE_ADDRESS);
    ; r$ ^$ r) ?# y& O! ~
  7. 9 R: a9 o& n) u* j4 U' Z3 ?
  8. /* Initialize Endpoint 0 */. }; V7 p# i  Q9 \2 U; Q4 f
  9. SetEPType(ENDP0, EP_CONTROL);
    ; i2 `& L5 ~* P: t- Q0 e
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);/ v' J" [% Q2 j" W
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);
    4 V: W2 M0 z- q( _3 b
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    % q% @3 z5 T& @# Z8 O9 r1 H
  13. Clear_Status_Out(ENDP0);. I! l0 r& p  ]6 ]& A' {
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
    . q/ o4 l' c9 F2 |$ ]: b8 K
  15. SetEPRxValid(ENDP0);
    5 {# P  I7 D5 E1 e

  16. . z) y1 q. u: J' ]
  17. /* Initialize Endpoint 1 */
    - K5 E3 H& p8 ^
  18. SetEPType(ENDP1, EP_INTERRUPT);0 e  ^+ j' C9 s, v
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);
    5 F0 _9 d2 L7 a/ ]3 Q+ Q
  20. SetEPTxCount(ENDP1, 64);
    : w- O7 p; I1 r6 k1 d0 G$ [
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);
    ) P, I" ?1 _0 C5 n3 k2 F
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);
    4 c# s. u( \6 U  b3 o, q: r

  23. 2 F7 L- u. u: h" ~" D; U) |4 B
  24. /* Initialize Endpoint 1 */
    # x- U6 G. }2 r8 X0 N6 Q
  25. // SetEPType(ENDP1, EP_INTERRUPT);
    3 ?& `! e, J# V5 R8 T+ L3 y
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);: t) D& q) D" _# K
  27. SetEPRxCount(ENDP1, 64);
    0 ?0 x4 `7 T" ~2 K
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);
    " l: Z/ I; J2 o/ \2 ]
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);
    6 m  ~3 X* w* v( c% _
  30. /* Set this device to response on default address */
    9 `% g" R$ ~; G; D
  31. SetDeviceAddress(0);0 R" D- P5 o5 p2 {7 J: o) [
  32. }
复制代码

9 i) X5 u* M& N% w# E; [五、usb_endp.c文件
6 T- Q5 ?. }% ^. M" `
  1. void EP1_OUT_Callback(void)
    / m3 H( H8 B+ n% }7 T3 v
  2. {
    $ w. a5 z' x9 [: j% j
  3. 这些写接收代码$ q: o' x) e( ?, n
  4. }
复制代码

$ `/ ~; G0 I0 O# a# b六、数据发送和接收,举例说明5 n- _7 x' v5 t3 I. |7 \' B! `
1、数据接收
5 w  b+ p" R' o; {$ |. J
  1. u8 DataLen;
    + S7 f+ y: O/ ^0 X+ ~  C# l
  2. DataLen = GetEPRxCount(ENDP1);0 z& L0 W9 Y5 ^: ?3 {3 C9 H
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);
    - A# Z3 P5 Y$ z$ _4 O, d
  4. SetEPRxValid(ENDP1);
    ) [9 J9 ?5 c4 F
  5. USART1_Send(DataLen);" o5 N3 T8 q/ L# f" W
  6. count_out = 1;
复制代码

0 S, z8 b8 W0 P* V2、数据发送
& U$ v  D% u9 i
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
    2 b& X+ f& z5 e( l: K% I" ~
  2. SetEPTxCount(ENDP1, 64); * X) b0 ~$ z7 ^+ p
  3. SetEPTxValid(ENDP1);
复制代码

6 l% @: `5 ]- h1 K' \) u1 W, H7 f如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。7 T- ~. o1 W8 K; u6 C' i
0 E$ R4 {4 H9 \* H% r
( L4 X: s! s; ]' Z6 ?
(by xidongs)
8 S) X. O+ t: z) G
收藏 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 手机版