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

【实战经验】通过STM32CubeMX生成HID双向通讯工程

[复制链接]
zero99 发布时间:2016-7-12 14:57
通过STM32CubeMX生成HID双向通讯工程: ]6 E8 w7 N" p
前言
3 J9 f0 e7 v' G" U! A' y2 `, n& U客户在做USB通讯的时候,基本的需求就是发送某些数据到USB host端,同时接收一些数据从USB Host端,那么如何快速的建立一个工程并验证数据是否正确呢?下边我们就结合STM32F072的评估板(其他的STM32xx系列的实现方式都是类似的)来快速实现一个简单的数据收发实验。0 Q( m' e! Q5 [8 [/ V. F
; X& i- B' g9 |8 O
问题分析
6 ^# ?- {! P: j7 F! F+ mUSB Host软件
, z& |4 W- F2 d# n1 fPC端软件使用ST免费提供的Usb Hid Demonstrator。这个软件可以在ST官网上免费下载到。连接地址:STSW-STM32084,此软件调用的是windows标准的HID类驱动,所以无需安装任何驱动程序及可运行。
7 m' I: p1 R# M! @: k7 F9 ~' R
7 C# J0 T* l! ?4 M 1 q$ J, T* ?, t- Z2 ^, t
7 M6 q) }' E: W+ A
下载安装完这个软件之后,我们就可以开始开发STM32的USB 从机程序了。5 O8 O! e/ i9 x" O7 f+ ]& ^2 w
首先,打开STM32CubeMX,新建工程,选择STM32F072B-DISCOVERY开发板。$ u, ~& [6 F2 p6 j7 u; F! }5 u0 ~; X
% k5 @3 b+ h) m8 `; P: M

$ S; r3 w3 f' B; N8 _. ~! d
6 s$ F* S) L% f' L# X! d其次,在Pinout选项中,开打USB的device功能。: M3 a2 @8 t" H" q6 H

