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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程5 ~2 H$ }: A! |5 H/ t
4 y' V# o& O, G  R
最全USB HID开发资料,悉心整理一个月,亲自测试
/ z5 H, o! u# j0 [+ y& |" B* g6 E' ^$ z' p
通过STM32CUbeMX建立USB HID的双向通讯实验成功
. f6 T1 k5 A+ x. V6 M6 M
( a* ]4 |. j9 _

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

! c9 I% t( Q4 @7 u5 i4 ]5 f" m" }1 @5 A6 o8 w! F/ b: b0 p! ~+ S1 Q6 l
HID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在
$ J5 M1 h+ t4 u# X# B+ h* Z) D
0 v$ ]1 a; ]- l, z一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。
2 W4 c8 q- ?& m* @. L) L( b
7 |! V" n% ?8 y9 F8 t* |二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。
. ~! O$ K$ N$ r% f" u" I
7 ~, @* @' b4 V, `2 eu8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的4 L& D$ E- H. }3 [1 E
0x83, /*idVendor (0x0483)*/
: o. g6 U4 ~# X; N3 Z* B* v! s, z0x04,3 }9 u) o* r, A
0x50, /*idProduct = 0x5750*/
- J8 A, d; M+ S; W8 _, f- J2 H0x57,; N' `: \, q3 ]0 |
//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。: ^2 ?: v- ^2 ]4 E* n3 B
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
( d! r0 \' I; i: Y. o* A//注意小端模式,低字节在先。
" i8 F- c. }% r3 @0 ^& }//idProduct字段。产品ID号,我们这里取0x5750。
9 n6 H# Z1 N0 p$ F! m7 x//注意小端模式,低字节应该在前。
, K% r+ E; L, ~# p/ t' R! R9 f4 S  R4 P  r
; n2 [' s% S9 _1 C
const u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下
( f( S- c! D5 n7 z) w; W* m
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */) R& J0 o* y, Y1 T
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */
    7 W6 V5 Q6 N6 r8 \
  3. 0x03, /* bmAttributes: Interrupt endpoint */
    9 X/ T# u4 R/ v3 a4 m3 R0 Y
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */* q0 O" E2 w7 a+ T) s+ T$ W% p" D
  5. 0x00,% p4 Q; I. ~4 E, y% y0 |
  6. 0x20, /* bInterval: Polling Interval (32 ms) */
    , F2 c; d3 P6 g: A) s3 t2 k4 b
  7. /* 34 */2 c/ V% h8 j% N
  8. 0x07, /* bLength: Endpoint Descriptor size */+ |  t! x/ X( y6 C/ J3 d0 k
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */8 x& }2 q2 W$ Q5 a9 u* k
  10. /* Endpoint descriptor type */5 V5 @' g$ v' O4 M5 H  f
  11. 0x01, /* bEndpointAddress: */
    7 \/ B- L  m8 e( |# D
  12. /* Endpoint Address (OUT) */: V( F2 R  I. Y( @: {
  13. 0x03, /* bmAttributes: Interrupt endpoint */
    * ?7 k9 |( |( H. I7 B% o9 ?
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */
    1 ]! ~, d$ u& J- ^; |
  15. 0x00,8 P4 Z. c+ q- c8 ]7 p; [
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码
* u) r3 }3 C4 h0 R& M
上面包含了“输入端点描述符”和“输出端点描述符”。
+ S4 K1 Q/ X5 W; g0 [2 l  \//wMaxPacketSize字段。该端点的最大包长。
# _# N7 g+ Q0 ^" i, U//bInterval字段。端点查询的时间,* n0 y8 V+ Q# v4 n3 v

  B0 M3 Q& `$ I  l. _( x0 l为了实现更高速的通信我们修改如下:9 e7 a4 M# R) X2 E0 p  O& s2 C
  1. /******************** Descriptor of endpoint ********************/
    $ j& V% N. e- X
  2. /* 27 */
    ; Z  ]: X: J' O6 ~8 n3 ]" I
  3. 0x07, /*bLength: Endpoint Descriptor size*/' S( v$ v! c' }5 S( a( c& m  v/ a' N
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    # d/ U! l  I/ C' R
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/
    " }& k# _  c. P% O0 \6 F
  6. 0x03, /*bmAttributes: Interrupt endpoint*/9 U; h; X1 g5 @! H( e7 B
  7. 0x40, /*wMaxPacketSize: 64 Byte max */
    , ~( n* Z" n7 L# _+ a2 t
  8. 0x00,
    ; x3 I) t/ x4 W+ z6 e
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    : P- S  x. b7 d" \1 ?  a: m& O% u
  10. /* 34 */
    + H  t  z2 n. C3 [0 U
  11. /******************** Descriptor of endpoint ********************/
    0 t, y3 X& O/ l4 o/ N; ?
  12. /* 27 */
    7 U. z# B  t) @( I
  13. 0x07, /*bLength: Endpoint Descriptor size*/
      p& ]# \( m) A3 j9 \5 F; F/ N5 Z9 K7 u
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    + B0 N% Z/ K% [, N& a6 o
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/
    - n( n: h( d0 J4 U7 G
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    / ?& U/ T' a8 f0 L# ~/ u! B
  17. 0x40, /*wMaxPacketSize: 64 Byte max */# i; D6 c% ]& q( `5 g( t4 I
  18. 0x00,3 Z4 Z2 L0 ?3 s% z5 g' \/ v1 b
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/$ A% h7 f4 T3 y0 a9 q6 |

  20. 2 z; w% q) g" g5 r$ [
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。6 i3 C( h" a7 ~* Z4 M" f
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
    / E, }4 i, q% C6 q; Q& M( z
  23. {
    0 X6 i& x6 g2 X
  24. 0x05, 0xFF, // USAGE_PAGE(User define)
    " ], q" x& w+ B( ]& b( W
  25. 0x09, 0xFF, // USAGE(User define)
    / N& K2 d; t7 `7 d8 x% H+ p' h
  26. 0xa1, 0x01, // COLLECTION (Application)) }4 o; N- M) c8 h4 O/ \; N5 _
  27. 0x05, 0x01, // USAGE_PAGE(1)' {' E( x2 A: U2 t- n8 s; m
  28. 0x19, 0x00, // USAGE_MINIMUM(0)
    3 j- F+ J: Y* v/ i/ t: N& n5 x. |8 P
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)  q; N  Y' H# I3 M5 e
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)1 B9 F5 y. P+ A7 g  v
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)* U; L9 c4 ]3 }1 j' n- {
  32. 0x75, 0x08, // REPORT_SIZE (8)- W! y- E) U9 |! j( r1 ?6 y
  33. 0x95, 0x40, // REPORT_COUNT (64)
    ) W6 n$ t" w* A: b, A5 H3 d
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)$ Z1 K2 F6 K$ V6 Y: C
  35. 0x05, 0x02, // USAGE_PAGE(2)' e1 e; ~& B6 P7 G+ b! J
  36. 0x19, 0x00, // USAGE_MINIMUM (0)
    7 d( s, h/ @( z0 b
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)! j' J3 Q8 f2 \3 Z4 ^8 B
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)9 s" T/ i% Z* x; I3 R
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)9 Y& u" _+ |2 g
  40. 0x95, 0x08, // REPORT_COUNT (8)8 a4 ^. s  P2 C( J5 ?, W
  41. 0x75, 0x40, // REPORT_SIZE (64)- e, F3 l- n( V2 Y( V7 E
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)3 f! \& V. X9 e: U5 `. G
  43. 0xc0 // END_COLLECTION
    + ~) _# x7 {% w' N$ k9 U+ |
  44. }; /* ReportDescriptor */2 J1 D' b! k2 B, c, Z

  45. 6 Z, f" I% p0 a3 x9 L5 r6 w8 Y
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]
    & Z* y" _  J* L3 _  E
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]' P0 I9 F" F, {& V+ U+ e
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码
0 A& P+ t& s2 T& V( {
分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。( ~  o7 b# L3 j1 ^" N/ C  u
. @& p. W9 q0 t
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39' l* P# E: G' j9 B/ B
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64
    9 ?" u1 G5 e- i2 W" ]
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28
    2 u. j6 p1 p7 }9 N0 v
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码

% d/ W% ^% b$ V0 Z) w. v" J  Y三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数1 m/ t- |( L. e! m
a) Set_System(void)
3 _$ {7 X+ T: H; k$ O  @: d5 e) ^b) void Set_USBClock(void)
  Y& @5 Y' e# p( p1 _# Qc) void USB_Interrupts_Config(void)
: r5 ]3 S6 T3 ]: A  k; Pd) void USB_Cable_Config (FunctionalState NewState)
+ o; e* m- c/ Q; }- A' Y) h0 A/ ~# v特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。
. q8 |' @- k- I
* `- j' b7 E$ Y: ~: C
+ Y- p; b6 I' m. Y# c( J四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。8 k" s1 C1 Z+ O& x9 C; O
打开usb_prop.c文件,修改如下:, x$ v4 q. J0 s- G; `
  1. void CustomHID_Reset(void); U" G2 P3 |" S
  2. {: C7 T. W3 O* ?& y6 _3 i- K. W
  3. /* Set Joystick_DEVICE as not configured */
    , X8 Y! F: _( c$ Y! l9 Q/ [
  4. pInformation->Current_Configuration = 0;" h3 u: h9 Z( h& W. t7 |
  5. pInformation->Current_Interface = 0;/*the default Interface*/, V, D, l4 d0 U4 p# Z
  6. SetBTABLE(BTABLE_ADDRESS);- _7 z" }) Q- g

  7. 9 t# @% V: v) V$ I/ |1 f% B
  8. /* Initialize Endpoint 0 */4 ~/ e, g5 w$ b9 `0 s
  9. SetEPType(ENDP0, EP_CONTROL);- x' z$ L! K! {
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);& \& W( d1 c% C
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);
    $ V7 h8 a! k9 {# O5 ~& ?% J
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
      G5 c  R: E3 ~8 a1 i( q
  13. Clear_Status_Out(ENDP0);
    1 I$ g. `0 {& Z3 t9 G- l7 w
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
    * y' f  l( q' E
  15. SetEPRxValid(ENDP0);
    ; o0 G& z* Q0 g: C
  16. - ?9 N! F( F6 ?! W4 Y
  17. /* Initialize Endpoint 1 */7 f# M5 w& S6 n/ _/ e# y9 ]- d
  18. SetEPType(ENDP1, EP_INTERRUPT);; e0 h+ }2 Y7 D$ F7 l8 }
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);9 n' n) |2 |. J8 `
  20. SetEPTxCount(ENDP1, 64);8 U& H, J' y- v$ J
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);6 @0 j+ H9 B0 }1 y% N
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);
    8 a& W8 ^$ Y2 K  S1 c4 _% p6 q  ^" B  v

  23. 1 k( s7 j/ B7 ?' ~9 r/ O+ O. e
  24. /* Initialize Endpoint 1 */  L3 T0 b1 d. [; b% i' c
  25. // SetEPType(ENDP1, EP_INTERRUPT);9 x( O' q0 ^) W/ e, h7 e0 a5 K
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);
    8 r% ~4 s/ {- Q
  27. SetEPRxCount(ENDP1, 64);6 s; T' E! |& M
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);
    $ x. k) _2 s5 K: m8 Z) `
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);
    " ?4 }( g( o; f- h
  30. /* Set this device to response on default address */
    * v8 ?# H- m2 X- `0 R6 o
  31. SetDeviceAddress(0);# a7 }) b" U/ z/ S5 `3 C2 t0 P
  32. }
