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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程
# x& A/ v. C* k7 N/ ]8 h+ Z2 I% M- g
7 u9 R3 X# D. w9 ]* s8 U1 e" G5 v
最全USB HID开发资料,悉心整理一个月,亲自测试/ S" ?0 K9 U0 J9 T; x; F& X* Q5 v

7 H+ h, U& B: [
通过STM32CUbeMX建立USB HID的双向通讯实验成功& p% Q& E: C- O! Y2 |

" b" o' f1 P" h6 m  q' x  R
3 |7 K( {: G1 l
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。

5 F* E) Y2 `4 P# m# l2 d
) G$ [2 `8 f; ^* L; d) hHID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在- C" f! l& d( A: @$ ~* ^0 F

2 z2 f6 }7 g0 r$ W8 k一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。
, K: P3 S, t' Z2 R. b. a) ^- F" R* t# V# S! Q( A/ `- L0 v
二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。5 W6 O9 q2 k( F) H9 _- [% Q

- r; Z" \4 i$ M9 h% Wu8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
" P; g7 _$ l7 o7 T+ ~' V0x83, /*idVendor (0x0483)*/
1 ^2 ~7 A7 e0 u0x04,: I  j. x6 c; u" g8 u2 {6 t5 }8 Q
0x50, /*idProduct = 0x5750*/
# q! \1 a8 J5 j0x57,
* K# L# z/ |2 p5 l//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。3 M( @- A/ K4 F8 F" O: M
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。- L$ B/ q9 i8 v. ]( g  x) R
//注意小端模式,低字节在先。
% u) o) j+ g) G' X" B* e. h//idProduct字段。产品ID号,我们这里取0x5750。
2 f. r1 W" o3 {6 L! E. q//注意小端模式,低字节应该在前。
2 l) L6 H0 {  _9 z4 f8 @8 u" {0 G

/ V6 e( W" z+ Hconst u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下
6 N0 a7 q! y8 a" {
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: *// Q8 w( o1 {# H7 s
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */' [, a& d  @6 Q. V0 `. e
  3. 0x03, /* bmAttributes: Interrupt endpoint */
    9 h7 I7 F" q( z
  4. 0x02, /* wMaxPacketSize: 2 Bytes max *// i' `  l  c( [
  5. 0x00,
    & Y/ a. U9 F/ P
  6. 0x20, /* bInterval: Polling Interval (32 ms) */
    . V6 H1 I# b+ x1 a1 K2 g
  7. /* 34 */
    ! ^$ A2 ^, v8 K1 V* B4 ^2 L/ Z
  8. 0x07, /* bLength: Endpoint Descriptor size */" d, T8 m% R  `9 w
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    - ]% R, w/ b9 k5 M3 m, n2 w
  10. /* Endpoint descriptor type */- N+ b) M# \8 f7 \
  11. 0x01, /* bEndpointAddress: */
    ( v* F, @$ N$ r4 }9 r
  12. /* Endpoint Address (OUT) */
    3 O: q$ Y. e( @8 ^2 ]7 M5 e
  13. 0x03, /* bmAttributes: Interrupt endpoint */
    0 p3 o6 |, r% \" @3 ]1 I
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */* F( l' x6 z4 G; Z/ r/ d: {
  15. 0x00,
    , K' D5 a) \  W9 f
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码
9 W6 j) t9 E9 Z0 E  ?0 ?
上面包含了“输入端点描述符”和“输出端点描述符”。7 Y  j! f4 s, ?$ B1 S
//wMaxPacketSize字段。该端点的最大包长。
" y. `0 h, m$ x  F; i  _//bInterval字段。端点查询的时间,
9 m3 O! }& e: t1 H, L0 ~. v: E
5 g( V" P; J6 x; w+ r为了实现更高速的通信我们修改如下:
5 u. V. m! X2 P# V- N; X. y
  1. /******************** Descriptor of endpoint ********************/
    6 D/ ~- `9 T- w7 G! w
  2. /* 27 */
    / G: f+ U6 J. J) U
  3. 0x07, /*bLength: Endpoint Descriptor size*/) R& |. [# T5 c) D. ?& R
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/; p& x) ?' `8 Z3 W8 c3 p: _
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/; x% \3 B( \! q1 g% f4 B0 G2 I
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    1 \' p; V: e6 y7 {
  7. 0x40, /*wMaxPacketSize: 64 Byte max */
    % ~2 F. ]) o* k2 ?
  8. 0x00,2 f: ]& a2 e8 L$ J
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    8 C! W  L4 v2 k) b* G
  10. /* 34 */
    6 Q9 `7 U2 J/ e* j7 v1 Y
  11. /******************** Descriptor of endpoint ********************/
    + ~* E- v+ j& d- r" L0 D
  12. /* 27 */
    . X8 s8 i  t: A, P: f2 X
  13. 0x07, /*bLength: Endpoint Descriptor size*/
    ( U/ i$ {, J+ C) s( `7 ?
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/% p1 s0 x9 J# Q1 F
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/
    0 ?: S3 J; K1 g- b' f) G
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    ( H, A3 s$ w( f, u5 s
  17. 0x40, /*wMaxPacketSize: 64 Byte max */6 [$ [) t% V' f1 N8 p( h
  18. 0x00,
    5 ?( L+ V! l; c2 |) j& s6 u- ~
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/' w! {: {  a# @; h* z' K0 W

  20. , A9 y3 I$ a+ p9 }$ B5 f- f
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。! x. G' q2 N6 \+ |, Z
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
    0 O6 j( f5 f; P" c" A- H. O' z: c
  23. {
    ; K8 `0 s1 `" K0 D0 s# F
  24. 0x05, 0xFF, // USAGE_PAGE(User define)& s% {; r; c2 ]: U+ @
  25. 0x09, 0xFF, // USAGE(User define). P# G; ?2 F8 o
  26. 0xa1, 0x01, // COLLECTION (Application)( c' H+ T- s8 B- i8 f: U
  27. 0x05, 0x01, // USAGE_PAGE(1)
    : _$ S# Q5 B+ u0 v! N
  28. 0x19, 0x00, // USAGE_MINIMUM(0)7 q- d6 L) N' D6 g8 a1 r
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)6 l7 {+ _% B% {  ]0 c' c1 g; {
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    6 d$ q/ x% X; V* ^+ P4 c$ n
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    0 K" h- q: k! x- y0 g2 M
  32. 0x75, 0x08, // REPORT_SIZE (8)
    ; K# z; U+ c5 u7 @) A, e( \
  33. 0x95, 0x40, // REPORT_COUNT (64)
    ; M& U* X, |( ~% r9 D1 c7 V
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)
    - s0 i4 o8 Y7 Z. `; |
  35. 0x05, 0x02, // USAGE_PAGE(2)
    0 k7 L, w; M0 U, z2 E6 C  ?: z0 ~
  36. 0x19, 0x00, // USAGE_MINIMUM (0)
    + j6 ~9 Y0 n8 w+ \
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)
    , {: [9 s+ f, U- a- G* g. |
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)" h* G, ~' S" X4 R* E
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    - {9 Z+ ?+ L: ^7 W8 M
  40. 0x95, 0x08, // REPORT_COUNT (8)
    7 }+ W3 G! N  M) ^, I, b
  41. 0x75, 0x40, // REPORT_SIZE (64)
    2 b9 r) V4 F2 v% A+ Z5 o2 p2 }7 A, t! y
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)
    - O% S& M/ f3 D$ r
  43. 0xc0 // END_COLLECTION% g1 P$ K! r( ]+ R$ g
  44. }; /* ReportDescriptor */
    1 M- y  B9 ~/ B5 k
  45. 9 |3 G0 v' ]7 K, R
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]
    * }! O% M' p) L( p$ h3 V! }
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]7 s) ]! ?% O( ^; ?: ^* l; I- O
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码

  I4 T6 ~! O. l, [( h# I# e分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。
) n1 [7 V9 ]8 V6 H  K; a  i& f2 U* {1 f% K" E! E* r# S
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39
    9 ]9 Q) `% }# x- V2 m+ m* P, }. Q, K
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64
    2 \# F+ V" g( k8 ]$ L
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28$ U/ w7 L- z4 X0 M. F# A
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码

# j3 a$ U5 _. \9 }三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数1 f% n" l' A' y+ l/ p
a) Set_System(void). Q- V; C8 z( F8 M2 Z* `5 p
b) void Set_USBClock(void)
4 g4 }: U" l0 |! I3 ^. h  Jc) void USB_Interrupts_Config(void)5 A: g7 }# P- B( A# v
d) void USB_Cable_Config (FunctionalState NewState)
& U+ o, m: a: ]. X* y3 K& o特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。
; B7 s5 I6 o( g, ^+ Z4 a' o! g9 J5 J' X/ i) C; P3 E
* C* o( @! s: d
四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。7 F% ^7 o8 C& r2 l. N
打开usb_prop.c文件,修改如下:
; F* J- R1 S! }$ \7 c  e
  1. void CustomHID_Reset(void)! N  F2 g9 {1 F
  2. {
    + \! u4 Z- W* r7 Y/ b2 A
  3. /* Set Joystick_DEVICE as not configured */
    , R9 @* ?" h/ h& c9 [
  4. pInformation->Current_Configuration = 0;0 P2 @: K0 R7 l8 X# N
  5. pInformation->Current_Interface = 0;/*the default Interface*/
    % }6 t- M' N7 n4 \) ?' Q
  6. SetBTABLE(BTABLE_ADDRESS);
      c; B1 u) {9 ^$ {5 g8 L' F

  7. 8 v3 Z- J: d! Z& `8 L8 P6 l
  8. /* Initialize Endpoint 0 *// O; ~3 w/ h  G1 l/ V6 B5 g
  9. SetEPType(ENDP0, EP_CONTROL);
      N, x2 B, H; P. {
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);
    : H/ e& o" R, t3 }2 ]- ]
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);
    ; }, \4 E3 }; X" g
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);1 T) T  T! j: j( }3 a
  13. Clear_Status_Out(ENDP0);' ^/ z7 R5 c  e, `9 R
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);; r, k/ M" _& x$ F
  15. SetEPRxValid(ENDP0);! Y- ~6 \; H& Z, q! Z! P* p
  16. & c$ E/ t3 p3 _! F9 Q7 t
  17. /* Initialize Endpoint 1 */! E/ D( V2 \& B( Z
  18. SetEPType(ENDP1, EP_INTERRUPT);) V$ q/ q" i3 L* J/ S
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);* o& h( I. H, I9 _) T, z
  20. SetEPTxCount(ENDP1, 64);8 H  |. i+ n; m6 d
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);$ P. O" {/ W8 d9 Z* S. Z
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);3 N8 n7 s( t$ A* x9 ~& t
  23. " d$ Q5 _& p0 p  E
  24. /* Initialize Endpoint 1 */
    ( y/ s: v3 t8 j5 |' e. |/ `
  25. // SetEPType(ENDP1, EP_INTERRUPT);. \: b& d0 w% q+ Q" a
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);) n4 q3 Z( H0 Q
  27. SetEPRxCount(ENDP1, 64);4 |4 p" `3 n6 H' x
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);
    # C, p2 j4 {) f- o  s
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);7 T# ^+ {4 \  J6 T
  30. /* Set this device to response on default address */  D; o! v/ t& ]6 z3 K6 w
  31. SetDeviceAddress(0);& J. P  g& |) [0 D" o) \: z, Q/ w
  32. }