8 z- X5 N4 L6 [/ X2 n6 [, z' S) j
4 k, [9 e6 K( e$ c, M, s/ ]/ o+ O
0 B  f$ |" B/ X; [5 X并在Middleware中选择开启class for IP中的 custom Human Interface Device(HID).% W4 D: h% n/ L$ q  |# Q0 j
( B/ i, ~7 L- H/ E9 t* ?
' n+ h7 l; U5 q2 q& B

$ U( N3 u+ _! m$ M点击“保存”后直接生成工程。我们这里以生成IAR工程为例,项目名叫做HID。0 o  F4 ?$ s: l: j' O* H$ O
& Z- a5 J: j8 u/ G2 ^6 u! ^/ L& R5 }

5 j) K1 k, F  j! d* E9 k! e# L, ~1 U8 A5 L5 H2 T
这样我们的工程就基本成功了,但是还缺少最最关键的一步,就是USB主机和从机的通讯“协议”,这个协议在那里实现呢?因为我们Host端软件已经是Usb Hid Demonstrator,那么这边的协议就已经固定了(其实在实际的开发中大多是主机端和从机相互沟通后,软件自行修改的),从机只需要对应这套协议即可。
( X0 l. G* U& T. c6 I1 f) q% B8 L将如下代码复制,替换掉usbd_custom_hid_if.c文件中的同名数组。8 v; ~7 U3 }' Z) B# w! g- g! d0 n  `
( k0 }% q3 ^8 f; u3 S+ L( r
/ }8 ^9 E7 x  _, u; y
, \4 r2 d6 J+ Y6 F" i" i
* {9 G/ q3 H6 _+ S8 o& P+ k7 l
- U. k5 }/ W' H
) g4 S* W. P" _3 Z0 D$ D
注意:这里一定要覆盖“同名”数组,千万不要覆盖错了。5 {4 `, z- x1 ?0 z# A6 e; i
之后将如下代码复制到usbd_custom_hid_if_if.h中。 9 i+ i; o& O; h
! D* q8 d# d% _8 J

9 K+ z" L. N' g1 b' z! s2 e. Z9 M. j; g( l; Z
最后在usbd_conf.h文件中将USBD_CUSTOM_HID_REPORT_DESC_SIZE的定义值修改为163(默认值是2)
% {7 \2 a6 T* n
1 ~3 K7 _' s; d+ w, b为什么这样修改呢? 简单说一下其中关键值的含义。这个HID 的报文描述符其实定义了8个部分(条目)的功能定义,分为LED1,LED2,LED3,LED4,按键输入,篡改按键输入和ADC输入。每一个部分基本的格式都是固定的。以LED1为例(其他条目可自行对照文档解析):
+ L; J: n5 ~5 f; i/ P3 q* d) {( i/ c( i, d' q1 D0 ]0 Y
0x85, LED1_REPORT_ID, 含义是这个功能的ID号是LED1_REPORT_ID(宏定义为0x01)8 V. ]5 g0 |3 ]5 ]. h/ H9 ~" c: F
这个ID号是每次报文发送的时候最先被发送出去的(USB都是LSB)字节,之后跟着的才是我们实际有效的数据/指令,到底是数据还是指令,就看你的应用程序如何去解析这个数据了。% U: q" S! S5 i/ r: a+ W1 u6 c" d

4 \2 ]8 }" ]5 g( g$ `  L0x09, 0x01, 这个功能序号为1,后边的序号依次递加,没什么好说的
2 W- ]; Q1 D$ b" h2 s5 \$ `3 H' Z& U+ O0x15, 0x00, 这个是规定逻辑最小值为05 X% ?) [# _0 @. o7 L
0x25, 0x01, 这个是规定逻辑最大值为19 @2 D) L1 a. `* B1 c! X2 }
上边的这两条语句规定了跟在报文ID后边的数据范围,最大值是1,最小值是0.(因为我们的LED也就只有灭和亮两种状态); i' v" b& v" H4 {6 Q
3 ^% z! j  ^1 [( \" m
0x75, 0x08, 这个是报文的大小为8,只要别写错就行了2 S6 w( [  y. }
0x95, LED1_REPORT_COUNT, 这个是说下边有LED1_REPORT_COUNT (宏定义为1)个项目会被添加,即这个功能的数量是1个3 V" B7 t$ \8 D, b4 O5 T8 N
0xB1, 0x82, 这个是规定能够发送给从机设备的数据信息3 g5 O- |- ^$ @! _- y. I3 i0 T
0x91, 0x82, 这个规定了这个功能的数据方向是输出(USB的方向都是针对主机来说的)5 [# }# F  J: U0 j' H) {- B" l
总结一下,通过这个报文描述符,我们就告诉了主机,在HID中有一个功能ID为1的功能,其方向是从主机到从机,每次发送1个有效数据(前边的ID是都要含有的),这个数据可以是0或者是1.1 B' {+ J& S! S9 ~
关于HID 报文描述符的详细信息,您可以在下边的网址下载一篇叫做《Device Class Definition for HID》的文档来参考。
, ?; Y0 W6 p( _+ @5 q* V  r5 ghttp://www.usb.org/developers/hidpage8 ?4 H, v. i9 `9 T

/ m9 K( h" Y" j# t7 o  x9 z这样基本的程序框架就已经成功了。此时我们可以先编译一下,看看是否有任何遗漏的或者笔误。如果编译是正确的,那么我们就可以先下载到硬件开发板上,连接到PC端,看看是否可以枚举出设备。如果您前边的修改都是正确的,那么在PC的设备管理器中会看到如下图所示的内容。& R# A  x: P4 Q3 S: V* J. P# p

0 m) {2 m+ y. C8 y' `. H, s# F * |1 Y2 d2 Y4 X8 w& p% B$ N
, E' _6 i( `& T  \2 _7 I
注意:开发板上有两个一模一样的Mini USB接口,一个是USB USER,另 一个是USB ST-link,下载代码的时候用USB ST-Link,连接电脑运行程序的时候要用USB USER。6 C& t9 ~; _. P, e) z, L  j
此时我们的USB枚举就完成了,这个是USB通讯的关键步骤,之后的应用通讯内容都是通过这个枚举工程来进行“规划”的。, D  \/ Y, F  |) q" c4 F4 \+ u