复制代码
0 Q% S7 Y; \. X1 X) M
五、usb_endp.c文件: m5 j. N! g8 \8 i: i* o
  1. void EP1_OUT_Callback(void)
      H5 o- ]6 X" g9 O
  2. {
    4 }% f1 ~( C& X5 v8 |- D8 f. P9 t
  3. 这些写接收代码
    , x" N0 V$ b8 z1 O2 @8 I
  4. }
复制代码
9 S5 g* V8 ]. _, }0 q+ m( M
六、数据发送和接收,举例说明: S6 i  P$ W# S* d7 p, t
1、数据接收
- \* Y1 Y. C: N4 x& A" w8 J
  1. u8 DataLen;, L' X1 C" k9 c0 w+ o+ x# b
  2. DataLen = GetEPRxCount(ENDP1);+ g6 w' x7 g) |" g* D$ Z. T8 P
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);
    " ~. i1 x& _  k+ F9 {2 E% e
  4. SetEPRxValid(ENDP1);4 k& J% c2 B0 A. D; N
  5. USART1_Send(DataLen);
    : P9 p6 O& {1 Y' r2 z
  6. count_out = 1;
复制代码
# }9 a7 x- a, h& `
2、数据发送  ^' M5 N+ |- {2 }# r) ]( B
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);6 I% J  W) k- M
  2. SetEPTxCount(ENDP1, 64);
    1 o; y; m9 O4 p/ i
  3. SetEPTxValid(ENDP1);
复制代码

9 ~2 i' F; g8 U/ E如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。
8 \+ a: o% D. ]% k! ~7 Q$ k. x) K! ~1 L9 v5 F1 p
; R7 ^7 {# ?5 N8 r3 \) G/ p
(by xidongs)
, O  V8 q( h4 g0 Y1 m; a
收藏 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 手机版