复制代码
4 j- H: _: G, c6 G8 J0 d$ O4 ]
五、usb_endp.c文件
7 i6 t: Y& f2 B. r! v5 B8 O  r+ W
  1. void EP1_OUT_Callback(void)
    1 d  T$ ^8 M' C( S: k1 j
  2. {
    : h, g& h0 l* O3 v1 X/ b. b
  3. 这些写接收代码- q2 z: L# X8 h/ N. r
  4. }
复制代码
. m. `) P- o/ k4 x9 M
六、数据发送和接收,举例说明+ v/ m7 s8 S  d. d" n2 ^* Y7 O6 y6 r3 U
1、数据接收
& `( ^0 [$ _. z- M+ h$ z
  1. u8 DataLen;
    7 z; p6 z  s6 B& f
  2. DataLen = GetEPRxCount(ENDP1);
    4 g; ]/ g) \, r1 i2 R0 _
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);
    : r; P6 x8 r! B7 n7 G. h; K
  4. SetEPRxValid(ENDP1);
    1 w2 `6 L  x! O: x0 Y& @
  5. USART1_Send(DataLen);- ?. o( _: ]* A- i6 r! j5 H" {
  6. count_out = 1;
复制代码

8 t( s( S- M+ f" R& [- w$ c; M2、数据发送
$ N( s! @: A  s, G9 C% y
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
    9 Q7 p% L, Q; d: Z% a7 f# e
  2. SetEPTxCount(ENDP1, 64);
    0 _8 q4 Z* {% v' I0 w
  3. SetEPTxValid(ENDP1);
复制代码
, m; e5 N& j$ [; Z
如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。* C: \4 m) u' K5 c

/ j! |  X6 w+ ?6 c) ~
, `" j9 _. s& z$ r
(by xidongs)
( v# d; c5 S3 K# ^2 L( g
收藏 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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版