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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程; R# u: M5 l0 d$ k

! {) e' U( B  l9 `( @% t& o. k
最全USB HID开发资料,悉心整理一个月,亲自测试
2 C2 i$ ~/ G+ F$ r/ [& D2 h- H! O! \  f6 N/ F6 W
通过STM32CUbeMX建立USB HID的双向通讯实验成功& K0 I! A9 z) U$ F

' ~; u1 e' d, J2 X9 T

# m$ }6 j' V% Q; B' g发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。

5 p) E# q! W$ X' r8 d5 S) c- p
2 U+ X4 Z7 W* e# y0 iHID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在2 f! e/ z: y: h
, L4 f* c" x$ G; r8 G. x% Y- J
一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。
( x* q7 {0 z9 p0 s# V) B: z
$ b5 ]  E- O6 ?- I5 I  G二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。0 d* s, n; G. B; D
/ q. E9 n- y) D9 t0 n8 t6 X
u8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
" t' _, t0 _) j+ e$ w* M0x83, /*idVendor (0x0483)*/# Y+ l% c* f% i
0x04,
5 l* ^4 I' F7 n- M! p0x50, /*idProduct = 0x5750*/. I0 m. U6 I4 |, h, D# D7 |, i
0x57,
0 K" k. v4 c) N7 [//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。
/ D# F2 `+ B- i# Z//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
1 y& g- D6 S+ l9 w. ^' L( f//注意小端模式,低字节在先。3 \( a' m0 k2 b9 I& L' x
//idProduct字段。产品ID号,我们这里取0x5750。
! \0 G" B- E, T& \//注意小端模式,低字节应该在前。
' s, ?5 _$ P: ]+ r8 b4 I( R. ~- C" t9 i% F

