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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程
7 r. r$ H, F! d
! Z% ?# w, h7 B5 O$ X. f
最全USB HID开发资料,悉心整理一个月,亲自测试9 `; m  @$ f0 ^' L0 ^7 l5 G8 V  s+ |

; o/ t  l9 J7 x# n3 M# g0 p8 d9 S. D% s  W8 t
通过STM32CUbeMX建立USB HID的双向通讯实验成功
! P. O  i# Y5 W- M. @# ?
7 |9 {7 C& E# _+ e
* }) x' N# `2 \0 D! S
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。

3 M; G( V1 X  R
- m. i$ w) Y* i# {, vHID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在/ o$ ]' \/ W  i

& e  R7 O# X7 d8 k# h一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。
4 L- D- g8 P  i& e
+ G$ R) N+ h2 m" _/ p! ^! }7 z4 Q二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。
3 P8 l" H6 Z! r; Z- D5 F6 z2 w9 r7 N0 L) g; \6 {5 x+ X8 S
u8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
% P* J, \. i3 O5 q" X$ `0x83, /*idVendor (0x0483)*/, k5 K+ H3 R2 U' r1 c
0x04,( `5 N* L; m0 @, g7 ~& r  i
0x50, /*idProduct = 0x5750*/
0 U8 ?) h  `% V" o0x57,
4 N( H( f$ |- a/ }; ?9 N$ W//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。
7 @1 ^5 I7 |- I' \//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。  F* j: _* C2 ^4 h* q  t8 v
//注意小端模式,低字节在先。
7 s9 J, C$ ]6 Q; d3 C//idProduct字段。产品ID号,我们这里取0x5750。& F- [) Z+ e3 N
//注意小端模式,低字节应该在前。
+ H8 \- i- U) r  Q6 s% q$ A: t. e8 j+ w5 A- W3 r2 ~9 W
3 ^( |1 Y" E0 F
const u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下
& a# C7 x0 s) N  T" x6 ^! r0 H
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */* b* \/ }5 v5 G8 F/ t
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */* t+ V; X9 ?  a' t: H+ J) M0 w$ B
  3. 0x03, /* bmAttributes: Interrupt endpoint */
    : W# I, E" F1 w+ E2 J
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */6 _1 v6 y# E  j' \( U
  5. 0x00,/ t8 S9 D: c" A/ s! u9 n" Q" E
  6. 0x20, /* bInterval: Polling Interval (32 ms) */
    1 m! W/ l8 F+ k& |# o# g/ J8 q6 ^
  7. /* 34 */
    8 A% r4 ?) @# ^6 G* j! u- x
  8. 0x07, /* bLength: Endpoint Descriptor size */2 R" v5 ~: A8 ]" v
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */) [0 q& h8 I9 R, Y5 B2 z; N
  10. /* Endpoint descriptor type */8 A0 s; a1 j) N  x, D
  11. 0x01, /* bEndpointAddress: */" B2 P' e  B+ G) `4 i+ S9 J% W
  12. /* Endpoint Address (OUT) */
    3 ?8 s" K+ |- M9 |. g
  13. 0x03, /* bmAttributes: Interrupt endpoint */4 i  }; F) e9 B1 S" \! S! n' \8 J
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */- B' c( g6 W' x# F" F
  15. 0x00,) b1 P1 l" k$ a+ a, Q
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码

