请选择 进入手机版 | 继续访问电脑版

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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程
4 R/ E9 ~. l1 f' c7 _
/ t7 r& j' a' S+ G4 l; P% }
最全USB HID开发资料,悉心整理一个月,亲自测试& Z# j5 x4 q3 W7 F% `
" c: w& S. s7 M6 L
通过STM32CUbeMX建立USB HID的双向通讯实验成功: L+ J" g# V! G5 I5 g; @9 K5 U+ X3 P
/ G0 a+ |- p. H7 h; ?% C
7 W* V$ F- {% m
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。

3 [( A( K6 b6 @% R; ^2 l' [) Q; M" m, q. V  @0 k# I
HID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在
! j  g! W: [& G9 }. A5 o, h+ ], y8 J8 G: Q
一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。+ H4 N5 U7 ~: B+ [/ ?

& D; W( {$ f* j0 R二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。+ W8 f0 L: M4 D. s

# T4 ^% \. @. e6 Bu8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
5 Z& q1 P1 P8 S$ g0x83, /*idVendor (0x0483)*/
7 s5 D+ o) N$ z2 i* E0x04,7 \, E5 q' g4 s' q; z2 \7 F) ]
0x50, /*idProduct = 0x5750*/+ f$ ]4 d) s& j' Q
0x57,  h# t6 k* H3 f
//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。' o+ [4 T, F/ F' S! t* m
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
# P4 z3 F5 {% q+ X//注意小端模式,低字节在先。$ j# `# N7 _- w# E8 F; J7 E6 p, c
//idProduct字段。产品ID号,我们这里取0x5750。6 g+ w2 g* m) P2 J! ~( d
//注意小端模式,低字节应该在前。
/ ]" k3 Y4 }1 M* k+ S2 J! |2 q0 Z- c. K6 u: @: D2 i

