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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程
% v6 ^5 h* G8 k/ }3 u
: z3 @" P! Q7 H7 {$ v% M6 u# |
最全USB HID开发资料,悉心整理一个月,亲自测试
' d, M7 D; J5 U; ?
0 u/ M/ X/ E- ?6 T6 s
通过STM32CUbeMX建立USB HID的双向通讯实验成功# `9 I, o. G8 l4 Y  |2 G' Y2 V

% C$ P) B- i/ A1 ]6 ^: h1 Z; c3 h" j
5 ]  V- j9 Y9 P( f6 N$ A
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。

" R% `( {% ]8 ]; q5 v+ L; e2 ]; z- F( S; ~, o! i
HID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在; d8 U% X9 d- o& o- E1 ~( r
! c0 M% a( s0 `$ w7 E
一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。
4 R' @& p/ Z, |6 [1 p9 A5 Y* R
% s' H, z1 K2 P% T/ N% o, Z* g0 g二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。/ w* ]; R% z$ }7 A( A2 o8 }4 J% T

/ B( e  ^$ q* q" p: Du8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
2 u1 w7 S- S0 L8 x( ]2 b- r0x83, /*idVendor (0x0483)*/
2 c. P7 E& f1 z9 E) A, f2 e0x04,1 S) U) s& q* x
0x50, /*idProduct = 0x5750*/& A; z1 _. T0 W
0x57,! {4 m/ P. J4 E  L% a- D# V
//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。; ~9 x: U: i: J
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
! L6 ]$ P! i( Z9 p# d& t( D  j//注意小端模式,低字节在先。3 p4 K- b! g. T0 x3 g
//idProduct字段。产品ID号,我们这里取0x5750。: m' z, b7 q. m
//注意小端模式,低字节应该在前。6 }/ n0 ]* q4 [4 a. ~

- {% G4 Y! O& Q: N# x. `7 ]5 L% ]( [  s
, |3 `  i3 e' u* d7 econst u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下. s) s, k2 U: A0 f7 S; L+ {2 z4 m9 x* u
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */, K& q- H' ~' l+ m# e6 c
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */
    2 O0 h3 I0 M7 }' h$ Q0 W
  3. 0x03, /* bmAttributes: Interrupt endpoint */
    2 \9 y5 Y6 Y2 S3 V# }
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */
    9 ^0 {+ j$ B9 D* u
  5. 0x00,6 U) b' N9 A. o2 k0 S
  6. 0x20, /* bInterval: Polling Interval (32 ms) */: y/ m6 j, R, ~3 b  a
  7. /* 34 */
    : D" Y, F/ `  N8 C( e
  8. 0x07, /* bLength: Endpoint Descriptor size */6 l8 O  ]% n; r
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */* _5 g" a9 v+ h) W
  10. /* Endpoint descriptor type */- |) I+ Y, N; {
  11. 0x01, /* bEndpointAddress: */
    . ]7 A/ }7 U! c3 e+ L
  12. /* Endpoint Address (OUT) */5 H+ U& N8 s9 d3 B6 p5 ?3 }- {
  13. 0x03, /* bmAttributes: Interrupt endpoint */& M) x! M% i2 c! `  i
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */
      E% W4 B; i- N0 K# x- r
  15. 0x00,. K* T: N$ @0 O0 H" ^
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码

0 m; P+ |5 v' t) s上面包含了“输入端点描述符”和“输出端点描述符”。5 k; h, _: P# y
//wMaxPacketSize字段。该端点的最大包长。) c$ v9 r" P3 B6 g; L
//bInterval字段。端点查询的时间,
! K- V( `% s: j* N6 J# I
( L5 F3 N! G6 J. x5 D4 r为了实现更高速的通信我们修改如下:' c- @1 V& u9 T( t
  1. /******************** Descriptor of endpoint ********************/
    3 v& v4 y  t9 A' z8 N
  2. /* 27 */0 r2 e4 T* N7 j3 T. }
  3. 0x07, /*bLength: Endpoint Descriptor size*/
    # H% X) F8 W8 c
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/6 ~6 f( Z7 X: B7 g& @' x
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/
    ( B; N8 I2 J2 q2 S2 r# s& c
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    / l! E, h- S) a7 U4 q& S7 P0 H# b
  7. 0x40, /*wMaxPacketSize: 64 Byte max */9 w! X4 t" {5 g
  8. 0x00,/ l4 f0 h, B5 a  c% u
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/5 w6 o! ?9 a1 m$ m. E
  10. /* 34 */- n( B0 A( \5 p; `
  11. /******************** Descriptor of endpoint ********************/
    ( l$ p9 g2 @: |, ]) q* x! Y
  12. /* 27 */- E. F4 U. u  ^0 ^' s4 e/ {& v2 m; j
  13. 0x07, /*bLength: Endpoint Descriptor size*/
    " j- z, d& n: n$ j" `
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    7 j7 [! y* D- Y) c
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/4 O4 @0 d, O4 \8 |- o( |
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    : G+ ]8 d9 I. k; l6 Y
  17. 0x40, /*wMaxPacketSize: 64 Byte max */
    8 `" V+ q' K, o4 u
  18. 0x00,2 G4 T; e( B4 d
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    ) a/ [" m* k) N) ?+ B8 N# a
  20. - s; j; ?; r6 i8 |8 E
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。
    " J: v7 b: ^  P0 k  i2 P/ A& c5 I2 V
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
    $ p) ~5 I6 G9 \
  23. {
      ^( ^4 N& X! R# M0 }% T, u
  24. 0x05, 0xFF, // USAGE_PAGE(User define)
    % ?/ c4 Q- q9 o' J; I0 L, @. D
  25. 0x09, 0xFF, // USAGE(User define)2 c% M5 L4 @) V! E& h' P% S% b
  26. 0xa1, 0x01, // COLLECTION (Application)
    % r( \5 S: @$ j8 e4 i$ }6 M
  27. 0x05, 0x01, // USAGE_PAGE(1)* D7 j/ {2 C* y0 v' j1 O
  28. 0x19, 0x00, // USAGE_MINIMUM(0)
    ) N& L" r5 f, d
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)
    ( O7 P$ R, i! |9 Z+ n( F6 H, q: _: d
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    # I$ V/ p6 [5 M2 m* o
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)& \$ }: r" x/ }  C% p9 |( X
  32. 0x75, 0x08, // REPORT_SIZE (8)7 M, h% \8 P7 a$ F3 m" o
  33. 0x95, 0x40, // REPORT_COUNT (64)
    " i, I0 `% z- L- C0 X
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)) T8 }5 U# R  h# q. Q; p( O* ^! ~8 O
  35. 0x05, 0x02, // USAGE_PAGE(2)
    2 m- e& M* ]6 t8 |) D
  36. 0x19, 0x00, // USAGE_MINIMUM (0)1 W. N+ U& j2 ?* i0 Z
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)
    % y; a% Z* s0 t/ K
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    ( L/ z( V! h* [8 z; M4 t+ R
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)# u( w5 r* j8 J+ `9 `! q
  40. 0x95, 0x08, // REPORT_COUNT (8)
    ( M! d: Z4 @* A) @, l) Z
  41. 0x75, 0x40, // REPORT_SIZE (64)
    ( J4 P  v- A0 C) R7 n8 c
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)
    / e/ u% f: V' y7 f
  43. 0xc0 // END_COLLECTION/ Y- m& A9 r% t5 j
  44. }; /* ReportDescriptor */
    4 O! u3 d7 N- H& J; _7 j

  45. 5 f1 z7 ?( K) Q2 O& X/ a
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]% ^" c; W2 p. {5 k4 x" u. b
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]
    6 A* p2 ~. k  ~( d* |
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码

/ j& E, K* x7 q0 ^* a6 ~+ c# L分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。
' x0 j% Y; V8 h! K- S
0 \- R5 J5 ?/ c
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39
    , x6 Z0 r9 t9 k" t2 K  h
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64
      l, a% T+ I& L4 I/ c6 I
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28
    * r( L) S$ C  b4 ?; O
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码
9 |1 y5 @# S6 v: g
三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数: }, |& @  z4 n0 N8 R4 ^) K
a) Set_System(void)2 V* E8 l: I. d0 F
b) void Set_USBClock(void) 4 ~, h/ C3 P/ m" q( Z' r9 m
c) void USB_Interrupts_Config(void)
6 P8 H; V2 G& B! e& ud) void USB_Cable_Config (FunctionalState NewState)
" f; R7 V. `7 n% y特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。/ ^3 v+ }0 {) B