9 w, w: F5 ^+ z0 I9 o数据发送( X" M+ j5 h+ j9 v# s) g  O
就类似串口通讯,我们首先做一个数据的发送工作。
: d6 F: o4 e& o% f. v3 J在Main.c文件中,我们在while(1)的主循环中增加我们的发送函数,主要就是调用发送报文的API:USBD_CUSTOM_HID_SendReport()- [# G- o: M7 e% ~5 i- T$ h/ k

* m  t! y3 A, q: }
4 S5 q. k0 P7 _
3 a9 l: z1 y/ {8 v编译后下载到MCU内,连接上位机软件即可看到如下所示的进度条在不断的增长。8 [: o: ~5 t6 P- g' B
+ G" [$ V; m& \( Y9 ~! J

3 ?6 L  P% f6 x* X3 K0 G2 T; O' u
8 c$ v8 z: {3 o- y这个就是我们上传到的数据在上位机的图形显示,你也可以看Input/output transfer里的数据变化。9 d# L) U- S4 n7 N9 E

3 J/ p; Z; r6 `! r
2 j7 Y( j  e6 r+ L; R$ t) B8 r" |
( [2 t5 M1 K9 z* j9 U这样看起来是不是更像是串口调试助手了?嘿嘿 本来机制就差不多的。
& H1 [* u$ b/ J# z2 J5 a  H. L) L8 E
" W) y% r. h2 Q/ i6 ~- ?" T数据接收7 r; z1 G) y6 H9 n5 A
MCU的USB数据是如何接收的呢?是不调用一个类似于串口接收的API呢?) w, S4 N( q* \& s2 Y
不是的!USB的数据接收都是在中断中完成的,在新建的工程中,我们在函数CUSTOM_HID_OutEvent_FS内增加如下代码。. z' s/ p8 x5 l: r
2 Q# N( Z, g# H2 a6 h: M
% j% A3 u4 h' {. ]8 {; T
3 j& l# ~* w& _  [  h
编译之后下载到MCU内,通过USB USER连接到PC端,打开Usb Hid Demonstrator,我们可以通过勾选右下角的图形界面来实现控制开发板上的LED电量或者关闭。% |3 b) L# z0 r; L: l

4 h3 f/ Z. g5 t; a. W
, A# x. c) L% x* z' K: n$ d4 }9 P# j# Q
当然,这个是通过图像化的界面来进行控制,你也可以通过Input/output transfer中的写入对话框来完成这个操作。注意,写入的第一个字节是ID,表示你想控制的是哪个LED;第二个字节是0或者是1,表示你想让这个LDE的状态变成灭还是亮。
! U3 P6 h. c8 p( m. P" r
3 {# D( {4 I0 s$ }( s9 x6 R! g* g
% A2 S& o- t' J# \: J
4 n# S# S9 d1 Q0 v" D+ E$ R) P0 x总结: 本范例程序是为了快速实现USB 从机设备与主句设备双向通讯目的,其初始化代码是用STM32CubeMX来生成的,大大降低了工程师开发USB设备的难度(尤其是是入门阶段的难度)。从这个工程的基础上,工程师可以比较方便的建立好框架工程并,对其中的代码进行研究,进而移植或增加自己的应用代码。  j: w4 ?; P( {' B' [
( J  q0 u# V$ e: I- U$ W7 d4 ]. T" R
文档和代码下载地址:
# L; j4 ]" g7 X) Q: G5 }- @) n7 ohttps://www.stmcu.org.cn/document/detail/index/id-217181
7 p: P0 G* F  g2 H$ g& E" K
* b8 p, F) w* t$ _' ^% A  N
工程* v4 `. R6 \$ p- m( p0 t
& M: ~. a7 H$ Z# f8 }& W
17.png(37.22 KB, 下载次数: 0)

% |- a) D7 k4 r7 J) b1 \. }" Y
$ l: Y$ M( l4 Q5 U" U% E7 H0 v  {7 h$ q7 V+ K0 R! L/ m1 b9 f% m
16.png(37.78 KB, 下载次数: 0)
- a& I* |) }2 D# M* _/ [! g- L: W
. Q5 n8 g/ S+ L1 P) D1 r# G- u2 R

