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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程
0 _. G$ ~- J- O; k2 H, Z* O- G- B2 W; |- Z1 q
最全USB HID开发资料,悉心整理一个月,亲自测试- q* r0 S7 g9 A( Y' j  A

5 b" D' [2 ~  x
通过STM32CUbeMX建立USB HID的双向通讯实验成功. Z$ w1 u3 G# M
4 L- b# f# T% W1 r+ w
5 i" _7 j9 x. m3 T; p
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。

5 H& F( A5 J9 W0 s' \3 s. N- ]: e& j0 k6 h) n
HID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在
8 ~+ D1 r5 [& N% |. M6 A- E& A8 k8 q3 k1 J6 g+ h7 V
一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。( n) g$ \3 N- ]

: V- v) V' _" t; D二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。" \& {8 m2 m* k  J4 \5 j0 R

; c6 y4 k0 U, o  iu8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
/ H& t7 w2 G: }$ t0x83, /*idVendor (0x0483)*/
( m2 h/ M3 R* W2 n4 J7 s0x04,* d$ x, q" U! f2 G
0x50, /*idProduct = 0x5750*/- T$ p+ n2 o0 K9 J
0x57,
- F! [$ i! d! i2 a) }- f. B//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。/ _/ a6 s$ p; E7 q
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
+ r# ^3 K5 Q0 Y//注意小端模式,低字节在先。
- a7 T" W# b3 n' X2 y( o, z; y//idProduct字段。产品ID号,我们这里取0x5750。2 }2 \* U& E4 G2 o6 a4 ?
//注意小端模式,低字节应该在前。( y4 t: s! @* J