: d) R, m, e" Z* N3 Nconst u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下, a0 w6 [0 R0 k. z& c" \+ j9 ~
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */7 O$ r7 F' N) r# H$ s$ |' I. {; K4 K, X
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) *// z7 J- E/ D4 H9 |7 Z: p
  3. 0x03, /* bmAttributes: Interrupt endpoint */
    4 P/ t& S; G0 b& K  a6 C0 f
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */
    , k9 V/ P8 N8 g# g. V4 ~( \
  5. 0x00,
    / I/ R- Z% ]) S" p3 E8 C
  6. 0x20, /* bInterval: Polling Interval (32 ms) */
    / V9 J9 o% r4 b8 d- G2 t
  7. /* 34 */% f2 ]& G( A% w3 A9 T
  8. 0x07, /* bLength: Endpoint Descriptor size */7 H( l4 ^  @+ j9 ]* s) G! H2 J3 M
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    7 k/ G; }& k- a! _; ?% h
  10. /* Endpoint descriptor type */8 i! z, j: n: b! M3 X
  11. 0x01, /* bEndpointAddress: */
    % u4 a( q  e( {: U* a
  12. /* Endpoint Address (OUT) */; R0 }" S7 i& ~5 b# ?5 `+ y1 E
  13. 0x03, /* bmAttributes: Interrupt endpoint */
    5 v0 e! g' q' n1 L
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */
    # ?* `: J1 O. _, _
  15. 0x00,
    & j! B' T# T+ e8 @
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码

9 d( B  N" s+ Y/ p$ z; W上面包含了“输入端点描述符”和“输出端点描述符”。8 U: |+ U7 D6 m+ Z1 W
//wMaxPacketSize字段。该端点的最大包长。7 q7 x$ T. y7 A" Y
//bInterval字段。端点查询的时间,: O; v! e7 r0 \. o! J

# r. `! h# d) H! z为了实现更高速的通信我们修改如下:5 l6 u: i5 T* @* [6 W/ `
  1. /******************** Descriptor of endpoint ********************/. @9 o' H. G  b0 Q" [# I
  2. /* 27 */
    2 ]/ w' E6 b2 B( q4 n9 z
  3. 0x07, /*bLength: Endpoint Descriptor size*/
    8 o1 l. l" u  n. e* {
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/4 H  _- q4 P+ K1 ]5 V
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*// \: I2 s3 i, w) \& m: N
  6. 0x03, /*bmAttributes: Interrupt endpoint*/5 p2 k8 u: I& U0 Q! D* n% x
  7. 0x40, /*wMaxPacketSize: 64 Byte max */
    1 z7 l9 C" W4 l) n0 L& o# \
  8. 0x00,% h9 ~9 {: @5 p, s" C6 m
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/+ G* @/ y. C8 }) }; q2 V) {
  10. /* 34 *// w: a8 X$ i% w" G
  11. /******************** Descriptor of endpoint ********************/' J! k& v! A7 @9 {- e  v
  12. /* 27 */+ g4 _9 g3 p# ]2 {7 p; F) k  O
  13. 0x07, /*bLength: Endpoint Descriptor size*/
    / i: t. w/ U1 u+ r3 B* l; n- M
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/- F2 c3 W9 {# r0 }) E, E
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/' d) i  H0 U/ y7 b9 L" _8 K
  16. 0x03, /*bmAttributes: Interrupt endpoint*/5 s2 V" o- j# e2 r  @3 c( v
  17. 0x40, /*wMaxPacketSize: 64 Byte max */, V/ M* H+ a+ L' ~  s
  18. 0x00,
    ; p  a: P& g7 s( Q$ F1 Z  Q) C
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/+ L. F$ K$ z5 H7 Q% |
  20. & y* _/ l9 D: }) b$ x4 o$ O% I
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。
    6 Y6 ^$ b3 \+ w3 S% G2 e+ g: ^! I
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =) L  O$ V6 a3 Y
  23. {6 r+ X  K% K7 m# t- R  K
  24. 0x05, 0xFF, // USAGE_PAGE(User define)) s( r, m, S5 q4 P4 m( O. |. l
  25. 0x09, 0xFF, // USAGE(User define)' \5 Y7 J, N; `' \1 C" x$ U3 T
  26. 0xa1, 0x01, // COLLECTION (Application)2 C- ]( v; ~9 i1 l
  27. 0x05, 0x01, // USAGE_PAGE(1)) X2 I( R2 K: h3 k) `$ Q% `. b
  28. 0x19, 0x00, // USAGE_MINIMUM(0)
    & H) j4 j9 C& P4 f, O2 d0 X
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)
    ( |: \) x* y8 X+ Y
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    ) P) i# s$ ?. [+ F1 P; Z
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    & V1 q5 r+ {! T5 l- Q" j
  32. 0x75, 0x08, // REPORT_SIZE (8)- j6 I+ D: O3 |' }9 Y. U: M7 s
  33. 0x95, 0x40, // REPORT_COUNT (64)
    - N/ Z: X! u8 O
  34. 0x81, 0x02, // INPUT (Data,Var,Abs): _# C$ k, U! i
  35. 0x05, 0x02, // USAGE_PAGE(2)
    % ^& p' S& }# ]) {& F+ S1 s
  36. 0x19, 0x00, // USAGE_MINIMUM (0)  ^$ A& t6 ?$ H" ^
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)
    ; U$ ~: r' B) \
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0): K8 _5 U: p: ^" a$ r) H$ c
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)1 B& [2 s& A$ p  a1 {8 E: I4 I
  40. 0x95, 0x08, // REPORT_COUNT (8)$ K# [0 \) q1 I: K  x2 T
  41. 0x75, 0x40, // REPORT_SIZE (64)% y! |; L4 Y( ]4 G4 X8 y8 f
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)# }8 t: ^* ]- y( O; t
  43. 0xc0 // END_COLLECTION
    2 j! W1 n, w" K
  44. }; /* ReportDescriptor */
    3 B+ p' N+ c' u1 Z0 m* G( V/ v
  45. # H1 |$ q7 Y4 @  H4 M! e1 a
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]
    . m+ v6 ]6 o; f" _8 j7 I
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]
    ; F; ~, H% ^& L" F! b0 ^5 P' D
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码
1 E, p! i% y5 i
分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。
9 o. k2 k( t1 g: o
! J, V$ q+ B0 J* N9 f5 E
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39
    5 K8 X4 ?- Z+ K. L+ q8 e' d  r% n" e: F
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64. X9 z' O6 q1 _, H
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28: U; \9 a( P4 z; r4 _
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码

$ [% \" \: O$ R/ j5 \, \/ L! y三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数. g" [7 H- {6 k$ ~
a) Set_System(void)! v) }/ s0 @# X4 o
b) void Set_USBClock(void) - R8 H0 `2 s1 P  @
c) void USB_Interrupts_Config(void)' L( @( q$ W0 p4 c! v+ g4 l9 m
d) void USB_Cable_Config (FunctionalState NewState)$ u2 x/ A3 a, u4 m/ @
特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。
& \, E5 ~6 X* L: w& R. e) J
/ K9 x% @( ]" R* e1 y
- [) V, l5 Y1 d8 h四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。
+ D1 a5 e; O7 E' g打开usb_prop.c文件,修改如下:
4 R9 s( M: c/ }" q
  1. void CustomHID_Reset(void)& M' l0 V2 E: U( s( M9 ~- K; a
  2. {
    ) M) I5 }% q$ m" U
  3. /* Set Joystick_DEVICE as not configured */
    ! v% H  E  p% W' ^4 n- k8 |
  4. pInformation->Current_Configuration = 0;% U; D0 S6 l3 @  O
  5. pInformation->Current_Interface = 0;/*the default Interface*/
    ) E' p( X7 O3 g' P6 D5 _9 b. W
  6. SetBTABLE(BTABLE_ADDRESS);
    # b) U, t! E( S5 P# K& G. v; x) w
  7. 7 r: A5 N8 ]/ Z! l& r
  8. /* Initialize Endpoint 0 */
    ( @1 K3 z; ^2 x) Y+ P# T8 N7 K
  9. SetEPType(ENDP0, EP_CONTROL);
    % y" u2 x! u# n* u0 V2 A
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);! C2 L# {: t* f; h5 |" R5 C# T7 {
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);. m5 [. l3 k6 r# ~
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    ! w) z  I( o. a) r+ V/ D2 F2 Z
  13. Clear_Status_Out(ENDP0);
    + `6 F# X$ U' Q% b
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
    : i" J" B6 y. J# a: [$ u, V
  15. SetEPRxValid(ENDP0);9 G& M8 z0 L' t( {( U* q

  16. 7 V* N  [+ b! H) k4 z) K
  17. /* Initialize Endpoint 1 */* ]4 C5 ~- d* H0 ~' v+ K
  18. SetEPType(ENDP1, EP_INTERRUPT);% x9 F% Z* z9 c5 q) e7 }
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);
    " o; z( t0 Z& k1 V
  20. SetEPTxCount(ENDP1, 64);. j/ ?3 t9 y3 q% |3 @
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);
    ( E* d& n6 j; x7 z2 Z
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);
    ; R) Z- q, X" \5 e$ }9 V0 f

  23. & z! t( u! q2 l. I$ n0 u( E5 \
  24. /* Initialize Endpoint 1 */
    1 n+ {* U( n5 U* l
  25. // SetEPType(ENDP1, EP_INTERRUPT);
    & T/ f) X/ w, m2 Z' u! y
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);
    ' X! o; K7 t# g+ `* k% Q
  27. SetEPRxCount(ENDP1, 64);8 p! U! R; S; P9 G- J; p- P
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);
    ; Y* y& j1 y2 _) u1 ?4 |; ^
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);6 X0 A& S. S( x: ^- H' y* u% d' Y
  30. /* Set this device to response on default address */
    3 v4 u$ Y- C' |. m! l( s  Y
  31. SetDeviceAddress(0);. @$ q0 D( K, D5 H& I
  32. }
复制代码

# _5 B+ U9 Y# k5 Z+ o五、usb_endp.c文件9 `3 R! v. _5 {4 X! D& P
  1. void EP1_OUT_Callback(void)2 I+ `( \  f$ ~- U& \- p! o; O
  2. {' D- L  ~! W' E7 o; V
  3. 这些写接收代码1 Y2 [8 Z6 L9 P
  4. }
复制代码
9 n8 Z. y6 A3 t- C8 m
六、数据发送和接收,举例说明
! g: \$ c8 L- |6 I) n& h1、数据接收
1 X4 [; z, D3 G
  1. u8 DataLen;
    8 e9 ]+ [* k! q' q+ I& _: C5 U
  2. DataLen = GetEPRxCount(ENDP1);
    2 p; P; R$ H$ C/ V  |3 m9 A$ l
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);, \  ?+ u& y: `" N' @- b3 Y# d
  4. SetEPRxValid(ENDP1);- P0 k& }+ j& @% E
  5. USART1_Send(DataLen);3 o# U4 Q  _! e2 ]5 g4 y
  6. count_out = 1;
复制代码
6 }$ Z8 X# }! t' b3 s( v4 O. c: P
2、数据发送. q" \7 U  t2 O) q) Y
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);  h0 z6 H" m" h$ N% Z4 }
  2. SetEPTxCount(ENDP1, 64);
    9 v# K# H: t" s/ W+ h6 Z
  3. SetEPTxValid(ENDP1);
复制代码

" ^& U; H, f& u如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。2 S& `/ P6 N1 o  a% {! q9 K" K

4 e# o  l, x: x
; Q% x  h  T; e& s! X
(by xidongs)* P. m9 x" G: ]/ `2 e
收藏 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 手机版