7 n( O3 X5 D7 y+ U! U; f  y  U0 p
15.png(52.58 KB, 下载次数: 0)
% \) h6 x: i- a- W# H  }

/ f  I0 G, y% _% p
' y6 Q8 m" g' r8 x% W- X1 O4 [
14.png(34.56 KB, 下载次数: 0)

6 l# T7 s. M: V- ]9 `! B, z5 J
# y* P5 l& B, ~& a! n" Q% Q* @( a7 @0 S% V9 o, F, N
13.png(38.14 KB, 下载次数: 0)

. b9 y/ C% g$ f) A6 y, w8 u* b* X* b1 V. \; B

9 g3 a  T2 [2 O* w* W0 ~% F! g0 `' C* m* S  Z' a
文档和代码下载地址:
/ Q0 l+ J4 X! I1 }5 g" p5 D- ]3 ihttps://www.stmcu.org.cn/document/detail/index/id-217181

. ~4 w9 q& x- p" Q6 T* j
# s* v1 Y- g& J+ m1 k+ v/ ^( f实战经验汇总:* P+ U; R' d5 S6 k  D9 U9 j
https://www.stmcu.org.cn/module/forum/thread-576401-1-1.html
收藏 7 评论14 发布时间:2016-7-12 14:57

举报

14个回答
gonger 回答时间:2016-7-13 08:57:33
谢谢楼主!学习了
yklstudent 回答时间:2016-7-13 11:01:35
mark,谢谢楼主的资料分享
darren_liu 回答时间:2016-7-13 11:54:38
谢谢楼主!学习了
stary666 回答时间:2016-7-25 09:47:41
jq-362251 回答时间:2016-12-13 08:32:49
很好的例程~谢谢
五哥1 回答时间:2016-12-31 02:37:11
宝贵的实战经验
5265325 回答时间:2017-1-2 14:11:40
tanr 回答时间:2017-1-12 11:09:44
本帖最后由 tanr 于 2017-1-12 11:11 编辑 3 z' o  K' z( M

8 u) N3 b2 h5 T/ i如果不在main函数中的while中调用USBD_CUSTOM_HID_SendReport()这个函数,为什么就不能继续使用HID了,有大神为我指点迷津不0 {; Q, E+ z1 Y0 j' Z/ R
sangarr 回答时间:2017-12-6 16:30:35
成功了
Mr.Luav 回答时间:2018-2-7 18:51:23
很好的帖子   学习了  感谢!
Mr.Luav 回答时间:2018-2-8 12:41:25
本帖最后由 Mr.Luav 于 2018-2-8 12:42 编辑 + \" d7 B$ V" q" D4 I6 y
0 G% `3 J& m, z) ]5 j
楼主   移植你的代码 通了,,现在我想修改下使用 发现设备无法被识别  使枚举失败了?
1 `& B* g" t* X7 K0 G我修改的如下:% U* O$ `; w+ f7 n' k9 b
我将ADC IN 的描述注释掉,在对应的头文件里将ADC的 ID 宏定义也注释掉,," K  Y5 l5 y0 n7 ]4 ]
接着我把#define USBD_CUSTOM_HID_REPORT_DESC_SIZE     163//2     这里的163改为144(这里的大小是根据 请求描述里的设置字节数对应的吗?)
% F2 z) j2 K# y: A3 w5 S1 X编译后下载  HID 设备就无法识别了,但是一换回去 就又可以通信了  。。  是不可以修改么还是哪里改错了??    有了解的也请指点一下  感谢!
, b% q; |2 w+ X5 O

点评

你好,本文出自ST FAE,移植中若有问题,欢迎单独发帖交流~  发表于 2018-2-8 15:06
litewei 回答时间:2019-2-27 09:59:54
有用,感谢!
happytaoxiaoli 回答时间:2020-5-12 16:46:43
谢谢
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版