! |" T( {: [" L* g1 ]% u9 b& X
+ G: X9 i# f4 n$ S. m; yconst u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下3 w/ q, x( P) H! ^' ]8 i9 Q; a8 b
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */" Z1 w1 L9 R# B7 p' {. V( B
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */
    0 l/ Y' m, D( V2 A$ a/ j' d
  3. 0x03, /* bmAttributes: Interrupt endpoint */  F% S+ r, s  S0 c! g* |
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */) W: L1 Y4 G; ]
  5. 0x00,- J& V2 Q$ l" f/ k9 {1 I" I
  6. 0x20, /* bInterval: Polling Interval (32 ms) */! P' r3 m$ v+ W) N0 |9 @3 S; g' s
  7. /* 34 */
    1 d4 y1 p& |) u
  8. 0x07, /* bLength: Endpoint Descriptor size */6 q8 [1 f0 D2 I& ~9 _
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */; Q& ~6 D. |# H# z# n" L5 j
  10. /* Endpoint descriptor type */
    & g* X, L/ K3 M. I4 P. F
  11. 0x01, /* bEndpointAddress: */
    * ~. Q  A3 h0 D: U( j. E
  12. /* Endpoint Address (OUT) */
    / V+ K; G/ ?. G  P$ H% ^# ]  E$ n  a
  13. 0x03, /* bmAttributes: Interrupt endpoint */
    . Y8 }+ M" c4 j6 `3 X7 F
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */
    3 F' L) G4 x; B
  15. 0x00,
    + Z& a2 q- T. j+ U
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码
3 F) j4 X# ?0 n) M# i
上面包含了“输入端点描述符”和“输出端点描述符”。
- d7 t( a9 u  k% z# b9 C//wMaxPacketSize字段。该端点的最大包长。# e* F8 H% w+ m' G! T5 e
//bInterval字段。端点查询的时间,
  ]* Y/ x8 z) T2 _( F
8 V$ m' _2 _; f3 f1 s/ B为了实现更高速的通信我们修改如下:
8 N9 [) o3 j" K4 Z- \8 h7 j: b
  1. /******************** Descriptor of endpoint ********************/& k- ^9 [4 {. j, n
  2. /* 27 */9 \- \0 H: q, |8 A) R* W
  3. 0x07, /*bLength: Endpoint Descriptor size*/
    , c" O$ F$ r, K
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/$ ]9 p$ g2 B1 i+ k7 e/ p
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/% r: H! R  c2 [
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    & J, d9 t. H+ M  y3 p' b
  7. 0x40, /*wMaxPacketSize: 64 Byte max */0 o1 w4 P# I- s$ d
  8. 0x00,! S& |6 k/ {8 d# t1 u
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    # l) X& D: E3 k2 C
  10. /* 34 */
      M% o! N; P& d4 c
  11. /******************** Descriptor of endpoint ********************/
    ( b6 [1 E- Q' F6 A/ c! K& f5 `& |
  12. /* 27 */
    , q" J: Q  a; `7 T9 M' x
  13. 0x07, /*bLength: Endpoint Descriptor size*/. l& [7 Q# c* z$ W/ j# b. l5 [6 c
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/( ~( m& K. P6 H2 I" s5 O6 e0 s8 J9 g
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/; G/ w2 Z% K( B6 ~( S; I6 G% P
  16. 0x03, /*bmAttributes: Interrupt endpoint*/
    2 ^6 M: ?. F! o9 s) J
  17. 0x40, /*wMaxPacketSize: 64 Byte max */
    - A, @1 a1 a1 P5 O7 r8 |
  18. 0x00,* t! ^7 ]& o* g* d2 n% b2 }
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    : Z# V% h% i/ k2 X' f

  20. / E% f' i: D1 v' a8 I5 z( a9 S
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。
      [' F8 Z$ L/ P( X( {
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =1 v2 a! i0 e' z, X
  23. {
    ! B5 h  ?' U2 g, _; f3 e
  24. 0x05, 0xFF, // USAGE_PAGE(User define)+ J  `; b  a2 a, }1 u  T) L7 m. y& u# X
  25. 0x09, 0xFF, // USAGE(User define)) B9 D1 `: N' o3 M1 W
  26. 0xa1, 0x01, // COLLECTION (Application)
      \4 Q- v' ~- k% m0 |. V
  27. 0x05, 0x01, // USAGE_PAGE(1)
    5 z) t, @6 Z9 s  j  T) n
  28. 0x19, 0x00, // USAGE_MINIMUM(0)
    7 |, j1 d  x5 Z
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)
    3 h0 Y2 u0 {3 n5 W: B5 i
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    - S. `$ b+ X0 i+ i. `! ~
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    4 Y9 k. K% A7 U( Y3 A. G, @: g
  32. 0x75, 0x08, // REPORT_SIZE (8); }/ F+ u! }2 C% j, K" C
  33. 0x95, 0x40, // REPORT_COUNT (64)& q  _6 S: Q4 P. L/ V
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)
    ' A# i( I( F+ K% q; e1 H
  35. 0x05, 0x02, // USAGE_PAGE(2)& w; m5 c8 v8 X2 ]  L: e
  36. 0x19, 0x00, // USAGE_MINIMUM (0)& K) q# |4 p  e+ X- [
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)( E/ d. B- S" R6 X. h7 q
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)' v2 }# e/ d) K& V  V: f. D
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)/ s; g8 ]; J& g% Y8 r  B. y
  40. 0x95, 0x08, // REPORT_COUNT (8)
    / K7 i6 L7 g; {! ?0 }
  41. 0x75, 0x40, // REPORT_SIZE (64)+ H6 |7 _2 I8 I
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)4 r; }; t9 V# K. v* I( i* Q1 l" q
  43. 0xc0 // END_COLLECTION
    & c/ ^3 k4 W2 ^1 e, t" _
  44. }; /* ReportDescriptor */3 h) [+ _) @; K8 t, c! x
  45. 1 \4 l7 D7 z( K8 M
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]
    7 L/ e5 q) d) I$ {
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]& g% {0 D% r" f: S- g0 J, b
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码
* C. O4 ^) m7 I2 g: m+ @0 J
分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。
* W6 Q+ R8 S" F; Y9 W# u
" t$ G1 t- b6 I: N" R8 t
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39
    : a) e0 d$ \3 d" P7 E, l9 r" X
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64# j  C7 P+ k' O+ j6 T
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28
    ! C; T- ?3 T0 C. z; o2 u
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码
2 f% p3 G! G! _1 f
三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数
$ }4 }; \4 l* b* `. ra) Set_System(void)7 Y, L3 s  B' b, M- A
b) void Set_USBClock(void) + l4 B) {9 d1 ~+ o
c) void USB_Interrupts_Config(void)
% I; }. b0 f6 c% Nd) void USB_Cable_Config (FunctionalState NewState)
/ S. [6 L1 a/ }  ^9 z" R- i7 R特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。
, \# C( I% T1 `( [( X. E0 f5 O

/ P7 E! F& R8 t0 H9 ^四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。
' k# I% |0 F6 q$ C打开usb_prop.c文件,修改如下:
5 A  e! Q# v7 s( G9 n7 v
  1. void CustomHID_Reset(void)& S' }  I2 r6 A% \9 y, B
  2. {  y6 [/ I3 ^' n! O3 U4 }! t
  3. /* Set Joystick_DEVICE as not configured */
    2 ?9 T$ q% K  R! x) Y+ b2 c; V
  4. pInformation->Current_Configuration = 0;
    . |" ~; h  }& a' Z
  5. pInformation->Current_Interface = 0;/*the default Interface*/( g1 P' V% I5 K8 Y5 `+ q( j% l
  6. SetBTABLE(BTABLE_ADDRESS);
    ( z4 S. P5 T6 ~/ S3 K

  7. , ?- C5 c( U8 \: z! L
  8. /* Initialize Endpoint 0 */% j& j2 x7 _- ^; f& F
  9. SetEPType(ENDP0, EP_CONTROL);
    # y; ]2 E  O( {$ j9 _
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);9 }5 L" G  U1 b" K* }" B
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);
    * T, W, b1 ~' H
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    3 |( G. Z, d3 i7 s  X, S8 @/ e
  13. Clear_Status_Out(ENDP0);
    % h+ x7 h7 Q+ l& A
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
    $ u  g! f; ]' v# d0 R. d  f: b/ C
  15. SetEPRxValid(ENDP0);
    : `' {* G+ R$ F. A

  16. , x4 c% {6 ]; J  T* b
  17. /* Initialize Endpoint 1 */
    ! n; P- A, s( @. ^* S
  18. SetEPType(ENDP1, EP_INTERRUPT);
    5 q! f# V. H) G- u3 q
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);
    ( {$ b& E6 @6 M& o/ j4 r# i
  20. SetEPTxCount(ENDP1, 64);+ |9 f; ?( w7 Y! J% E
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);
    ' X. h- q1 e4 b! I  T& r# k! X+ P
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);, M; C, T6 m9 H; r) O

  23. ! T' R% O8 z* @" y; b
  24. /* Initialize Endpoint 1 */
    ' h1 w7 ?' [# T: B6 p
  25. // SetEPType(ENDP1, EP_INTERRUPT);& i' T3 Q* z( B$ @& E& l9 k
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);
    & _& ]& W+ g2 x' O: Y
  27. SetEPRxCount(ENDP1, 64);
    2 }, n( d3 F* h- T& W! }) i
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);' j( ~5 S; Y; Q+ Y) \" D
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);
    ; `; e+ |4 m: m3 K6 v: f
  30. /* Set this device to response on default address */- t/ r0 w- `1 t$ O
  31. SetDeviceAddress(0);
    0 n2 v0 ^) Z8 F1 ?2 p( P1 }
  32. }
复制代码
9 T0 A0 {9 U+ b1 i
五、usb_endp.c文件3 p) H; e8 \2 y; C8 R) J' M; x
  1. void EP1_OUT_Callback(void), a% |: W& b5 L  B+ r" |
  2. {
    5 X% K! q# [7 T; z# ~2 y
  3. 这些写接收代码- w0 X3 @5 a: T: E
  4. }
复制代码

. I$ J/ T# i' U, X六、数据发送和接收,举例说明6 p9 }2 [. a6 c* K
1、数据接收
4 ?7 v/ n! \  P/ @3 M7 a
  1. u8 DataLen;
    - V5 q/ x/ T# P( s
  2. DataLen = GetEPRxCount(ENDP1);
    " P3 }& n  P; c! o, K; A( C. T
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);
    ! M2 b! @! h1 E
  4. SetEPRxValid(ENDP1);+ e5 ?+ \! v1 K8 Q5 e
  5. USART1_Send(DataLen);/ E6 ~2 j! o- _+ v6 {: W0 ^3 o! ~9 j* B
  6. count_out = 1;
复制代码
" d% u! R0 r( e, D
2、数据发送" m: u% U4 _* d  [
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);& f5 z2 a5 Q4 Z, v5 x' j( r1 ?0 G
  2. SetEPTxCount(ENDP1, 64); : M+ x  ]/ W) H1 b) w
  3. SetEPTxValid(ENDP1);
复制代码
/ h! S% ~* ^" L# a) w0 |
如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。
$ J" g6 c8 `; k
6 ~3 E# l. X, E6 |5 H  y/ W

! S! A/ k. s" G/ i(by xidongs)
, }' G. ~, z9 x8 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 手机版