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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程! b! u7 A* H7 ?1 p' Y9 @9 F

: {; ]" b8 ]* t1 |  V  h
最全USB HID开发资料,悉心整理一个月,亲自测试" y* C+ I# M' `- w/ G' s

8 }% m4 M: r$ L. W2 {8 t1 g
通过STM32CUbeMX建立USB HID的双向通讯实验成功
0 \( z+ A9 T! d3 m2 ^2 J5 `4 l1 ^% P1 p& O: ~6 d! m" b+ x
/ u/ Y7 l, R5 \: T# d+ B0 P% {
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。

( W* y/ P4 T; ^) y  l6 G& @% _" H
HID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在
" I3 R+ h* G+ y
5 d  S' a; K. C! a! b2 p9 n( N6 C一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。
) U) V, ~1 Z/ J- O, B9 x
% B" O! T! Z, j& M; P6 A  Z二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。" O! b3 Y: C3 I* l
  |" J0 [$ d$ t9 G2 i4 X
u8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的4 M4 L; O% |( A5 B) t. T
0x83, /*idVendor (0x0483)*/
/ P  i5 T; O: {0 v8 p( N0x04,
9 Q( h1 z* u8 u% m8 R0x50, /*idProduct = 0x5750*/5 X7 p7 p$ @4 z) Z4 d( n5 K
0x57,+ A/ p* `5 o0 B' v) K  P2 h4 t2 r* V
//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。
- \5 t+ P) c4 g. S//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。1 z6 H9 p, a4 V6 p1 h
//注意小端模式,低字节在先。
/ ]+ D; m/ k! o/ p//idProduct字段。产品ID号,我们这里取0x5750。4 `1 n! Q5 O/ G) O1 i( B, f; L
//注意小端模式,低字节应该在前。
& u. B1 c( K5 p8 {2 @
8 e: c$ d% n  ?2 ~! N
' |1 C6 o* ~, o& s1 zconst u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下" C3 w# d" \. {2 N* o
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */* _1 R5 s/ D  I  ^
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */: l% N# Z( |7 e" T2 G0 j
  3. 0x03, /* bmAttributes: Interrupt endpoint *// D* P- f* k- P2 `0 R7 I
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */
    2 B# [- ~- ]- e* J5 T8 n
  5. 0x00,
    % \. V+ D% B5 d$ h
  6. 0x20, /* bInterval: Polling Interval (32 ms) */; \/ _( Y$ ^: }! q1 v. Z5 p
  7. /* 34 */! x# ~  U/ y4 |* f
  8. 0x07, /* bLength: Endpoint Descriptor size */
    ( a% T0 n9 L7 C5 ~  [
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    . G. N: A4 `( @. {: y3 o1 `
  10. /* Endpoint descriptor type */1 R; S; y! J+ u. X5 m& C+ {
  11. 0x01, /* bEndpointAddress: */
    4 }  n) k$ h& ^4 K% Z5 Z& F9 I) I
  12. /* Endpoint Address (OUT) */
    ( f3 `6 Y* P/ ~, V: l
  13. 0x03, /* bmAttributes: Interrupt endpoint */* v. Z0 W% _$ j: l) s2 j0 M7 g( |
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */
    3 g5 C. k5 z/ y5 J8 {
  15. 0x00,
    ! o/ m3 ?+ I/ d5 F( P" H( Z
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码
% c$ ^3 G5 v3 }' h
上面包含了“输入端点描述符”和“输出端点描述符”。& W+ m" D  ?3 ~, H$ `
//wMaxPacketSize字段。该端点的最大包长。
$ j) J5 l8 S/ t6 ^//bInterval字段。端点查询的时间,
. ]6 ?: c) X' h  m
+ }0 z6 l* O) N- H4 j8 \' T为了实现更高速的通信我们修改如下:6 Z1 N  |$ h- T* R6 n3 g1 R2 u
  1. /******************** Descriptor of endpoint ********************/% @0 c7 S- X% K; H; z
  2. /* 27 */
    7 Y6 t; c1 d9 B4 N- j; n. [
  3. 0x07, /*bLength: Endpoint Descriptor size*/! M( q* ]# _; q2 l2 C# S- Q5 l
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    : U6 e) ?0 R" K1 r6 |% ?/ e
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/
    - y; @8 c" `& I3 J7 P. G" k
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    ' `, q4 b' C" I7 i7 x" J* Q9 i
  7. 0x40, /*wMaxPacketSize: 64 Byte max */
    4 C9 ^' v0 C3 U" M7 b! a+ S  T9 L
  8. 0x00,0 h) T8 ?" b2 y, m- T( W4 h
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    - ~% F/ O  ^5 D+ G$ u$ M6 g/ D- y
  10. /* 34 */
    ; U7 e( p" H; N6 `2 a2 _
  11. /******************** Descriptor of endpoint ********************/6 e  c; A3 T/ B8 Y, @
  12. /* 27 */  }7 O# h$ f8 t: H! b3 Q+ r  z
  13. 0x07, /*bLength: Endpoint Descriptor size*/4 u# \) n$ J6 }) I! P7 e8 K! l  V
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    ; u3 C. Q9 }) z- v; w& w
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/
    3 V) u* h1 ?# \2 `) d$ z" z
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    " @2 n" N9 c6 g1 k/ b9 i2 N: @
  17. 0x40, /*wMaxPacketSize: 64 Byte max */
    " W/ ~) d* B8 F6 N) P. J, p. N
  18. 0x00,2 i& D/ m+ W1 M) {" B2 t# k
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    0 K( z) B! i& m3 |

  20. 9 Z" R& {1 [' \3 \7 Z
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。( X$ ~3 r; T5 m
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
    ) Z. I8 b# Z: j4 E6 n3 @+ ]9 E& k
  23. {
    ( b6 `" _* _; u
  24. 0x05, 0xFF, // USAGE_PAGE(User define)
    3 Z& y7 d2 x$ x1 {
  25. 0x09, 0xFF, // USAGE(User define)
    9 e4 R# S* m  S
  26. 0xa1, 0x01, // COLLECTION (Application)3 q7 U$ X# P" W7 Q* V: Y. X! d
  27. 0x05, 0x01, // USAGE_PAGE(1)
    4 L3 M* t; Q5 W7 w) K9 ~( F
  28. 0x19, 0x00, // USAGE_MINIMUM(0)
    / ^- y3 X6 U8 [. H1 a
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255). m5 b" L# y/ `+ ~& x) V, S8 O
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)3 Z# x/ x& v2 M8 a. _9 B0 ?1 }
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)9 N& V3 Y2 w0 ^7 W+ ^/ ~0 S
  32. 0x75, 0x08, // REPORT_SIZE (8)
    2 j% d& Q- [6 z/ ?9 o
  33. 0x95, 0x40, // REPORT_COUNT (64)
    1 z2 A3 x6 d( q# G+ c2 }
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)
    + T1 e* k0 l: i; B+ u
  35. 0x05, 0x02, // USAGE_PAGE(2), ~. A! g+ v% S& n8 h8 f
  36. 0x19, 0x00, // USAGE_MINIMUM (0)6 G; N8 n, ]& u9 P3 l( x
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)
    & s) q( _+ W: j9 M4 I
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0); m* i5 L# g" M! D
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    9 \# c6 `1 W) J
  40. 0x95, 0x08, // REPORT_COUNT (8). y$ P4 u) a% h9 V
  41. 0x75, 0x40, // REPORT_SIZE (64)
    & W* x3 g- I$ }4 c: s/ i" _' X+ I
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)4 R) D7 g+ G+ y# P
  43. 0xc0 // END_COLLECTION
    * k# {9 Z9 U8 W( W% M
  44. }; /* ReportDescriptor */
    ( H( `2 t2 C' \0 [4 O1 a+ g
  45. 8 B8 s9 W& W: N, o
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]/ H# F6 B% m0 L6 ]
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]) e$ n% _) w6 }! K1 n" P' c
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码
+ N6 T- H2 f; r0 D) f$ E: ^' t
分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。
/ Z$ l) k; f8 F% S
8 i2 M" H5 M; E5 }1 h% w' `1 S' A! Y- m
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39
    + V) ^3 y, Z/ `8 @
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 642 ]  }4 D) ^/ F4 a% q9 b
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28
    ' r9 G" x" ?. }1 y6 [, n! X* }, Y/ u
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码
1 Y$ _  p* t) _
三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数) f, }' T# @( d3 c
a) Set_System(void)- g5 Y6 G& V  g: t/ v7 _( h+ y
b) void Set_USBClock(void) 2 g: L3 g% \4 V+ U5 g4 a
c) void USB_Interrupts_Config(void)
$ ?$ ^( Y/ ]. g6 E2 |' I9 n5 ]d) void USB_Cable_Config (FunctionalState NewState)
8 p, m0 g: H% s  a; w特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。
; L1 F- z' m! K" E! t+ X( N4 I( Z! x; {5 _9 l
/ u/ N- T5 s# \' g9 q; P
四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。
0 @1 D0 k' h2 P+ g4 ~6 i! e& e打开usb_prop.c文件,修改如下:
% v& O! X: W3 G. |  L2 o
  1. void CustomHID_Reset(void)3 L/ u( h, I& Y* e. X4 C! W- Z
  2. {
    ' W# s: x6 L# w, W! h# G: _, T( ?
  3. /* Set Joystick_DEVICE as not configured */) G/ I8 f7 o+ K" J- {: {. M
  4. pInformation->Current_Configuration = 0;! E6 I/ R5 k1 y+ d  U
  5. pInformation->Current_Interface = 0;/*the default Interface*/
    : u  K2 @% r  ^* L
  6. SetBTABLE(BTABLE_ADDRESS);' a6 {3 d9 [# B9 V9 _

  7. 1 P( X3 {8 |& o, S9 j
  8. /* Initialize Endpoint 0 */
    4 o7 `! ~5 W& c. p0 ]2 T' ~
  9. SetEPType(ENDP0, EP_CONTROL);& t8 j  ^6 l( I6 Z' G
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);4 u; [7 `' u& Y) n8 E  _1 x0 p
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);- S: V# z6 f- ^: A0 g
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    3 W) A2 J& T) \' A5 \- Q
  13. Clear_Status_Out(ENDP0);& O& |! Y5 x% P8 {
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);& o! s$ d/ G; X5 O
  15. SetEPRxValid(ENDP0);
    / c# t$ f% f% @: v2 F& g# l

  16. $ h8 u( P9 n1 q- S" d  y: d
  17. /* Initialize Endpoint 1 */9 v; H5 K9 c3 I8 R
  18. SetEPType(ENDP1, EP_INTERRUPT);6 A  @' w7 E! z4 {) J
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);
    : E* T/ ~7 v. }& p5 f, K
  20. SetEPTxCount(ENDP1, 64);" ^4 K- ^  [5 J5 Y$ y& o: u
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);5 M: I; y8 M7 B, O. g& {
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);
    9 d6 d; w9 t5 v$ C3 {

  23.   j1 o/ z' Q' {
  24. /* Initialize Endpoint 1 */8 W. o  I2 I$ z3 T
  25. // SetEPType(ENDP1, EP_INTERRUPT);3 I. }% g- L. a+ Q/ P; u; {
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);
    - ?# X' G7 x: \; o  t4 }8 |. q
  27. SetEPRxCount(ENDP1, 64);
    6 E; D! c: C. M
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);9 Z1 m2 I  F6 O& `+ y1 a
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);2 t2 v  ?: E$ h, ~; F. p9 M7 E
  30. /* Set this device to response on default address */
    ( r8 o  z7 m; m7 k2 \7 C" ]
  31. SetDeviceAddress(0);
    5 u  T( Z- L$ g$ r4 O- D, v
  32. }
复制代码
8 @& D7 ~' x' e, p, @
五、usb_endp.c文件- a& {$ k4 x: B" N: k
  1. void EP1_OUT_Callback(void). e5 l" g1 [: H6 f) i3 |# Q
  2. {% z8 G6 ^6 P8 Q- U( J- ?( a1 F
  3. 这些写接收代码
    , ^, _2 K6 r% V9 O$ U
  4. }
复制代码

0 X* r+ n5 Q, q+ t3 a3 w六、数据发送和接收,举例说明
& F# B7 W$ [' S9 F1、数据接收/ r, m+ E5 b3 D: |
  1. u8 DataLen;
    4 s1 |1 g3 K# q& J1 x& f2 |" M
  2. DataLen = GetEPRxCount(ENDP1);
    0 I) M7 y5 u& [% [9 S) N9 O- |; \
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);
    0 L% i% V7 C/ }5 ^% H
  4. SetEPRxValid(ENDP1);* L" D& p; {) u- c3 C; ^/ ~$ j
  5. USART1_Send(DataLen);# Z, V0 X8 K0 Z: m( l
  6. count_out = 1;
复制代码

6 D* D0 P7 U( i& e) U- m& \2、数据发送+ L  J. d' i: `+ b' z- [
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
    $ b3 D( A" x. g! q
  2. SetEPTxCount(ENDP1, 64); 0 a) N: F, l: I- }4 j
  3. SetEPTxValid(ENDP1);
复制代码

% q% F" v6 S6 a% I( v: P4 x% j0 u如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。4 S, @5 U: u- |: X- ~

! {) G/ H+ s5 [: f

2 q2 N- R) f: g# B# U' K/ x+ X(by xidongs)( O8 O4 d  r7 S0 f6 T5 C8 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管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版