请选择 进入手机版 | 继续访问电脑版

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

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

[复制链接]
Tom Chen 发布时间:2016-7-24 16:52
STM32 USB HID键盘例程
: ~2 i, w0 C" U
, O. _* Y1 g' u4 H+ I% r- T
最全USB HID开发资料,悉心整理一个月,亲自测试7 Z  E* z, Y  n- j9 t/ P9 V1 }
: s* o9 r7 b6 z1 E
通过STM32CUbeMX建立USB HID的双向通讯实验成功. U* s6 J3 L+ ]5 a" b

5 @/ K$ M$ S6 ?) t
% x0 U% D6 @1 `3 ?+ {6 M9 p2 \
发现很多人对STM32的USB通信很感兴趣。要将USB的通信协议搞懂确实是一个比较漫长的过程。但是USB的HID通信无论是上位机的设计还是STM32程序的编程都非常的简单。只是我想很多人都不知道而已。这篇文章的目的是让大家以最短的时间将USB加到你的设备中。如果想学得更深就靠大家。
$ `, y8 p2 c5 k$ `. U! l7 C) I8 L

- ^, o; D/ V/ v3 a* kHID只是适合低速传输,其理论上可以达到64KB/S,但多由于windows系统和硬件的关系一般达不到这个传输数度。但这个速度对于一般系统的控制和数据传输都已经足够了,而且是免驱,省去了很多麻烦。如果您需要高速传输可参考我的另外一篇文章《STM32的USB例程修改步骤》文章在1 D3 Y( Z+ u* d! H( K& H
: ?. {* d5 [: o+ D2 y0 F
一、安装完MDK后请打开C:/Keil/ARM/Examples/ST/STM32F10xUSBLib/Demos路径,将Custom_HID在同一个目录下复制一份,如果你要放到其他路径你需要在MDK Options for target的C/C++中添加USB的头文件路径(MDK下的/INC/ST/STM32F10x/USB)。
& k2 W& Z3 v( q: ?# W) K/ F; S0 k8 R
二、打开usb_desc.c文件,该文件主要包含的端点描述符、设备描述符、配置描述符和字符描述符等。具体请大家参考其他资料了,这里主要说几个常用。! o. z7 V6 V4 a* e" `0 a6 Z
" R: k( p. O' f" d6 o' ]
u8 DeviceDescriptor[SIZ_DEVICE_DESC]为USB设备描述符。当中的& z! t8 p  n, C8 H. g, L
0x83, /*idVendor (0x0483)*/
& D' N1 `) _% o6 n# x& T( ?0x04,
3 r2 ~, l! W0 s' {/ \0x50, /*idProduct = 0x5750*/
1 S; ?4 v1 }" `3 C0x57,
. [. m7 }1 V4 L. b2 Y& b//idVender字段。厂商ID号,我们这里取0x0483,仅供实验用。! A4 a( m3 |! O1 w% r/ w
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。' Z+ Z6 F' h" j9 y+ k/ A6 {& F
//注意小端模式,低字节在先。% E5 u# S" q' }* R; {+ D
//idProduct字段。产品ID号,我们这里取0x5750。
) D- _( n. R. y! _' L//注意小端模式,低字节应该在前。6 H! \2 |+ K+ ^7 M

5 D7 r! T0 R0 e2 W7 [
8 r5 S1 R$ H2 mconst u8 ConfigDescriptor[SIZ_CONFIG_DESC]是配置描述符,注意如下
0 y6 R( z5 f. E, @0 Z
  1. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: *// s6 m" c2 x9 Q! u8 G7 }. y
  2. 0x81, /* bEndpointAddress: Endpoint Address (IN) */! j9 J4 A0 ^6 \7 m+ {4 F
  3. 0x03, /* bmAttributes: Interrupt endpoint */& X1 ^; `7 U" x% p+ G
  4. 0x02, /* wMaxPacketSize: 2 Bytes max */
    & E9 {/ _/ R3 ~9 b+ t4 N
  5. 0x00,
    ' ^9 i( S0 s( C& J. i
  6. 0x20, /* bInterval: Polling Interval (32 ms) */
    ; f! z, E  u" G- y3 w2 K; \3 ?
  7. /* 34 */
    7 ]+ F' k. {0 i+ I# X- `
  8. 0x07, /* bLength: Endpoint Descriptor size */$ a$ A9 A) `& a! _& }+ n
  9. USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
    " A, t3 L' }' N
  10. /* Endpoint descriptor type */& z- E+ ]7 q0 F* U6 K0 n& Y3 R
  11. 0x01, /* bEndpointAddress: */
    ! f7 ?1 N" D/ @9 v9 I
  12. /* Endpoint Address (OUT) */
    5 n, }/ j. r4 W% O' T+ W% G
  13. 0x03, /* bmAttributes: Interrupt endpoint */
    % N4 g) d2 d! w# ~5 @
  14. 0x02, /* wMaxPacketSize: 2 Bytes max */
    0 b+ {- j3 z  b
  15. 0x00,/ E& c* \5 a% ?1 K+ _% g9 C' [
  16. 0x20, /* bInterval: Polling Interval (20 ms) */
复制代码

2 o" M& W8 _: D+ N上面包含了“输入端点描述符”和“输出端点描述符”。
/ @% w9 K# r: p3 H% q& c//wMaxPacketSize字段。该端点的最大包长。) T" V5 o# {# v  N5 B
//bInterval字段。端点查询的时间,3 s- N* q, S2 ^4 E( Z
! K0 Y- i) ~  W
为了实现更高速的通信我们修改如下:
9 J3 J9 m# U3 L* Y  n; d) I6 N# D
  1. /******************** Descriptor of endpoint ********************/
    " i- v* V7 E. [" H: q! \2 [( Z
  2. /* 27 */
    9 p( |, x  W. I2 g( `5 n
  3. 0x07, /*bLength: Endpoint Descriptor size*/
    ' k$ a% l1 n; b# S2 A/ h% F
  4. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    % i- q$ \, V  U* T# w
  5. 0x81, /*bEndpointAddress: Endpoint Address (IN)*/3 V  Z  r+ E6 I& J" r
  6. 0x03, /*bmAttributes: Interrupt endpoint*/& I4 K) g8 W  d; R
  7. 0x40, /*wMaxPacketSize: 64 Byte max */
    - f" S, c2 c/ I* v
  8. 0x00,. Y# @# a' z7 W4 X9 Z0 R2 n) ]- {* t0 z
  9. 0x0A, /*bInterval: Polling Interval (10 ms)*/1 c; w# n* m! Y0 }
  10. /* 34 */2 u) H2 B8 N3 z) y
  11. /******************** Descriptor of endpoint ********************/
    1 T5 `; p! ^; G; C9 I8 X+ C' t. s, Y. K
  12. /* 27 */
    - O# n) M5 B  ?; x+ O
  13. 0x07, /*bLength: Endpoint Descriptor size*/) h) c+ Y; U% o# Y+ |9 @( W: h9 O
  14. USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/& j( X8 D% C$ S# l
  15. 0x01, /*bEndpointAddress: Endpoint Address (OUT)*/1 A1 `# n. `# R0 H) E" V
  16. 0x03, /*bmAttributes: Interrupt endpoint*/8 ?$ h& y- \# Z  K; `
  17. 0x40, /*wMaxPacketSize: 64 Byte max */
    ; _- N+ C* x) w
  18. 0x00,
    - E6 P4 J$ i( K% g* c
  19. 0x0A, /*bInterval: Polling Interval (10 ms)*/
    , L, X7 l( ~* r1 Q& {

  20. , v8 `- E* U5 Q0 x
  21. const u8 ReportDescriptor[SIZ_REPORT_DESC]为HID专用的报告描述符,具体的大家就参考资料了,这里可以直接复制了。
    * c: @: J: R1 X
  22. const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =9 I4 Y0 S. R/ O" m1 m5 T  k
  23. {
    / Y9 i+ k/ f# W& b: k( M1 I/ }9 Y/ q) J
  24. 0x05, 0xFF, // USAGE_PAGE(User define)4 H" v, l8 C0 e( |. d; r8 |- [
  25. 0x09, 0xFF, // USAGE(User define)
    " j1 ~2 b! @0 Q( L# E
  26. 0xa1, 0x01, // COLLECTION (Application)
    6 R+ A0 Y( C1 {7 f6 G9 b' a
  27. 0x05, 0x01, // USAGE_PAGE(1)
    3 j1 V( ]1 Z& w! c' y: B
  28. 0x19, 0x00, // USAGE_MINIMUM(0)
    1 ?; a8 Q$ Z0 w9 g
  29. 0x29, 0xFF, // USAGE_MAXIMUM(255)
      M; ^: R9 X! r3 A' e' C. j1 V
  30. 0x15, 0x00, // LOGICAL_MINIMUM (0)
    3 k! C* m6 ?: V& X* j: {6 Z+ |3 E/ g
  31. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)# Z6 k4 T3 p( Y: Q
  32. 0x75, 0x08, // REPORT_SIZE (8)# Q* s! I3 H. E* i1 E0 l9 M5 w
  33. 0x95, 0x40, // REPORT_COUNT (64)7 P) T: O- Y  N! \+ ]
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)7 x- w, K' }8 |2 {
  35. 0x05, 0x02, // USAGE_PAGE(2)% z: R# g5 a! N- n8 S- Q
  36. 0x19, 0x00, // USAGE_MINIMUM (0)6 e  P" m- U/ F- P
  37. 0x29, 0xFF, // USAGE_MAXIMUM (255)
    6 V6 A6 E# U. n& w0 ~" P
  38. 0x15, 0x00, // LOGICAL_MINIMUM (0)* x+ R$ e( M) d" R8 l
  39. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
    * r# R% g) k5 n! X. M5 X7 Q! O
  40. 0x95, 0x08, // REPORT_COUNT (8)
    # B3 f! ~# b& X8 M. x! ?* Q$ F3 ?, z
  41. 0x75, 0x40, // REPORT_SIZE (64)
    5 B/ T4 a$ Q8 E; \7 G
  42. 0x91, 0x02, // OUTPUT (Data,Var,Abs)
    + s7 U2 j7 d" e4 A/ _3 X6 `& k
  43. 0xc0 // END_COLLECTION
    ) P/ _, y( H5 W% i
  44. }; /* ReportDescriptor */
    : L: j- y6 K/ \% E

  45. * B3 p2 S. t! s' Y( e
  46. const u8 CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR]8 O: B4 r$ Y2 l- i& G. i0 e! h3 h: u
  47. const u8 StringProduct[SIZ_STRING_PRODUCT]
    2 D& s. b6 g) |, b0 B6 R2 a
  48. const u8 StringSerial[SIZ_STRING_SERIAL]