+ i0 u  t# r. Y) K' v, Z0 u上面包含了“输入端点描述符”和“输出端点描述符”。
' z' j; b5 p) I/ c9 [  a//wMaxPacketSize字段。该端点的最大包长。! u. a+ @  w: Z; K& W
//bInterval字段。端点查询的时间,; g* n8 a; h# v7 c$ U2 n

- \$ X' i* F0 R) p& d为了实现更高速的通信我们修改如下:
" U5 P) S0 y0 Y& L5 q7 ~0 _
  1. /******************** Descriptor of endpoint ********************/3 q8 E/ a  T" k
  2. /* 27 */
    6 e4 R" `* j8 M& C# m* t
  3. 0x07, /*bLength: Endpoint Descriptor size*/5 z# p/ R) H- T$ l6 u
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/! m* G( y6 b( l- `% E( Q
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/% f( ?& V* ?$ |' h5 L
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    , K( V% ~! G, A- ^3 o, L# @
  7. 0x40, /*wMaxPacketSize: 64 Byte max */8 |% f, O6 V) G9 s4 ]
  8. 0x00,
    ( j7 G# V  y/ v* T% f9 X9 Z3 f8 `4 S
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/* X' C8 r6 F" @7 L) M4 i
  10. /* 34 */. C9 f( |- C  t5 a5 W) _
  11. /******************** Descriptor of endpoint ********************/+ H2 y& V7 k/ ?0 L0 d
  12. /* 27 */3 ^1 U# P4 j+ R$ ~4 W& E/ O
  13. 0x07, /*bLength: Endpoint Descriptor size*/9 W& j- R1 \' U% N0 {; z! q0 f2 q
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    7 C4 k9 z1 L# O! L2 W+ L
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/* Q" q3 \& n+ U
  16. 0x03, /*bmAttributes: Interrupt endpoint*/& I% [9 ~" D6 d- {- g, C4 L
  17. 0x40, /*wMaxPacketSize: 64 Byte max */! g' w4 T5 }5 {# ?$ Q# x
  18. 0x00,
    & L2 Y6 {) \* V( w2 C
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/6 z5 Q' K3 g2 H: t) D1 w' s

  20. , @0 d3 W; O, q5 `
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。6 `' o( _& F4 F3 }
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =  H, R4 i8 r* c
  23. {
    6 v# E2 r( s: p! G4 t
  24. 0x05, 0xFF, // USAGE_PAGE(User define)
    " z- n* I; q6 T: H
  25. 0x09, 0xFF, // USAGE(User define)# t: I& x2 |# D# {7 f% t( S( A4 e
  26. 0xa1, 0x01, // COLLECTION (Application)4 m/ v; [! b/ v7 U' l) t
  27. 0x05, 0x01, // USAGE_PAGE(1)4 E2 v" z9 _2 g, v
  28. 0x19, 0x00, // USAGE_MINIMUM(0)9 n2 ]% b, j  I2 O
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)- k% t( p3 i1 p2 t* c
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0); R& `- x" p+ D3 b
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    9 P1 k! ~& ^( L: ?( X0 P
  32. 0x75, 0x08, // REPORT_SIZE (8)
    0 d& M$ P! {/ k. }1 J! J: i# |7 d
  33. 0x95, 0x40, // REPORT_COUNT (64), @% n: W2 R$ f, q
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)
    " \) W9 L& J8 s* S1 s1 k% o
  35. 0x05, 0x02, // USAGE_PAGE(2)
    5 b7 u. [8 S6 s4 p
  36. 0x19, 0x00, // USAGE_MINIMUM (0)4 G* Q) q( [" N! U+ o; \: T; C! g
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)
    6 U; S7 [' o# }/ T
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)7 T5 S: z& V! Y
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)' }0 ]3 g( _7 @. L
  40. 0x95, 0x08, // REPORT_COUNT (8)
    ( F7 g5 I( c/ W
  41. 0x75, 0x40, // REPORT_SIZE (64)
    ; L  S- X# _+ {) W' D/ ]
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)
    : c" g; t8 |* S2 Z. W
  43. 0xc0 // END_COLLECTION
    0 a/ D. I0 A& M  \8 |4 B/ u
  44. }; /* ReportDescriptor */! m% A$ d* A# A
  45. " x) c8 K7 B9 h$ m, U* k
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]
    $ H9 N: G/ N& D) J* s) U& a& C
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]5 x8 A* Y4 O# ~) v, Z5 T
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码

' _$ G# L6 i; y/ J: }8 N( r分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。% k2 ]' i: L- A1 T

1 z# r; g- l+ H* E
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39
    1 G/ J  w$ ]3 q  c: Y
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64+ \% j0 R' I7 H- e& y. V
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28
    . r: u" y- w% _; t) J7 S
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码
0 o, u+ _- e8 g: A7 y' J/ o* X
三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数
5 V7 m. g" a! r9 L  Ra) Set_System(void)  W8 |- X% Q1 z( Q, m+ z! P  w
b) void Set_USBClock(void) ! r( [& c/ D1 P5 B
c) void USB_Interrupts_Config(void)
/ Y% V; G9 P% d5 _4 y$ vd) void USB_Cable_Config (FunctionalState NewState)7 A$ ?. d7 l# J. F, ?
特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。5 S; g$ J) c7 i; u/ i( I
4 o' @& }9 d. F8 N7 d+ i$ v# J, l

1 x$ j. a" e( F四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。0 i9 J! X- m5 H+ F( F6 a
打开usb_prop.c文件,修改如下:
5 D. n4 y' e: P% q% [" E4 @
  1. void CustomHID_Reset(void)/ [; B. I4 z: [3 P
  2. {
    + R. L: Q. p% T/ [& v4 W
  3. /* Set Joystick_DEVICE as not configured */$ X5 L7 ]4 f! y. _- D
  4. pInformation->Current_Configuration = 0;
    % |( }1 \) I! ?: d' @% F
  5. pInformation->Current_Interface = 0;/*the default Interface*/. ?; O5 {5 q1 H' J4 h
  6. SetBTABLE(BTABLE_ADDRESS);8 {: L8 G6 t, P8 n# M
  7. 6 T# V* b+ w( C! P% e# F3 \6 J
  8. /* Initialize Endpoint 0 */
    . N( i' _$ j' Z1 x
  9. SetEPType(ENDP0, EP_CONTROL);
    2 m1 ~6 [( a, g0 D( Z9 b. w2 j
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);
    ; i" L. R" ?, b- q4 q
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);- }2 N; _1 o& n% f' O
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    5 W. H4 n( X/ x# ]6 V
  13. Clear_Status_Out(ENDP0);) q. v! h  n3 E4 q  f. r
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
    # c, A' |7 L$ R" f2 N  t
  15. SetEPRxValid(ENDP0);  n. y* L6 I0 G2 |$ d# |

  16. ( J; R; B' E3 m; \" o
  17. /* Initialize Endpoint 1 */* s+ ?3 b! c8 W) y# f9 q- o
  18. SetEPType(ENDP1, EP_INTERRUPT);, V; _4 b& U" r( {8 Q
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);
    8 g% \$ L+ u8 s+ g
  20. SetEPTxCount(ENDP1, 64);
    * C, q& o9 U, o% a9 P
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);! `9 L" H' R! l9 b) `0 H& p
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);
    5 _+ P4 w* W/ Z+ f+ d) X

  23. / U% d+ k% J/ A1 j6 B) `+ a' |) N( M1 j
  24. /* Initialize Endpoint 1 */
    # Y1 t: p; G' u3 u( |
  25. // SetEPType(ENDP1, EP_INTERRUPT);
    ; g1 P6 a, \) a$ V: o7 G
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);
    ' m  z. c2 \7 G" T
  27. SetEPRxCount(ENDP1, 64);; R  O; {9 U  T( p7 B/ @1 T
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);
    4 E: K: M0 E" }
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);& p, n9 Q. \6 G/ H  V$ M$ N
  30. /* Set this device to response on default address */
      M' D. L  _3 b* d* g
  31. SetDeviceAddress(0);* S& A$ D- ^! O4 j8 S: r. _
  32. }
复制代码

# H7 I1 H1 @! o五、usb_endp.c文件# R0 ?+ M+ I( W* Z2 d
  1. void EP1_OUT_Callback(void)
    / A! g+ ]2 C' L1 u
  2. {
    ( X& i5 d3 ]7 u2 X) m  p
  3. 这些写接收代码5 @+ B# `4 n  ~, ]
  4. }
