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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程
# C( p# B2 c; t; c9 X4 a5 t; o8 Y, n, n
/ L7 y. M3 s/ h8 n, f
最全USB HID开发资料,悉心整理一个月,亲自测试
: n5 W8 A- a1 C/ @2 W5 L" v4 s; ]; r% x
通过STM32CUbeMX建立USB HID的双向通讯实验成功& k: V+ S/ J" P9 t$ F
1 x; u5 B7 m. Q! P% O) T
4 J4 Y6 ?6 p- b, p$ x8 a
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。

% V9 R# I& q; f$ M; f' Z3 M) @* L2 y: K; N6 W1 t# c* W3 e2 D" K4 {
HID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在
! k+ }- B; I, d! w+ W- q: ?2 [) x6 }+ u/ S: ~
一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。$ I3 a% Y6 b: r/ n6 }: x3 V2 X

$ H/ h& Y( Q$ b$ e4 h$ G7 g二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。8 Q5 s: @8 J- w  O

8 V+ G% J$ q! y% x& Zu8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
9 D( t4 \4 Q- i8 ?% a5 p0x83, /*idVendor (0x0483)*/3 Y" Y7 _3 R1 G, D
0x04," @  U" M" R( ?& {
0x50, /*idProduct = 0x5750*/
7 \+ d& I5 N7 W1 W  W0x57,
/ b. ?: z" f6 u8 s//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。5 o  t4 w$ T$ j
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。$ P/ F% E4 m5 |% f
//注意小端模式,低字节在先。
6 d1 S, w7 \* i! f8 Y  K, |4 G//idProduct字段。产品ID号,我们这里取0x5750。
8 O, B0 k1 C0 F% B6 K) ~//注意小端模式,低字节应该在前。
0 ?) C- K! M% p) S8 b( f6 A& `2 z: h1 ~* z7 X
# \2 W" f8 z) X( ?! b- l
const u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下8 b4 E2 C: S" @( E
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */# i2 ^4 n. d6 A; k
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */
    ( P4 z$ j8 j) J  P
  3. 0x03, /* bmAttributes: Interrupt endpoint *// i. Z# \: v) N7 I9 h
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */' j* J! E' }' G+ z9 `+ {7 ~# ]3 L: u$ B
  5. 0x00,3 s/ C) H/ D4 W, M
  6. 0x20, /* bInterval: Polling Interval (32 ms) */, z" d2 s/ H# r4 I6 O& L9 r
  7. /* 34 */, q( Y' D! ]( g4 [
  8. 0x07, /* bLength: Endpoint Descriptor size */* A+ _0 X" ]0 b( R  y' |& B" `
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */1 e3 h9 A8 w8 F: H' u
  10. /* Endpoint descriptor type */
    ( Z( R! `8 h0 w0 ]0 q
  11. 0x01, /* bEndpointAddress: */
    ; ~0 O- m) c6 h) ~7 p
  12. /* Endpoint Address (OUT) */
    * n3 M! D% H0 ]0 O! \/ P8 y3 j# t
  13. 0x03, /* bmAttributes: Interrupt endpoint */3 A0 |4 x6 P9 T% r2 F% C
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */
    , t2 p0 I, i" F" X* B
  15. 0x00,6 |9 Q) p9 ?- s0 J
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码
$ D$ J) R7 m4 ^8 n5 e2 l$ p4 ]
上面包含了“输入端点描述符”和“输出端点描述符”。
% i5 p- ^: e7 ^7 g4 \( ^//wMaxPacketSize字段。该端点的最大包长。
& ~& f6 e: w" k* W5 f//bInterval字段。端点查询的时间,
6 ]% ~0 h8 y! y* H2 x
9 Z# B7 r4 r5 x# J& L为了实现更高速的通信我们修改如下:
; W; B# w8 r" [7 p/ L5 l
  1. /******************** Descriptor of endpoint ********************/  @& ?1 `6 A3 q  r
  2. /* 27 */
    0 S: {* n& ?8 s% \" h
  3. 0x07, /*bLength: Endpoint Descriptor size*/0 b4 F* t9 U" R( x" J: K1 D
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/6 H: N1 W' z! s9 V9 r. A. Y/ Y
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/
    - \, N% ]8 R; q8 `8 D6 ~  F3 Y6 i
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    8 Z% X; a5 q! W7 g& h& o. N. ?
  7. 0x40, /*wMaxPacketSize: 64 Byte max */. H- e$ ^! o- \3 O3 M. {
  8. 0x00,
    2 _, `) d4 k5 ^8 a3 d. }  ?! h5 ]. `+ {
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/7 u3 w1 c4 P& D
  10. /* 34 */1 A$ c( P" h7 ]2 A1 V
  11. /******************** Descriptor of endpoint ********************/
    . E7 x& ^& Q* E- N2 X, A0 t
  12. /* 27 */# D& U  e; P9 f3 F
  13. 0x07, /*bLength: Endpoint Descriptor size*/  L1 ]2 H( U3 j# u# L
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    , d2 D7 T" _& b* b, J
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/) W" N8 Z- Q, h) L
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    * e$ p& h3 W/ h+ O$ v5 I  K
  17. 0x40, /*wMaxPacketSize: 64 Byte max */  o3 u3 R: x* |# [; C3 F* Q/ h
  18. 0x00,
    ( y0 b3 Y/ K  O( p% m$ |7 L
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    / I7 J$ M# J) [2 X! M6 j

  20. % o2 {+ L7 O% x: Z; K/ |/ H
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。
    ' |; ^) D5 x9 U* b$ c* P
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =; k3 R. h) |+ Z5 g- \
  23. {: n- [4 p% X! o- s- N1 A' }; D
  24. 0x05, 0xFF, // USAGE_PAGE(User define): L; _/ `% P. Y* e) |' y  D
  25. 0x09, 0xFF, // USAGE(User define)
    ( T* D0 [4 c/ \( l$ w0 s% x
  26. 0xa1, 0x01, // COLLECTION (Application)% `$ h( J" K0 T
  27. 0x05, 0x01, // USAGE_PAGE(1)/ m7 \5 \) {) x" e6 q$ u
  28. 0x19, 0x00, // USAGE_MINIMUM(0)& R* X( y9 p# A
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)7 J8 b# ]# x; Q3 P' [
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)$ t; a) i1 E9 Y: n3 v7 T$ }  f
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    ( B  E  X; l9 G
  32. 0x75, 0x08, // REPORT_SIZE (8)
    ; G& O; P( w) F8 D6 w8 N
  33. 0x95, 0x40, // REPORT_COUNT (64)5 ?' i3 T" N' ~! \
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)
    + {2 z% A* U( h8 n
  35. 0x05, 0x02, // USAGE_PAGE(2)
    9 t( L  M4 o# j0 d7 ?
  36. 0x19, 0x00, // USAGE_MINIMUM (0), Q8 ^+ s# }' W9 {; J- ]; H% x
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)
    6 [: _# u% |5 q5 H  T+ x
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    ( u3 r# S) R3 K! ^/ k
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    1 |  |  S  a3 _' ?7 `
  40. 0x95, 0x08, // REPORT_COUNT (8)
    - y# v' z& ?$ F4 D
  41. 0x75, 0x40, // REPORT_SIZE (64)
    6 ~" o$ K( |7 v* d6 {1 E: ]6 A
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)
    * L8 k. f+ Q6 _6 N% {0 p
  43. 0xc0 // END_COLLECTION
    * S1 X/ P6 i' _
  44. }; /* ReportDescriptor */, k8 _% n* X1 }* v- K1 v
  45. . U0 g1 t  _- C" z3 ^
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]5 ]2 b/ T6 _$ U6 }1 P/ x9 @
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]
    + d, i% [6 ~' Z* F
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码

: \5 x" _0 @3 @5 g  h分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。
! u/ |# z$ H1 S- w1 c$ w+ Z# W/ s8 _4 }+ t5 y! o
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39( H9 V, L! T6 t, g6 g* T( C8 S
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64
    0 d. Y: E4 d  u' q9 D
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28( U4 O. ?  r& Z: x5 V2 C+ M
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码
$ A5 A4 V5 z- R7 D- M
三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数3 G2 U' {9 F8 D% H( P
a) Set_System(void): d! }/ q4 `+ M* v9 O0 |# {" Q
b) void Set_USBClock(void) 1 r; s7 z2 X2 |$ V$ ?" w8 c
c) void USB_Interrupts_Config(void)
6 Q  t( ?/ m/ H7 P5 }( md) void USB_Cable_Config (FunctionalState NewState)  e  O7 d- |+ D$ ?1 D  \$ U
特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。) R# l) c1 }% c0 V5 k. |

  ]+ z1 m+ x! y) _  ^; z
2 q9 c) o8 w& Q9 a( Z四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。5 S3 `% F2 j9 t: x% P
打开usb_prop.c文件,修改如下:
0 b9 y0 G7 i; R0 D
  1. void CustomHID_Reset(void)  l( \& s# ^. ^
  2. {
    & r: U1 P& k5 k1 b: v
  3. /* Set Joystick_DEVICE as not configured */2 ?& b: h% o- L8 e( ^: o; j+ @
  4. pInformation->Current_Configuration = 0;) Q0 \$ S4 G/ b+ U  q  c. C
  5. pInformation->Current_Interface = 0;/*the default Interface*/& H, \# i) X$ w, x  K9 B
  6. SetBTABLE(BTABLE_ADDRESS);
    / Q: h7 o! e+ q/ _, a2 c7 _
  7. 9 T: `  A* |& q
  8. /* Initialize Endpoint 0 */) {* V  [; L8 e' F' G( Z: F
  9. SetEPType(ENDP0, EP_CONTROL);
    1 v) @6 H% u- a& }& w! @5 g
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);
    9 Z7 Q) R" f( ?- R- h
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);$ s7 o9 }+ ?8 P. ]2 @# h, j4 H9 f
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);" e3 [8 G! J3 N
  13. Clear_Status_Out(ENDP0);7 o! N" r$ a2 Y7 q1 D- s
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
    ' q. s- }; H* T1 L0 O
  15. SetEPRxValid(ENDP0);* W- G9 V0 I* @5 d- y3 m* d5 m

  16. " v' t6 |; p: l5 P4 Y  ^
  17. /* Initialize Endpoint 1 */7 w0 k4 n5 F0 |; J0 |
  18. SetEPType(ENDP1, EP_INTERRUPT);
    2 v8 d' i5 N* \3 F  S+ f
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);
    . c. `2 P/ k" u$ ]
  20. SetEPTxCount(ENDP1, 64);
    - M$ I( v  d7 A% x
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);
    / E; x1 d0 P% ]( o
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);, `+ W3 [, i" S$ e

  23. ; ]% Y% L2 T! b7 ^7 }
  24. /* Initialize Endpoint 1 */
    7 Z3 a5 S- r' O: ^
  25. // SetEPType(ENDP1, EP_INTERRUPT);
    - H" d! L7 |, `  O& S5 R. w$ b4 \! D
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);
    & a" }1 {( R: }4 ^; [! ~
  27. SetEPRxCount(ENDP1, 64);& y1 @6 [8 K( \2 ?  o9 K. Q
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);& A0 c* L; A9 ]+ o( x( @
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);
    # z, D- j) r  L
  30. /* Set this device to response on default address */
    3 u1 i' W% q5 {6 i' ^! k* K; z
  31. SetDeviceAddress(0);* J3 L# P4 ]. f/ d/ d; M% U$ ?
  32. }
复制代码

' y& D0 U0 |, G( w- e5 `五、usb_endp.c文件
& t: R$ Z; x: l
  1. void EP1_OUT_Callback(void), }6 j% A8 _: A
  2. {
    2 H; J' K* c* g5 I$ [* F
  3. 这些写接收代码7 q/ x+ [9 C# f2 r" ^. n6 {# J
  4. }
复制代码
( W2 I  a$ Q" Y5 l+ w
六、数据发送和接收,举例说明
- k! b& s9 U5 \2 E5 A  Q1、数据接收* K* u+ y' F1 a& R
  1. u8 DataLen;
    2 g4 M# D; j/ M- H) H9 L" d7 I
  2. DataLen = GetEPRxCount(ENDP1);
    % L- [- g! f5 @( o1 h
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);; X3 C# S9 T/ t' Q* O. l
  4. SetEPRxValid(ENDP1);: ~  g0 M/ q  x& c: K* U3 k$ L
  5. USART1_Send(DataLen);
    & [* }! D* f; e& S* v8 }0 Z$ E
  6. count_out = 1;
复制代码
# _4 l8 Y8 u  f
2、数据发送, `6 y8 d% j0 c
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
    , I6 R' o# w5 K3 l9 L
  2. SetEPTxCount(ENDP1, 64);
    + D5 }9 ]8 x( _/ w" S
  3. SetEPTxValid(ENDP1);
复制代码
; O- _, L2 V3 ?) }* L7 C$ t5 z
如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。  i: A1 N; F2 w
- W& }9 u/ u3 q3 ?% c+ \% ^
* D; s# M6 d; i% |- z* P  ]& T
(by xidongs)
: \: G9 \8 H7 Z
收藏 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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版