复制代码

2 n! s; K, M1 g2 m& f& R分别是“厂商字符”、“产品字符”、“产品序列号”,这些将在USB HID设备加载的时候显示。但是这需要这些字符要求为Unicode编码,你需要将你要显示的字符先转为Unicode编码。最好大家还要根据各个数组的长度修改如下定义。" D8 ^- r7 r  K3 J

$ Q4 W) ?( J, `# v
  1. #define CUSTOMHID_SIZ_REPORT_DESC 39
    8 \0 z5 X& R0 |: ?( i/ l8 J
  2. #define CUSTOMHID_SIZ_STRING_VENDOR 645 l1 b5 ?  W8 q$ z* y
  3. #define CUSTOMHID_SIZ_STRING_PRODUCT 28& H& Y: d6 U2 W: D* _$ ~# ^
  4. #define CUSTOMHID_SIZ_STRING_SERIAL 26
复制代码

" w# G$ `+ `7 v- s三、打开hw_config.c文件,将那些没有的函数删除,只保留如下函数! g3 [" r/ D$ p; E8 ~6 y: B  N; L
a) Set_System(void)/ v* W* o, Z+ t6 w3 I1 t
b) void Set_USBClock(void) 4 q; @) k! o0 Y( d/ O8 E
c) void USB_Interrupts_Config(void)0 N9 A: q0 n; _. `
d) void USB_Cable_Config (FunctionalState NewState)
. e' `$ }& k$ O: U' }# \特别要注意最后一个函数,其主要作用是控制USB的上拉电阻,让电脑检测USB设备是否连接的。
6 |9 h9 D5 D3 l& ]* Q6 h( {7 ?4 a/ }* w+ ?5 V& _; K
# n7 L' v1 x$ K9 S
四、打开stm32f10x_it.c文件,把EXTI15_10_IRQHandler等中断内的代码删除。
' a9 X: n: \1 X0 p: w打开usb_prop.c文件,修改如下:: C" K$ I: m1 i0 a
  1. void CustomHID_Reset(void)
    " x  J$ l3 O) S3 \8 t
  2. {! \1 ]) D8 `( w. M# s5 s$ y
  3. /* Set Joystick_DEVICE as not configured */
      Y" g* w- H. ^3 D2 A2 v
  4. pInformation->Current_Configuration = 0;
    8 R6 k. [: e5 I3 V# H) a
  5. pInformation->Current_Interface = 0;/*the default Interface*/! m, J2 t% |  y6 U% ~
  6. SetBTABLE(BTABLE_ADDRESS);' k3 m5 `( W8 \9 B) N8 d  G1 s

  7. % u6 F! B8 |4 H* p8 r& t
  8. /* Initialize Endpoint 0 */
    9 z, B5 m/ Z! }0 h1 k* S
  9. SetEPType(ENDP0, EP_CONTROL);
    + x  V* |' c9 r2 V3 }7 L5 {6 p
  10. SetEPTxStatus(ENDP0, EP_TX_STALL);3 _! @' C" k& C" l) Y- A" b
  11. SetEPRxAddr(ENDP0, ENDP0_RXADDR);) F# [( b% \7 M
  12. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
    4 w' z" E& m7 K: y
  13. Clear_Status_Out(ENDP0);0 A% R; P. z* e$ D! @" X. z
  14. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);* q/ `' ]0 s/ p( C% l3 C! ~
  15. SetEPRxValid(ENDP0);0 H5 l2 z( S- W$ c3 `) P( Y" _

  16. 6 C" T8 }8 ~+ ^4 V: W& r
  17. /* Initialize Endpoint 1 */; {9 p. V+ [, P: @
  18. SetEPType(ENDP1, EP_INTERRUPT);
    + `5 }# o4 \$ H2 S4 P! J4 U
  19. SetEPTxAddr(ENDP1, ENDP1_TXADDR);/ `* Q2 f' d( B8 V2 e
  20. SetEPTxCount(ENDP1, 64);7 s% U1 U( R- L& J- ]
  21. SetEPRxStatus(ENDP1, EP_RX_DIS);; I# m9 Q; F8 g0 W: @: f+ S& c
  22. SetEPTxStatus(ENDP1, EP_TX_NAK);. Q2 f- @6 Q0 U* T

  23. 9 x; N& a/ A- L. M* j9 b6 y  i
  24. /* Initialize Endpoint 1 */
    & ~! Q# A0 z7 U+ d6 n, U
  25. // SetEPType(ENDP1, EP_INTERRUPT);
    0 N+ I0 |5 z' d8 Q) F8 b
  26. SetEPRxAddr(ENDP1, ENDP1_RXADDR);9 N: j, R7 v1 f; y, F7 B
  27. SetEPRxCount(ENDP1, 64);
    ; K  A0 w) N; g6 Q" D
  28. // SetEPTxStatus(ENDP1, EP_TX_DIS);
      G% v; Z& [: e1 }1 e3 |- @5 p
  29. SetEPRxStatus(ENDP1, EP_RX_VALID);. R' E4 x) h' G& {. S
  30. /* Set this device to response on default address */( p$ ^$ l; S% K. W
  31. SetDeviceAddress(0);% [5 j4 \) l9 i/ o
  32. }
复制代码
3 h/ l% f. B6 g# o
五、usb_endp.c文件
" ~$ P# h8 A4 o$ w: s$ D$ C; e* F
  1. void EP1_OUT_Callback(void)
    0 v4 r4 b0 E* Z' B; w. p
  2. {
    , K( W# S6 o/ d/ s% q
  3. 这些写接收代码
    ; x0 R7 K$ G6 O0 Z& Q
  4. }
复制代码

' |# N- s, \: S2 K3 h' S, L六、数据发送和接收,举例说明1 K! T+ O! I/ l. ?, z
1、数据接收" k4 P: F8 e2 U, Y& Y
  1. u8 DataLen;
    & E0 i5 I( o1 A" v' q
  2. DataLen = GetEPRxCount(ENDP1);, Z, W6 i2 x4 n3 x4 W
  3. PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);
    . \) }* e7 D* Z: {9 j; ~# R
  4. SetEPRxValid(ENDP1);& r# O  s" z* @% Q
  5. USART1_Send(DataLen);! L7 m/ O( o3 G. Q7 E
  6. count_out = 1;
复制代码

; @5 I* r" L. |9 P+ y* T8 T6 v% c2、数据发送6 I( ^: ?0 O1 ^/ g- z
  1. UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);$ A# @0 q8 F# K8 v% `6 m2 i
  2. SetEPTxCount(ENDP1, 64);
    $ X, _0 X8 k/ C& D8 W
  3. SetEPTxValid(ENDP1);
复制代码
+ W3 g" O9 s, F  |" l' L
如果你发送数据较为频繁,每次发送前应使用GetEPTxStatus(ENDP1)检测上次发送是否完成。如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。: o4 B, j7 v- N8 Z6 b% `
6 K  M% t0 k* G. z* K9 ^

& f% k5 d# Z2 \2 w& b(by xidongs)
* n( r+ N0 O# q- d: P1 s* V5 G
收藏 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 手机版