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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程! d. {/ d% J; n& |
0 ]8 y, a2 w& ~3 x/ r# m
最全USB HID开发资料,悉心整理一个月,亲自测试
' W6 F2 h: q/ `5 ^; U4 H- {- h# S
& t& @6 s! l8 p7 c6 e; E
通过STM32CUbeMX建立USB HID的双向通讯实验成功
( O' r$ R9 n3 w3 \3 T* X, w; h2 C# e; J

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

1 m# Z* M5 z% n
& \; R4 R/ @0 Q. lHID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在  V1 o- P0 P5 d8 X0 o/ K
# n; M. ]9 Z' h! r& |# f
一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。+ a( a7 _5 i0 \
& D. Z& K9 z/ ~+ }+ \, x
二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。
% Z9 f  x8 ~& M& s9 b. H$ M) K
6 z3 t! V1 ^$ k1 U) zu8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的
" k/ a' c9 F$ j& ?" b0 R7 Z0x83, /*idVendor (0x0483)*/
7 J, D4 E1 _6 q0 \0x04,  Q$ \! y) X4 A! \# U4 B5 c4 N. d
0x50, /*idProduct = 0x5750*/
1 C( R- S# b0 B0x57,
: w* E, E  \  n/ \4 h, ?//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。
7 u8 t2 X% h6 u. z//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
# i! F- i( J$ L: R( C//注意小端模式,低字节在先。
- R3 r! k# E$ z4 P//idProduct字段。产品ID号,我们这里取0x5750。& K  d& e9 a8 C
//注意小端模式,低字节应该在前。
9 m" m3 a, |1 T, {) D
4 V$ H1 @3 p  v0 k1 n3 l, E. Y4 |& f- e! _! d
const u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下1 z; e5 c( s& a) J& x
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */  X/ B  A# t& f: V( e- W( i8 b
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */  ~1 S3 r1 [5 e8 O! @9 d# _$ E
  3. 0x03, /* bmAttributes: Interrupt endpoint */
    & V# z1 r: |1 e( v7 y! U1 T1 f6 r
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */4 t4 E+ ?* L7 `. Z: N
  5. 0x00,/ k0 c: J6 v/ A/ L7 w! i- m
  6. 0x20, /* bInterval: Polling Interval (32 ms) */
    $ _1 L- m9 b8 x7 Q9 t) t
  7. /* 34 */
    : t3 f4 j' B) F/ [/ [, l3 f- t
  8. 0x07, /* bLength: Endpoint Descriptor size */
    ) L0 B3 s+ w) ]0 `, r4 x
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
      |6 k' @/ H" e) G: N+ E, w
  10. /* Endpoint descriptor type */4 f# k+ @0 N3 a( v2 b) e
  11. 0x01, /* bEndpointAddress: */" q: g' g: o! `8 b4 ~4 G
  12. /* Endpoint Address (OUT) */
    : R  `$ b- V. u/ }2 {
  13. 0x03, /* bmAttributes: Interrupt endpoint */3 ^% B- R  E5 M) ~4 S# `0 L- p0 R
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */
    9 A0 [% {! U4 I# E# ^
  15. 0x00,
    * Q5 D! v" I! j! T
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码
& Q9 ]7 L- h! [6 c* O1 w( J, x. s
上面包含了“输入端点描述符”和“输出端点描述符”。
2 D+ B6 z. {: B5 S( Y//wMaxPacketSize字段。该端点的最大包长。0 ?2 ?$ \9 d9 m. h$ S6 L3 Y3 T
//bInterval字段。端点查询的时间,+ K9 h/ E7 W  `; |3 C

& L# [) E/ n" l" M/ V为了实现更高速的通信我们修改如下:8 u* B- W/ K6 r( R' w
  1. /******************** Descriptor of endpoint ********************/0 t, ]" z3 U9 k  s" C- S1 }
  2. /* 27 */
    ' c2 u( o) F" q/ D# }
  3. 0x07, /*bLength: Endpoint Descriptor size*/" r+ H7 K- c7 h/ M. }
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    % [1 X7 M' b1 K6 A
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/( Z. K$ m# w7 N0 O
  6. 0x03, /*bmAttributes: Interrupt endpoint*/
    ! L" r$ y& C4 U
  7. 0x40, /*wMaxPacketSize: 64 Byte max */
    + ^. W. @/ g0 Y5 v$ ?. o
  8. 0x00,0 e& C0 U* H# z/ ?! X1 p/ `0 {
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/5 [; b4 I! |9 I4 w0 w6 X
  10. /* 34 */
    . u* W' T! i% X8 n9 z
  11. /******************** Descriptor of endpoint ********************/
    3 r0 a  G' a. r3 y8 O
  12. /* 27 *// M+ A" f# y  P# x4 Y$ h5 ?. q- \
  13. 0x07, /*bLength: Endpoint Descriptor size*/
    # I9 y# U8 a  j. B: C+ e, v( u
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/! s) B  j1 ^' W+ ]6 B0 N0 C
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/) F) i5 M' @; K$ w
  16. 0x03, /*bmAttributes: Interrupt endpoint*/" E9 S3 \3 [% r; P8 k. u9 w
  17. 0x40, /*wMaxPacketSize: 64 Byte max */
    2 }# U5 q. x& s0 {) ]
  18. 0x00,2 T5 o: E. S7 N. U" G
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    2 J9 n! A, Y3 M! V

  20. 0 O: [( L$ e# R( z/ v7 F0 p7 Y& p$ U4 Z
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。
    9 y% ]5 O* m4 z) {* [  w0 F3 o/ t' W  e
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
    ; I* r2 Z' k7 L+ y* L
  23. {2 N+ t* M+ W4 F2 Z% Q( }, X* r7 o
  24. 0x05, 0xFF, // USAGE_PAGE(User define): N) L$ z' `* s) E
  25. 0x09, 0xFF, // USAGE(User define)
    6 J1 W- h. R* P! _$ O: e5 n+ @
  26. 0xa1, 0x01, // COLLECTION (Application)
      B' x, d% V  f; U) @
  27. 0x05, 0x01, // USAGE_PAGE(1)! i3 ?# o6 q  [6 P5 w& n; N; i
  28. 0x19, 0x00, // USAGE_MINIMUM(0)% t7 Y7 [/ _/ P- N% H$ ~7 i
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)& S0 R) V# u, i0 C+ G/ w3 v. E
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0), l9 T; x5 Q2 y1 E" a
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    $ f6 D" D$ ^& D6 |, I& D
  32. 0x75, 0x08, // REPORT_SIZE (8)- x8 _- ]5 I+ n$ S! N9 ?/ E
  33. 0x95, 0x40, // REPORT_COUNT (64)
    0 Q8 F. b6 p7 |* L2 a
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)
    ( U$ s' a! i. q# T
  35. 0x05, 0x02, // USAGE_PAGE(2)
    ( y; I7 y; l& U% D! b& J- ]% j1 F7 H
  36. 0x19, 0x00, // USAGE_MINIMUM (0)
    + N/ u# c" G6 V3 g* }6 l) I- e
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)2 {# g7 h4 G& n, R4 k
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)& w1 y4 S1 F" Y( s
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    , S* V, q0 J2 d; G8 K
  40. 0x95, 0x08, // REPORT_COUNT (8)
    / r$ p4 M8 i% g% B8 v" c. s
  41. 0x75, 0x40, // REPORT_SIZE (64)5 H' m  t0 ^0 z
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)" l; g( b# w1 S+ `
  43. 0xc0 // END_COLLECTION# U2 Q7 R7 k! L0 |
  44. }; /* ReportDescriptor */& z' _. i  J* |/ _) j2 S, j& h; g3 P

  45. , \8 K8 U( m2 W6 s+ e3 \$ N
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]
    " t3 @% Q, b/ s! B
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]" k/ [' ~9 s" D& M# p
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码
4 g8 }- k0 R  S9 Y
分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。
- N3 y+ x* V9 k. @% k8 N7 f
7 B$ s( K+ U% \3 n' y8 O1 c( j
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39" E- [, A$ D0 ~! Y
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 64
    % i* K$ ?( [  s/ k7 G9 L+ M
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28
    0 p7 E0 Q& z9 F1 |( z
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码
% P, K7 [; E% L/ T3 Q9 P& H
三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数) X' R( \- o, g7 K  J
a) Set_System(void)' ^% i3 ^1 c0 H* y. r# Y
b) void Set_USBClock(void)
) o# Z+ ^+ N* X4 Q6 ~( O" `) a. Hc) void USB_Interrupts_Config(void)7 R% g: g4 p. g: \/ q
d) void USB_Cable_Config (FunctionalState NewState)( N8 O( S/ j: k. l( G3 X
特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。
& X8 U5 |# p2 M  t: E3 l
2 s0 r" W: _5 B. U& i: M, _4 c7 G+ |6 g* V" i
四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。: Q6 O! ?( R8 `3 H) z$ i! F- L
打开usb_prop.c文件,修改如下:
% d% _& I( K* e; m. O+ X' u) e
  1. void CustomHID_Reset(void)
    ' I/ A7 a* P* `
  2. {
    : P) v& }2 ]: D2 t  j6 q$ r
  3. /* Set Joystick_DEVICE as not configured */$ k: X' U/ C) `6 t# c1 M, L: j
  4. pInformation->Current_Configuration = 0;0 d$ f+ R  h5 p
  5. pInformation->Current_Interface = 0;/*the default Interface*/0 ^! [- ~# \+ S: m& M
  6. SetBTABLE(BTABLE_ADDRESS);$ F7 n- D1 K6 O3 a& Z5 }6 d# ]
  7. ! c" A- b9 u- H! l
  8. /* Initialize Endpoint 0 */( b  Z. \+ O0 D- E, f
  9. SetEPType(ENDP0, EP_CONTROL);4 p1 Y; S2 P- J" y3 c
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);" B8 s) Q& {2 o3 m" k! p
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);
    . y, O+ |$ w) {, c2 f
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);2 j" e  }1 l- a
  13. Clear_Status_Out(ENDP0);
    ' a* `0 j: I4 v' C* Q) |& q
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
    ! V4 w+ y" l/ M: Z
  15. SetEPRxValid(ENDP0);
    " R2 R: u0 k9 h( l

  16. - ], |: _% n1 V5 Y
  17. /* Initialize Endpoint 1 */
    ( T" g3 \2 F$ _* _  Z4 P0 l7 D( R
  18. SetEPType(ENDP1, EP_INTERRUPT);
    * ]1 d/ z% U  W
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);
    2 W* q( k) K( E( m0 y0 m8 Z  x
  20. SetEPTxCount(ENDP1, 64);7 v6 p/ ^# E/ j: L5 ]1 u8 K4 D
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);
    + n6 {$ a* K( I& ?3 D+ j
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);* J( h( a+ P. ]1 G) Q' Z" g
  23. ; L; l2 `: X, e7 I
  24. /* Initialize Endpoint 1 */+ v) T! e3 O! J
  25. // SetEPType(ENDP1, EP_INTERRUPT);
    - T# W/ ^5 b: L! a! X
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);
    - w( \8 {+ L6 A" c) y3 ]
  27. SetEPRxCount(ENDP1, 64);
    6 ?. p# D3 u4 O
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);
    : T& [* P2 E3 B7 @. S
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);: y! K1 S& Y( R3 t( {" c, o
  30. /* Set this device to response on default address */
    5 @6 M2 d; t1 Q8 j# ]8 A  N
  31. SetDeviceAddress(0);
    * X+ g2 B2 x& T9 c
  32. }
复制代码

. O2 j/ p: a& O& L+ l1 [) k五、usb_endp.c文件
& r$ P0 h7 V. j( [# k
  1. void EP1_OUT_Callback(void)
    6 l( B6 q9 ]2 V# U) y; P3 e
  2. {! b0 o, r0 Y; k+ I# h! @- p/ {, g
  3. 这些写接收代码
    4 }5 c* p: R& O( Y/ m. ?. B- z
  4. }
复制代码

; F3 Q0 D; q9 ~六、数据发送和接收,举例说明
1 e; {: D% s  O& C9 G9 P. s3 {1、数据接收7 ]5 L* L& X! [7 N5 m& s8 T  e
  1. u8 DataLen;
    # S# k8 ]3 u! k1 T# k5 D- r" y
  2. DataLen = GetEPRxCount(ENDP1);8 ?$ O* Y: {, J; Y
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);, V) y, C) r  t# L; n, T- e0 k/ k
  4. SetEPRxValid(ENDP1);6 ^8 H' X) K- {! O: |6 x
  5. USART1_Send(DataLen);' S* \+ `2 F! s! v# B1 x& I- o
  6. count_out = 1;
复制代码

4 j$ M) s; F" |  ^- D: y2、数据发送
# @4 V6 U# g6 Y% C6 L# E
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);% g' |5 E; g- O1 i2 `9 B
  2. SetEPTxCount(ENDP1, 64); * t2 B- ~1 q  R- M
  3. SetEPTxValid(ENDP1);
复制代码

& w$ m) v0 l  n+ d: G如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。
6 i2 ~) g$ A  I/ l
" ~+ y3 z$ Z1 b' X+ n" i" [3 z( ~
3 ]: Y7 [5 w3 [& x
(by xidongs). i( `4 a5 M; k/ H! s
收藏 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管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版