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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程
: y+ {9 M3 K! k; P! h4 P
. X$ R) K7 z$ h: w) ]1 p
最全USB HID开发资料,悉心整理一个月,亲自测试
3 c# G) ~, ^* }$ e
# _6 \% ]! a) @8 Z% g3 X% c' I- p
通过STM32CUbeMX建立USB HID的双向通讯实验成功, ?6 m3 R7 z  X& y- |$ q

+ w4 y. M: F" \9 ~% k
# ~" _6 U& R" \1 [" n" d
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。

5 j4 h: j& Y7 s- e" j$ |* P0 a
6 g3 N! v' U% }2 p( VHID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在1 ]- r) D. M7 y" ]

9 S# }: {0 C2 Q( y2 ~一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。/ ~6 y. M4 M0 O. h& L- J

0 z& t2 l, J) G% p0 ^9 f二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。/ |3 E9 j( A- y/ R" U( s5 V% S8 Q

' h# j: v( V9 i! h: }# Lu8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
7 R% Q$ e% ~3 E8 i. N0x83, /*idVendor (0x0483)*/' e/ L8 A, N" ~" G# f* k' @+ S
0x04,
9 W* @# U2 P, @$ A% J0x50, /*idProduct = 0x5750*/
- o7 _4 G) c0 e% K2 U) c2 w0x57,) |+ n; y$ V4 U  |5 o
//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。& g/ E2 @! c, g  Y' u
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
9 r* e  C9 G+ ^5 ^' I4 L//注意小端模式,低字节在先。
7 S: c/ p, f" W* m& ^1 q$ W//idProduct字段。产品ID号,我们这里取0x5750。8 R1 I4 {) K% m; y3 W, I) t
//注意小端模式,低字节应该在前。
) N  x" ?. t3 m* {$ k. v# }/ N# _( r8 @- ]% P- B
' x8 B+ L* F3 A
const u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下
& u, y; @( G/ g( U( q! _
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    ! M2 }: g8 Q+ P/ \
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */
    3 P" L9 r* @/ G& p( C7 |1 x" _
  3. 0x03, /* bmAttributes: Interrupt endpoint */0 R! L% j) ?6 E0 V; ~# S
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */
    6 j& S: g$ a# ?
  5. 0x00,. v* j) z' h/ ]; ]$ ]" [
  6. 0x20, /* bInterval: Polling Interval (32 ms) */3 _+ {$ K2 [5 a0 i* {8 d
  7. /* 34 */
    . M, _0 i' k0 W# o
  8. 0x07, /* bLength: Endpoint Descriptor size */
    ) d; X* A: c0 e. ~
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */) G" |9 `2 q. p# _. r, i
  10. /* Endpoint descriptor type */
    + g% {% a/ t" P# W; e0 q
  11. 0x01, /* bEndpointAddress: */
    / `0 G2 H( n# S. g
  12. /* Endpoint Address (OUT) */
    ) `& E6 y6 K/ i
  13. 0x03, /* bmAttributes: Interrupt endpoint */
      Q7 u* M( J. Q* K: W. C
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */2 ?* P0 w! Y' N: k5 G7 \
  15. 0x00,, ^, w1 v. [7 I" T" m$ N4 N
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码
5 ?  ^4 W% Q, v+ Y# H
上面包含了“输入端点描述符”和“输出端点描述符”。
9 `4 H+ c" o6 }: C//wMaxPacketSize字段。该端点的最大包长。% c3 ?+ k( M9 c: F
//bInterval字段。端点查询的时间,* O2 \" ]3 N6 z( b7 Q

* s7 \" S& T: N为了实现更高速的通信我们修改如下:7 H) x$ h9 R3 j8 m8 l" K
  1. /******************** Descriptor of endpoint ********************/7 N7 F5 w5 D: O* G( Z8 [0 A
  2. /* 27 */
    " Y1 A/ B( h! L6 |. @. g% K
  3. 0x07, /*bLength: Endpoint Descriptor size*/& a0 {  e+ S; A' l/ X
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    4 a: Q9 A6 T" T2 i
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/2 J* x8 q' f* v1 @0 |# `# f% x: i
  6. 0x03, /*bmAttributes: Interrupt endpoint*/: M$ ^$ }$ o9 a3 O* C
  7. 0x40, /*wMaxPacketSize: 64 Byte max */
    ( v, h- Q' r$ q. |' S# ~
  8. 0x00,
    ' _+ _( ?- V! _
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    ! H$ R3 D- |. B4 j
  10. /* 34 */! c7 J" ~8 b# |5 H. k0 V
  11. /******************** Descriptor of endpoint ********************/
    ) \" N# t+ X" f: D: R/ p. P
  12. /* 27 */
    ( i% u8 P- n7 w: e
  13. 0x07, /*bLength: Endpoint Descriptor size*/- b9 z* R+ Z2 l8 i- z4 N9 @
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    ; d. f8 U" E9 [- f- ~
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*// b2 u) L# z9 E$ h& z
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    2 n7 d; o9 }  j/ |- t
  17. 0x40, /*wMaxPacketSize: 64 Byte max */
    ( E$ t) u+ `- i$ ]! w
  18. 0x00,3 R3 [4 _2 V1 F$ R$ k
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    - C7 u2 u& x3 i6 G6 Y% |
  20. 1 [# x; @1 A. i- Y) A
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。: Y5 O' z5 @( a
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =$ X  v0 E1 [/ v( j: W& R# o1 ~0 Z
  23. {
    8 o- l& J  E- v# `/ s$ i1 N+ B
  24. 0x05, 0xFF, // USAGE_PAGE(User define)7 V) Y' W1 d# D) C
  25. 0x09, 0xFF, // USAGE(User define)
    & X! m! }' g1 f
  26. 0xa1, 0x01, // COLLECTION (Application)& ]% B7 p( j& b# N/ {+ S; ?  x
  27. 0x05, 0x01, // USAGE_PAGE(1)$ _5 @9 q. n" h- D7 v2 X- i, L& T5 m
  28. 0x19, 0x00, // USAGE_MINIMUM(0)
    " r" s" n0 l; W6 y1 N
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)
    & m. L; r& o5 Y/ e
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    - e' F" ]5 Q. k( T8 |+ Y
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    ' ~6 D8 x# |$ y* S' X7 b
  32. 0x75, 0x08, // REPORT_SIZE (8)
    ( `! l& I( E# e  A/ \. w
  33. 0x95, 0x40, // REPORT_COUNT (64)# r& B' z9 t* Z6 J+ v
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)5 h& c& R6 ^9 g  z) c
  35. 0x05, 0x02, // USAGE_PAGE(2)7 p0 q! X. b2 m7 O
  36. 0x19, 0x00, // USAGE_MINIMUM (0)) c. o& ^& v! V# M
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)
    8 u" y. T* t8 U. ~/ w- @
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)2 ^% R1 I$ C; m5 m9 Y6 P( Z
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)" F4 q$ N& \, H3 {: U
  40. 0x95, 0x08, // REPORT_COUNT (8)8 n( Y, H4 C  I  q
  41. 0x75, 0x40, // REPORT_SIZE (64)
    / h, |% d9 d) |
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)+ B# J( r4 o' B% `$ O( \3 I
  43. 0xc0 // END_COLLECTION
    6 X' C* g7 x0 f# g: u
  44. }; /* ReportDescriptor */
    - @7 v; O/ L1 H7 ^) s+ s

  45. % d4 l$ M( q3 I) a
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]
    1 N6 \* y8 `7 Z$ T  V
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]4 L) j8 i5 V" l+ p+ D
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码
% T7 Y, u2 x2 T6 b1 c6 S
分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。
+ Y8 T4 U9 w$ y+ Y# I! c- V' q, U9 C) q) q( S. D! u& u8 z
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39
    3 ?+ W9 c" P3 j- s/ B" A' X
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64" }" v9 c$ t- _" S  m
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 282 f% N) I( s6 O. I
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码

/ x8 d: o: ]- I, e. _2 u" h三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数7 ?' }9 Y6 w$ t5 x( x" N
a) Set_System(void)9 S  z7 |8 j) c/ S; x4 {4 b
b) void Set_USBClock(void) ) Z) q$ K7 l- H2 f9 X4 s
c) void USB_Interrupts_Config(void)% C4 i: o6 p6 J* A9 f5 V% a7 I
d) void USB_Cable_Config (FunctionalState NewState)# B. W2 j9 Y  {7 h
特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。8 m  v6 Z% ?; e4 Z* L1 v8 u+ E

# Z) e! W8 k  E# B7 m8 Y1 ?% {# Q
四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。
$ o: r% `1 n7 l( J0 X7 Y- r! D打开usb_prop.c文件,修改如下:
& r- w1 R  X$ O& w( F8 n5 H
  1. void CustomHID_Reset(void)! \0 X( ]! F9 A' @
  2. {
    2 |; B3 m5 {; X3 L& }& ]+ }% l
  3. /* Set Joystick_DEVICE as not configured */
    " D. n$ }9 O, k/ M& j5 w% F9 m# a
  4. pInformation->Current_Configuration = 0;
    * n5 P/ I9 Q/ n6 \  c' f
  5. pInformation->Current_Interface = 0;/*the default Interface*/
    ; W# v! N# t7 u; e8 \" U, D8 Z
  6. SetBTABLE(BTABLE_ADDRESS);) u6 g7 H/ L) N4 v; e
  7. * m" {- H: v7 w2 [
  8. /* Initialize Endpoint 0 */
    * h7 l( C1 A$ b6 v# E
  9. SetEPType(ENDP0, EP_CONTROL);
    # l' b/ z  ~3 s) b
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);
    8 s2 `/ u9 ~0 t
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);4 s; v- `! q, I  [
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);  ]( G; r( C1 A
  13. Clear_Status_Out(ENDP0);$ M" K; j: K# t. F) a
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
    + ]: N* o& m& G3 _
  15. SetEPRxValid(ENDP0);, T+ J* j! L" z

  16. . }/ ^. p0 ~; n; K2 c: h* l
  17. /* Initialize Endpoint 1 */
    $ P8 }+ Z( r! y$ V& z: v. E/ t: M  a
  18. SetEPType(ENDP1, EP_INTERRUPT);
    2 Z1 _& Z) A4 Q! n
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);
    - w1 [# I6 F4 z& g1 ]% {
  20. SetEPTxCount(ENDP1, 64);
    * I% P0 `' r* o8 u
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);
    ( i* @' G3 |9 f* ^
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);
    4 x' E$ S+ S: q: l7 c/ `) d  i
  23. " {) d+ ]4 _4 {. c5 n4 c
  24. /* Initialize Endpoint 1 */9 P9 \+ H2 t- |
  25. // SetEPType(ENDP1, EP_INTERRUPT);6 C: b2 o( S) z5 k0 c5 Z; r
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);
    7 r1 Z8 C( z) i8 M; n& [
  27. SetEPRxCount(ENDP1, 64);
    1 }, N. j7 o9 f! K9 O9 l
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);$ G! j5 L% i+ J1 \5 K' [7 t
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);
    ! [% S% P- a' `, t6 M' X
  30. /* Set this device to response on default address */
    8 D5 u9 a3 y  x: r3 C9 {2 S" _
  31. SetDeviceAddress(0);
    / P, [! l' m' I/ b0 _
  32. }
复制代码

" v$ f# }2 z( e9 _. A' p五、usb_endp.c文件- a! J8 _! z7 G4 u2 U
  1. void EP1_OUT_Callback(void)
    / A2 n* u7 `4 E. j6 T- `, ]
  2. {
    8 j6 l& m7 N5 g: B, j
  3. 这些写接收代码5 @  s) U  M+ U% {8 ^; ~" i% {
  4. }
复制代码

0 [9 ]' M" d& A4 H7 l( k3 q六、数据发送和接收,举例说明$ n0 m  M# F- a' X! k# v
1、数据接收6 G( j& o8 n. u! R& k4 U& r
  1. u8 DataLen;  p3 Z4 M4 f; e6 q' ?0 A# e
  2. DataLen = GetEPRxCount(ENDP1);7 y3 y) |% _' }, k+ V
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);$ I8 c8 Z/ w8 I
  4. SetEPRxValid(ENDP1);
    ( q: c# p, O8 B2 |& r; m) P3 j# E
  5. USART1_Send(DataLen);
    * H- D! z% M  `$ j% G  H8 w# {% n
  6. count_out = 1;
复制代码
2 d: u/ z7 U; B/ |- L$ b& M
2、数据发送: f$ L- B" @2 R* b" \4 l
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
    6 m& `; R9 f/ ^% x; q
  2. SetEPTxCount(ENDP1, 64); 6 [; s1 r- `: I3 I9 O
  3. SetEPTxValid(ENDP1);
复制代码

' v9 m$ g6 a$ v如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。, {. A$ V! l& d6 V

. g  Y' \# P; T; Z

; d3 \2 a+ l* z. {& I(by xidongs)
3 Z/ A8 O  j* W1 E9 d0 y. 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 手机版