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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程2 Q/ @3 w% h" {7 L  P# S
" n# f: M8 r+ N1 i3 U
最全USB HID开发资料,悉心整理一个月,亲自测试7 C8 O% C  D, U3 w$ A

4 B' A! Z8 N! K$ ~( v0 A
通过STM32CUbeMX建立USB HID的双向通讯实验成功" s; U) ~- K( e  @8 b

2 E4 F+ [. B9 q7 L0 v7 y
" X6 l# y8 c) r* v5 N- E
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。
! x) d/ O) M4 h, X3 C
5 W4 |. s5 N& W, ]& `; F4 C* ^( J! h
HID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在
3 _9 ?! h3 x8 K; w; _& s7 g8 Z4 v# q7 Z: ]* j
一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。8 u/ P8 K4 b* P9 |( }
# t9 a6 v' l! K2 |+ w  _
二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。1 ^' T1 C, X' h% V5 \* |

; t! J8 G  H! D+ B& Ju8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
/ g; e7 s' x6 v' w& L2 W0x83, /*idVendor (0x0483)*/
5 D. O! s! a- S( J" d) d+ E- d0x04,+ y# E% c( T" h
0x50, /*idProduct = 0x5750*/
3 v2 G! v! x, M; @1 D0x57,% f- N6 m: f0 A4 I
//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。
' K/ s9 H) c/ q0 l6 j, r! y//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
7 ^: P  i+ l8 m6 x2 Q! q) N//注意小端模式,低字节在先。; X% w* L) N  ?6 e/ C* O+ Z
//idProduct字段。产品ID号,我们这里取0x5750。0 {  y1 O0 u: U, N* y& {4 \3 {
//注意小端模式,低字节应该在前。
1 h0 a2 u# _; s5 @  l- j1 d4 y/ d6 _! G; O; M: C8 ?. t
- F1 k9 u# F3 N0 O' R3 G2 I
const u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下
3 e1 u; N/ Y- u; x0 A
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */! F3 }! j+ s5 p# \/ L
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */  S' Y. [7 ~+ u# g2 E) f
  3. 0x03, /* bmAttributes: Interrupt endpoint */
    " c5 j6 O8 y/ t+ d3 ]
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */
    $ g# [6 N8 q+ ^- J; Y0 N/ b! }( n
  5. 0x00,5 ?3 H+ Q5 r1 a7 E" R' x3 O4 r. N
  6. 0x20, /* bInterval: Polling Interval (32 ms) */
    4 ~5 @' I5 J( ]; [% G
  7. /* 34 */
    2 s/ l* a  h1 u& [0 U
  8. 0x07, /* bLength: Endpoint Descriptor size */$ ]- g$ j( l& ]. ]3 T/ g- n+ ?$ S. \
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    6 o+ w- t" c3 I3 G
  10. /* Endpoint descriptor type */
    # `8 g0 K6 Y% A$ S( f
  11. 0x01, /* bEndpointAddress: */
    " p6 R! [6 O! p0 f! g  w) e
  12. /* Endpoint Address (OUT) */( W& e" [' y* C+ O
  13. 0x03, /* bmAttributes: Interrupt endpoint */) U0 z9 `- O7 h( w/ t! m
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */* V2 _- D7 r) I% A( o
  15. 0x00,
    : j7 j! S8 D& o# N/ v( O" M
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码

9 b$ C  r8 F2 z) [# u5 m- \" d上面包含了“输入端点描述符”和“输出端点描述符”。4 d4 C& Y7 z5 s6 {
//wMaxPacketSize字段。该端点的最大包长。
& D. i. n. H6 v& l0 P2 F//bInterval字段。端点查询的时间,* T3 C1 n2 G* q- v
2 d: [7 P5 n* T9 s0 k
为了实现更高速的通信我们修改如下:! G9 C9 E5 d% d1 w/ l; G
  1. /******************** Descriptor of endpoint ********************/
    % e- t3 g0 L# T2 ^' h
  2. /* 27 */) |  ?% {( e6 s  F" ^% l: l2 F) v$ K
  3. 0x07, /*bLength: Endpoint Descriptor size*/2 ]- H8 u: g8 z' l+ ~' F$ r. d
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    " H; x5 w1 M7 e  E% ?
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/
    9 ^1 i; I4 W" r9 u; J4 i7 D5 m
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    $ y% q: [7 u4 g+ q4 {3 m! _, g
  7. 0x40, /*wMaxPacketSize: 64 Byte max */: x; @) l- d) u0 k) ]6 H
  8. 0x00,
    " ^  a/ O4 q" q  E0 B1 }
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    2 ~% L# T! u+ j
  10. /* 34 */% r6 w) W* x$ y3 e4 m/ x! ~4 O* J
  11. /******************** Descriptor of endpoint ********************/
    " o. c0 f7 ]0 n+ k2 S
  12. /* 27 */
    / R9 G, N6 B. \) a1 _
  13. 0x07, /*bLength: Endpoint Descriptor size*/
    # Y' n: e; `0 \3 V1 Q9 s% d" Q
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/0 w: S. k2 V( _5 y
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/
    + F+ l, C7 g: q
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    % t9 z9 b9 ~3 ^8 y) ]2 M
  17. 0x40, /*wMaxPacketSize: 64 Byte max */
    / u. `- c6 }4 X: U! J
  18. 0x00,2 u1 d4 l. G) d4 Y& ~% }
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/& \& ~9 @/ c* I& L$ h! i

  20. $ C7 l/ h$ O& c0 g0 a
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。
    3 ^* x  Z' \: M7 `2 \$ x% b5 L
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
    ; d( d3 O3 j) ^1 g- j1 E) ~
  23. {: E6 Y- Q0 M8 S5 h
  24. 0x05, 0xFF, // USAGE_PAGE(User define)
    : c* h$ }& ~2 A' e. w
  25. 0x09, 0xFF, // USAGE(User define)8 y0 N: z  \$ \  d
  26. 0xa1, 0x01, // COLLECTION (Application)
    9 F$ f1 P9 z5 @8 m9 H' v% T2 t
  27. 0x05, 0x01, // USAGE_PAGE(1)! p) Y/ A* \" p+ J3 i
  28. 0x19, 0x00, // USAGE_MINIMUM(0)
    6 t' k% {. M& c$ I0 o
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)
    : E  k: b& H& Q! e0 X
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0): }8 R( Z  E* G$ {% @3 [, [1 P
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)! t7 t# ~* S# }- i5 s, k5 s
  32. 0x75, 0x08, // REPORT_SIZE (8)
    , A, n! z2 M0 Y  _( O2 Z2 t, f3 U
  33. 0x95, 0x40, // REPORT_COUNT (64)
    . v3 |1 o9 f& g3 d% |
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)
    8 L6 j* l( J" W/ N- p
  35. 0x05, 0x02, // USAGE_PAGE(2)6 E) ?/ x; h# x1 D
  36. 0x19, 0x00, // USAGE_MINIMUM (0)
    3 x/ Q/ n' \6 A" l( J5 n
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)! N- ?$ c- L  R- v! X0 \
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    3 m" A2 I5 T) s8 T8 W
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)5 ]" p4 Q! c# o+ z) e
  40. 0x95, 0x08, // REPORT_COUNT (8)5 B& \+ K, _: E2 B
  41. 0x75, 0x40, // REPORT_SIZE (64)
    1 A# @) E. O1 m  K, n: H
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)
    , }4 [- }& \! D( G) i" s9 Q
  43. 0xc0 // END_COLLECTION
    8 l3 T* x  J. ~; ~
  44. }; /* ReportDescriptor */
      w" f; Q! Q8 b1 b1 a" Z
  45.   N, p& k6 c; O; j
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]' N9 x/ n- q/ A/ Q: [
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]3 S1 |* C) \+ t1 x
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码
" {& V' K4 U2 y
分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。0 y/ p& e# }) p$ k5 \' G) d: Q# r
7 \% K, {5 G0 C1 W- r# ?
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39) k1 F% z9 V% o" i+ J
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 646 g, d$ j9 H; G/ B  {
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28
    , [! ?+ u1 w) x; f9 ^
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码
& |1 H  g$ @0 E' p1 @& G' x% y
三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数
4 G& K) @/ I+ u$ K7 e& N4 O5 Oa) Set_System(void)
- _  X9 r' E* q% L) ob) void Set_USBClock(void)
! [& ?) `' e# }/ ?+ rc) void USB_Interrupts_Config(void)" ^# V" b0 j' o% A1 F+ b: L
d) void USB_Cable_Config (FunctionalState NewState), ]( @  X9 ?% N
特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。+ r" l" @  m8 ]: i

$ F/ r$ @' H8 `  X8 V, }! y$ ^% }7 E
四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。
2 p4 }( L' v0 S打开usb_prop.c文件,修改如下:' {# \: B1 p1 Z  O( g  P' u, y
  1. void CustomHID_Reset(void)* V( \) A2 v  D  e
  2. {
    / K0 M; s' M8 I& o9 U2 W
  3. /* Set Joystick_DEVICE as not configured */
    5 k: H2 o8 I6 p% `! R
  4. pInformation->Current_Configuration = 0;
    ' a) i; n! ~$ k* x% r  [( R
  5. pInformation->Current_Interface = 0;/*the default Interface*/
    6 A7 F) ~% d; v
  6. SetBTABLE(BTABLE_ADDRESS);
    9 @/ f8 G2 N+ T8 i/ t) O

  7. 7 h! ^, M' R3 _5 B* E
  8. /* Initialize Endpoint 0 */( t9 S, L+ o4 N5 \5 ~
  9. SetEPType(ENDP0, EP_CONTROL);
    ; A/ d8 X- w1 g* \2 B4 S' T* M' N& P
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);  L3 g$ m8 ?6 }5 D- i. H
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);
    , |1 m5 U4 F' ~, h5 G* k
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    & a5 R6 t+ V) K( P3 `
  13. Clear_Status_Out(ENDP0);7 q% U% f! T* e
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
    3 C' s' Y2 K6 c  ~, E
  15. SetEPRxValid(ENDP0);& y: E: Z" y3 K9 L' F
  16. 0 `5 U" q- Z" S
  17. /* Initialize Endpoint 1 */1 y- r5 w& a9 A/ |, \
  18. SetEPType(ENDP1, EP_INTERRUPT);8 _% w3 o- @- n1 L
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);
    . G6 U  {2 {5 t' ], o( `8 A; V
  20. SetEPTxCount(ENDP1, 64);
    * P/ W" X. G  M& `
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);
    , s5 T. [2 L# d1 _" s! A
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);9 W8 H& n" t, v4 g% H

  23. : K9 E" t# k9 J; N
  24. /* Initialize Endpoint 1 */6 E) _1 v, z* n' E
  25. // SetEPType(ENDP1, EP_INTERRUPT);" `: n( A8 d% Y8 K6 o( \( |" D; z0 V
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);( ^2 B& I. a" H! J) b
  27. SetEPRxCount(ENDP1, 64);0 p5 q2 @7 h8 B$ V' ~9 D- J
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);5 }: }' |/ E( {; G
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);
    " D4 G) w3 t/ n! q
  30. /* Set this device to response on default address */* @" N4 P2 p* S
  31. SetDeviceAddress(0);
    # v0 `$ g7 w, b1 J
  32. }