3 y. R* p4 o2 J' ~
4 v8 f5 x9 t8 X2 J/ ?四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。
9 t( X- ^  m( E" B! x& x打开usb_prop.c文件,修改如下:
  q, I9 I+ V. H' h) F2 c
  1. void CustomHID_Reset(void)7 {9 \4 P# Q/ B# K
  2. {, c1 `0 H6 @* V2 S7 x+ w2 K
  3. /* Set Joystick_DEVICE as not configured */. g+ J% m1 {% m% F' X
  4. pInformation->Current_Configuration = 0;1 c5 g- ^, }: n- P
  5. pInformation->Current_Interface = 0;/*the default Interface*/6 F8 [" K2 N% i/ `" h+ b
  6. SetBTABLE(BTABLE_ADDRESS);' m  p3 \. S, c, \! B4 [

  7. # Y& H. I* d, J) t$ u: K) z4 R
  8. /* Initialize Endpoint 0 */
    6 b- i8 E; j0 j  u
  9. SetEPType(ENDP0, EP_CONTROL);
    - S* @$ e/ x( W8 R" l
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);
    ' ~# G& D6 w, m
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);( ]- E4 e8 ~. K1 [9 U3 S
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    : v$ j( ?: j5 a" H
  13. Clear_Status_Out(ENDP0);
    # }+ q, J% V6 `
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);" n0 u5 @' t4 Y
  15. SetEPRxValid(ENDP0);
    ' G* T- }! T' D' |# ]" T

  16. 6 E6 W- h* s: k2 P5 k; O6 k6 H
  17. /* Initialize Endpoint 1 */4 [/ ?# o2 c! w# F4 u7 \+ `
  18. SetEPType(ENDP1, EP_INTERRUPT);, }$ _! |/ [! s% b1 u
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);4 O3 S& ]( h- R9 K" G' Y  e1 L
  20. SetEPTxCount(ENDP1, 64);  q/ ^; _$ O' `! E6 y0 Y: B1 f
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);: p9 C+ j- [( C) [' a
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);  d. W9 q3 k, Y1 W8 D4 f  w  {  L2 L

  23. 4 ]1 J; B% D6 t/ F1 K# u! S
  24. /* Initialize Endpoint 1 */9 U0 d/ K5 s) I. e
  25. // SetEPType(ENDP1, EP_INTERRUPT);
    * i- ]8 e8 {( r+ M
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);" C5 m8 s3 M, ]7 e' }
  27. SetEPRxCount(ENDP1, 64);; `. v$ f! B' l( \
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);
    1 j, n7 Y, D: z6 D3 y/ E4 N
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);
    3 b- P+ K& d4 ]+ h, ~' S
  30. /* Set this device to response on default address */
    4 H0 @/ W" t9 [' K/ Z
  31. SetDeviceAddress(0);
    1 h2 J" v/ x# {6 H& F7 I8 h
  32. }
复制代码
) I1 K* ?5 q( r* u
五、usb_endp.c文件
3 j9 f" t( Y; V4 V6 [) C- \, p
  1. void EP1_OUT_Callback(void)
    " T/ l. @7 U: R. |6 n/ s0 e" t* u* h
  2. {9 G% L5 v0 U7 I
  3. 这些写接收代码
    * P$ _, d8 I5 n: ]' U/ Q5 Z% _
  4. }
复制代码
& o( E2 E, f1 f- C
六、数据发送和接收,举例说明6 x) ]; U* |9 H( K; x0 E
1、数据接收
, W+ W" S* d. K$ k! q/ ^
  1. u8 DataLen;
    # h8 q0 \% n# z( a) X* G
  2. DataLen = GetEPRxCount(ENDP1);
    " I' w6 R9 m) E5 e* w
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);
    ! {0 E6 n1 q' R* W) q% m
  4. SetEPRxValid(ENDP1);
    ' [; c! J! J. g8 t! `
  5. USART1_Send(DataLen);
    $ f! i1 i: o! J0 j% k0 N
  6. count_out = 1;
复制代码
/ M$ f3 @0 H2 {
2、数据发送2 n2 k) Z8 R  w! t( V% C3 @
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
    ) O5 X, `# l1 c( a2 _" j4 h
  2. SetEPTxCount(ENDP1, 64); 5 u$ W* z) |- t" U
  3. SetEPTxValid(ENDP1);
复制代码

2 i7 P* {6 Z5 g2 Q2 g如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。5 F7 H, {% `, B6 G0 V# o

$ D  X7 w" l* a1 \, K- h% [
) t" |  f/ x& _
(by xidongs)& Y, }3 z0 l+ u+ @+ A
赞 收藏 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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版