复制代码
: j4 T& w8 `! n
六、数据发送和接收,举例说明
5 \6 N+ ~' d) V- V- Y1、数据接收  Z6 i$ X+ j9 @) w. j; }
  1. u8 DataLen;4 d* ]! l9 U8 D/ G7 p; [
  2. DataLen = GetEPRxCount(ENDP1);5 z% e' M& e7 u5 ?
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);
    - x! y) i' m" R
  4. SetEPRxValid(ENDP1);
    ( r5 x/ g; ~. d1 G: @1 L, [. ?
  5. USART1_Send(DataLen);
    4 y/ U" @- K) b+ g
  6. count_out = 1;
复制代码

  x: E: a) B: G8 Y2、数据发送7 s) r+ ]( F' @: s# l
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
    # A' ~+ M* `* L1 `! v5 h
  2. SetEPTxCount(ENDP1, 64);
    . \" ?" o2 `9 g9 E8 @% X2 M* u6 q
  3. SetEPTxValid(ENDP1);
复制代码

8 S2 f! |1 A7 C9 h9 G如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。. o4 n$ K, ~- A/ G
' r  ^5 x$ D5 T3 z3 ]* }' V

! \: q0 a. g1 Q8 {( s/ J(by xidongs)" g/ d1 w3 I, @2 n* s$ {2 [9 l
赞 收藏 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 手机版