复制代码

2 F2 D, O7 a# N. }" X  M+ a) N! F7 T五、usb_endp.c文件$ d: r+ |+ ~! s& |5 f2 p* `
  1. void EP1_OUT_Callback(void)1 n# k3 g! E! {$ q
  2. {
    ( k, L0 B/ F, ?6 ?7 D
  3. 这些写接收代码
    1 {* k& D% V  k5 ?
  4. }
复制代码
1 Q+ y1 P" ?' o2 A' }# }$ V% ]
六、数据发送和接收,举例说明
4 q) V% [4 y8 m; j3 n; i1、数据接收
$ S+ f6 N3 y6 d- }& [
  1. u8 DataLen;) o4 u: [! s; Q; C" N; X: [
  2. DataLen = GetEPRxCount(ENDP1);
    % N6 @  @1 E4 t5 W: q
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);
    9 g  _# k9 A# L5 B# V: h4 q- Y. z
  4. SetEPRxValid(ENDP1);$ g' e( x- ^' i
  5. USART1_Send(DataLen);  V: {( C' h. T& Z
  6. count_out = 1;
复制代码
6 q% U* s* o( r! O" v0 T
2、数据发送( L0 I- k$ K8 [* \
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);- G$ i( a2 w8 C4 R% ^9 q: {8 j8 x6 C
  2. SetEPTxCount(ENDP1, 64); ( {& c+ d- y; K9 z
  3. SetEPTxValid(ENDP1);
复制代码

# ?6 F8 K9 X! K( M7 ]! c9 U2 c5 A如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。; [2 {: R% Y  f2 Z
9 m) a4 m0 e5 P
/ R# M- S4 T9 A. w
(by xidongs)
  I) g' d% N! a2 z+ U6 ?
收藏 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 手机版