& h4 |* ]6 c5 ?8 z; Oconst u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下* i% f. A0 V$ Q: Y% z
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    # _# @* ]! W7 D/ N8 h+ C$ \
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */
    $ N# Q' o- I( a2 b; [
  3. 0x03, /* bmAttributes: Interrupt endpoint */" V. m1 O+ ]) h7 S
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */" Q9 z  I! Q- x/ C+ x
  5. 0x00,
    ; I; j. M/ L6 v; x1 Y1 Q7 B0 d7 Y
  6. 0x20, /* bInterval: Polling Interval (32 ms) */4 ?( @2 L5 M4 H9 w- R
  7. /* 34 */& E' U7 _  G$ `& _& p' K
  8. 0x07, /* bLength: Endpoint Descriptor size */! k- Y4 c1 W: P( P0 Y! K
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    4 e! V8 w6 z& H3 `/ l* P
  10. /* Endpoint descriptor type */
    # H% \5 J* }: H9 e+ P
  11. 0x01, /* bEndpointAddress: */5 @$ m, [! z- H0 D
  12. /* Endpoint Address (OUT) */" @* S8 S+ w2 ]# A' D8 j! `: `3 [
  13. 0x03, /* bmAttributes: Interrupt endpoint */
    " Y- G  x% f3 {* N/ D$ j- W
  14. 0x02, /* wMaxPacketSize: 2 Bytes max *// W; z3 \5 g% N+ v4 T5 d
  15. 0x00,
    8 i2 s7 ~$ I2 j# Z
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码

% G7 F1 I+ U: w' S* U5 V8 j; z9 W上面包含了“输入端点描述符”和“输出端点描述符”。. e5 t% L7 ~9 B9 z
//wMaxPacketSize字段。该端点的最大包长。6 D- J# O7 X6 u0 B0 n0 q/ ?$ o
//bInterval字段。端点查询的时间,# b+ B3 P& \, {6 L. O# N9 |- y
6 [8 R  |+ |! M# H8 B8 r
为了实现更高速的通信我们修改如下:
) a' u5 _! }, N7 M5 P2 R$ j1 M# A
  1. /******************** Descriptor of endpoint ********************/
    $ B4 d' g4 f: }9 `7 A  Z0 C0 N
  2. /* 27 */
    8 R$ `) g3 `& H
  3. 0x07, /*bLength: Endpoint Descriptor size*/; E7 K7 \- v: U* C
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/' d; Z/ [; {+ [0 _9 z6 G: r/ M
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/
    3 o& `- x+ ~" \. c* }0 w6 h
  6. 0x03, /*bmAttributes: Interrupt endpoint*/: {2 s, f' ~3 B+ j
  7. 0x40, /*wMaxPacketSize: 64 Byte max */( z4 Z5 Q2 o* e6 j7 x( Y
  8. 0x00,
    ( f; y6 r+ D# o4 T
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/& z/ Q7 v5 f- A" ]$ C( ?
  10. /* 34 */  C( b3 X2 }- T( }) r9 b
  11. /******************** Descriptor of endpoint ********************/! m# D2 z1 _4 `5 n
  12. /* 27 */8 {. T) X  _+ B. {) b6 z- H; ~( m
  13. 0x07, /*bLength: Endpoint Descriptor size*/- i/ g& ^" w9 z5 Z8 S/ {
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/3 m0 e% {1 V3 o( |1 o6 h% [
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/+ b. W: }1 {1 Z, j, q. J: A
  16. 0x03, /*bmAttributes: Interrupt endpoint*/- S5 Z/ g6 z# J
  17. 0x40, /*wMaxPacketSize: 64 Byte max */
    2 I/ V1 z* A2 n7 {
  18. 0x00,
    ! X4 v9 H4 I0 b5 I9 w! W3 S9 d) x
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    2 g. J; M+ e' R+ ^1 h

  20. ) L# I6 t, T9 G) z/ f
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。
    , J! D8 B4 W( {6 {- Z
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
    2 s! I$ }4 m+ C" R  j" k( U( G* n
  23. {0 s0 S- j2 b: Z% h
  24. 0x05, 0xFF, // USAGE_PAGE(User define)$ k! U% D8 N8 a7 \# E7 L! p
  25. 0x09, 0xFF, // USAGE(User define)
    * o# N0 ^- D% A9 z+ e
  26. 0xa1, 0x01, // COLLECTION (Application)
      D7 r+ V: o0 D; O& f5 t) A
  27. 0x05, 0x01, // USAGE_PAGE(1): H/ n# @% E5 h
  28. 0x19, 0x00, // USAGE_MINIMUM(0)2 v3 O2 b; t+ k  x, J5 G1 S; L( Y
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)/ a( D$ G$ ~8 }6 e: k! N
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)+ Y. P4 a" m- z; I, C
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    # z4 a+ g$ `: D( W) f
  32. 0x75, 0x08, // REPORT_SIZE (8)* U3 o; U* ]& Y  D
  33. 0x95, 0x40, // REPORT_COUNT (64)6 G# r# N" T. \
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)6 m  ?8 N$ F8 W6 r/ j3 _
  35. 0x05, 0x02, // USAGE_PAGE(2)
    . S# I) t4 n0 q( f5 Q. A
  36. 0x19, 0x00, // USAGE_MINIMUM (0)
      ~! E0 ~5 u' C  Z& C& K3 B* _$ A
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)2 t3 L+ t" e; x" T/ r% g; s, {
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    1 ^' y; W0 E4 F; [5 B
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    3 C3 Z$ p" G) ~' {8 U
  40. 0x95, 0x08, // REPORT_COUNT (8): I( L  S3 u) Q4 o% ?" H8 m: _/ N
  41. 0x75, 0x40, // REPORT_SIZE (64)' w' c. D7 T2 |* l. g. n
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)
    # }* e- U. W, C+ u$ F- h/ w
  43. 0xc0 // END_COLLECTION; i5 S# P1 c2 f7 g2 s
  44. }; /* ReportDescriptor */. {, w7 O" v! V9 G% M5 Z0 }% X
  45. % z6 ~/ g, l% j9 R
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]6 V4 {6 @# _3 _$ ?6 I7 p" ]
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]5 j+ C+ P) v! e* I" W
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码

% [' v6 P1 r3 O3 ]* Z分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。
) ^' |9 I5 t7 i$ D) G/ g9 O9 g0 h! P3 C9 f8 V, h  z, l8 v5 K
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39- p* Y0 z% H2 Z4 n
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 642 P/ b" [9 ?+ T5 ?; A' D9 k
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28* E* D/ S. c& T- t
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码

. n* H6 Z% @% L三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数* p8 C8 P' V4 s! k) i- Q4 g
a) Set_System(void). k* {! O5 I5 o6 L8 k) \2 A! \
b) void Set_USBClock(void) # z) y* C- E( U! ~
c) void USB_Interrupts_Config(void)
: @8 X7 r% B0 v& [4 r8 ^d) void USB_Cable_Config (FunctionalState NewState)
( Z% U# D8 s) u特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。- N  o- P0 h. r: C7 R% K( l
3 N* O: u6 |$ z

9 Q  u' W3 P7 Y+ ~- n4 \2 c2 L四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。) \5 [! Q; M7 p  i4 @( [( R: [
打开usb_prop.c文件,修改如下:8 ]2 r: W- H0 B- W; L1 h8 w( S6 X
  1. void CustomHID_Reset(void), k+ D! S1 R0 f7 I
  2. {+ n* m4 F* t9 p5 [% H- k7 Z
  3. /* Set Joystick_DEVICE as not configured */
    & Y, t# E4 h0 i" f( ?# I
  4. pInformation->Current_Configuration = 0;
    . f' o2 b2 [! m% n
  5. pInformation->Current_Interface = 0;/*the default Interface*/  S, V( u1 `( f; U9 G
  6. SetBTABLE(BTABLE_ADDRESS);2 v9 a# F4 D5 k) @
  7. ; `3 k  c- ^- j) C; R4 Z
  8. /* Initialize Endpoint 0 */, ^; ?1 c  m6 L* E/ X
  9. SetEPType(ENDP0, EP_CONTROL);. [, G  d% S$ k& E+ l) X9 S" T
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);
    9 G3 B9 @3 L' |7 V
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);3 p: \* u' @; U& ?: S  k' L
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    4 V; w( |1 ~( T/ K+ J5 S
  13. Clear_Status_Out(ENDP0);
    9 E, h4 Z! {- A! E% Q7 Z
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
    % B6 \" E5 Q2 a# `
  15. SetEPRxValid(ENDP0);
    ( I3 }7 e6 ~4 B' F0 G3 N4 n4 q

  16. * ?! t+ l7 C4 S: X3 }8 ^
  17. /* Initialize Endpoint 1 */: g" G/ W7 S2 E. ^# S- f% W, Z
  18. SetEPType(ENDP1, EP_INTERRUPT);/ h4 ]' ~2 R3 B+ t* B+ S7 o# r
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);( B1 ?1 m0 |9 X5 z& E5 m
  20. SetEPTxCount(ENDP1, 64);: w. k3 N& @8 ?4 b$ l* k0 h
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);
    ; C6 V) G* x+ }% R3 F
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);
    : B1 G; ]! R4 U! V. {, y8 W
  23. * F4 I, n5 h; k9 D( [$ N
  24. /* Initialize Endpoint 1 */7 ]# Y/ J. N! o8 ^* f
  25. // SetEPType(ENDP1, EP_INTERRUPT);: K7 |# M; X. b0 i4 P9 g9 ?
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);
    - a# r" E4 ]+ \
  27. SetEPRxCount(ENDP1, 64);
    * t3 f3 m7 x! E( n! _6 Q
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);) ]% g4 ~0 M7 M
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);+ G' r- a$ o+ W# _/ f+ x
  30. /* Set this device to response on default address */5 v2 B# s5 ~9 J3 i" I! b$ P& q- E
  31. SetDeviceAddress(0);
    - K/ A9 F" Y  e5 Y( n
  32. }
复制代码
  O- @2 Q' k% B) B$ }* }& i
五、usb_endp.c文件# q( Z( K, E! g5 Q+ w' [/ r7 Q
  1. void EP1_OUT_Callback(void)) K9 ~' e( v! Z) R) d
  2. {
    2 ^" \6 D; [0 L8 m* `! V# S
  3. 这些写接收代码
    & `+ u7 n0 P' D
  4. }
复制代码

+ M8 e  [- R1 q( G; d: U9 q六、数据发送和接收,举例说明4 {5 E: M# i; M5 Z3 X
1、数据接收5 F' Y  _  s) N9 \; h
  1. u8 DataLen;
    $ ?; Z' h+ O& W4 G
  2. DataLen = GetEPRxCount(ENDP1);. z2 W7 O% n5 o$ T" {: k% D8 L. `4 t' i
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);
    5 f, d& i) U" M5 B% @
  4. SetEPRxValid(ENDP1);
    9 j# W$ g. Q' f0 }- X# ~7 Y
  5. USART1_Send(DataLen);& \/ T/ ^7 ^: i& ~/ }, R9 B
  6. count_out = 1;
复制代码

5 z+ P. k4 O5 L2 \3 M2、数据发送( l9 O( m# C( D" S
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);: d& g3 d1 p5 J7 ?, j  y( ~2 B
  2. SetEPTxCount(ENDP1, 64);
    . H' I5 C& u2 H8 O3 K$ v6 k
  3. SetEPTxValid(ENDP1);
复制代码

1 I1 j0 C/ h2 s+ I+ K" n如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。& C3 @0 z( w% q7 w1 \8 h
& X7 n+ F1 b4 @5 U, k1 K
  I% |2 Q9 R3 n$ }# b: n# z
(by xidongs)
& C  |4 t7 y# C1 ?9 V1 ~
收藏 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 手机版