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

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

[复制链接]
zero99 发布时间:2016-7-12 14:57
通过STM32CubeMX生成HID双向通讯工程
* a' [: s. w3 t4 \  _/ |, \
前言- I- P/ z) C3 Y5 M" I! z; x# ?  C$ n
客户在做USB通讯的时候,基本的需求就是发送某些数据到USB host端,同时接收一些数据从USB Host端,那么如何快速的建立一个工程并验证数据是否正确呢?下边我们就结合STM32F072的评估板(其他的STM32xx系列的实现方式都是类似的)来快速实现一个简单的数据收发实验。& ^% t, b- u4 @9 G; T# ?! u

& g  L( |! g# }$ d6 A! [6 o, R问题分析* W% G9 X" m" `& K' g7 k; [8 u4 A# X( h
USB Host软件
6 @5 E$ A8 v2 I0 \PC端软件使用ST免费提供的Usb Hid Demonstrator。这个软件可以在ST官网上免费下载到。连接地址:STSW-STM32084,此软件调用的是windows标准的HID类驱动,所以无需安装任何驱动程序及可运行。
) [. k! t# @; x) [
7 J% a, ~3 d" Z8 G , V& i8 Q* {0 T! s0 c

0 Q; D! l2 Q  K* {4 l下载安装完这个软件之后,我们就可以开始开发STM32的USB 从机程序了。
. N7 ~1 _4 j1 P8 d/ e首先,打开STM32CubeMX,新建工程,选择STM32F072B-DISCOVERY开发板。
1 k$ m+ v& b4 Q) D: f% x% C) F/ u0 u/ t) ]! ]/ J5 w
9 O' d* _1 S( W2 M. r
8 D( W1 u: u+ l
其次,在Pinout选项中,开打USB的device功能。
7 `) z. m5 \! j* M2 i' s* c
2 Q. F" L- S* B. X  x , X1 N$ L; M) T  q0 N9 x
7 ^' y* G% f5 L) o1 \: I: f
并在Middleware中选择开启class for IP中的 custom Human Interface Device(HID).8 k4 Q8 Y9 @7 r5 O1 }& @) X9 X  `# B& J

: [# g; a. T1 s& C% K9 {
1 e+ [( c- ]+ d6 m9 W: O4 I4 u* [- t! A( a2 [1 a
点击“保存”后直接生成工程。我们这里以生成IAR工程为例,项目名叫做HID。" `! {% |# @' B" T3 h
$ D7 ], S, ^1 j2 G

5 J6 u  k, G1 n4 |0 ?" G* x4 R: ]% Q
) \% r: k) v. {1 u* z) g这样我们的工程就基本成功了,但是还缺少最最关键的一步,就是USB主机和从机的通讯“协议”,这个协议在那里实现呢?因为我们Host端软件已经是Usb Hid Demonstrator,那么这边的协议就已经固定了(其实在实际的开发中大多是主机端和从机相互沟通后,软件自行修改的),从机只需要对应这套协议即可。% G$ A+ f; [+ h  ^  G" K
将如下代码复制,替换掉usbd_custom_hid_if.c文件中的同名数组。4 p2 d& r& J& ]4 C6 y" ^

/ L; d6 p& h6 o" m% e- V; Q3 I( ^4 c, f2 R4 B* w& b" d

/ r% o2 d, e8 ~, c; v3 a$ e' Y( _$ c& r# Q; N
& M; G/ w$ F  g/ \: T, F

$ W, T9 Y5 e1 f' z, q3 Z注意:这里一定要覆盖“同名”数组,千万不要覆盖错了。, e- O1 O8 L+ s3 m$ h
之后将如下代码复制到usbd_custom_hid_if_if.h中。
$ E2 U1 x% V3 e! p
8 r6 q1 X$ [0 L/ x6 b 6 A) F8 K. H: W: T& W, L" j
$ q* ~. p8 c  i2 J
最后在usbd_conf.h文件中将USBD_CUSTOM_HID_REPORT_DESC_SIZE的定义值修改为163(默认值是2)9 ]1 T0 }* t+ N" U7 T' A

6 X2 C( J8 o, m2 Z; x0 E) }为什么这样修改呢? 简单说一下其中关键值的含义。这个HID 的报文描述符其实定义了8个部分(条目)的功能定义,分为LED1,LED2,LED3,LED4,按键输入,篡改按键输入和ADC输入。每一个部分基本的格式都是固定的。以LED1为例(其他条目可自行对照文档解析):
$ b1 C0 h! p6 `4 I2 g# S/ m" [# I8 Y0 N
0x85, LED1_REPORT_ID, 含义是这个功能的ID号是LED1_REPORT_ID(宏定义为0x01)( e* F; u# E8 I, @- C" K0 q4 M+ N
这个ID号是每次报文发送的时候最先被发送出去的(USB都是LSB)字节,之后跟着的才是我们实际有效的数据/指令,到底是数据还是指令,就看你的应用程序如何去解析这个数据了。: g. Y- d7 X8 J0 w" B! G( s: d

+ S& i0 l* g% V5 z9 K4 q& ?5 t, a0x09, 0x01, 这个功能序号为1,后边的序号依次递加,没什么好说的; [# M" f; E, w  C( R0 D( k
0x15, 0x00, 这个是规定逻辑最小值为0( Z2 S' L! b5 ?5 d- j9 t( t0 t4 k
0x25, 0x01, 这个是规定逻辑最大值为1
! s$ w/ @$ D8 w, Q  c- G上边的这两条语句规定了跟在报文ID后边的数据范围,最大值是1,最小值是0.(因为我们的LED也就只有灭和亮两种状态)! O# Y- V. K( a& T8 Q" Z7 Z( U& h
  a% D+ d* C4 n& `( b5 `
0x75, 0x08, 这个是报文的大小为8,只要别写错就行了
8 t; A& S" Z* n# M5 c0x95, LED1_REPORT_COUNT, 这个是说下边有LED1_REPORT_COUNT (宏定义为1)个项目会被添加,即这个功能的数量是1个3 I* h7 y! Y% e
0xB1, 0x82, 这个是规定能够发送给从机设备的数据信息
8 a. ~- [( A, R0x91, 0x82, 这个规定了这个功能的数据方向是输出(USB的方向都是针对主机来说的)) C7 }; y2 Q0 f0 S( W. ^. t+ A
总结一下,通过这个报文描述符,我们就告诉了主机,在HID中有一个功能ID为1的功能,其方向是从主机到从机,每次发送1个有效数据(前边的ID是都要含有的),这个数据可以是0或者是1.' d( C; Y8 b; c; _- v, i
关于HID 报文描述符的详细信息,您可以在下边的网址下载一篇叫做《Device Class Definition for HID》的文档来参考。
' g2 e/ d' q; s5 X& T* {! ^http://www.usb.org/developers/hidpage- p) \2 g% [; b5 Z3 i' D

4 l9 w' j: w6 e) H% t! i# ?这样基本的程序框架就已经成功了。此时我们可以先编译一下,看看是否有任何遗漏的或者笔误。如果编译是正确的,那么我们就可以先下载到硬件开发板上,连接到PC端,看看是否可以枚举出设备。如果您前边的修改都是正确的,那么在PC的设备管理器中会看到如下图所示的内容。
" w: K* Q& n. L5 ?+ t& T
1 b% O% Z# k' W) z: W
: I. i* k, m9 b& K6 G& C+ C8 v: `: e
注意:开发板上有两个一模一样的Mini USB接口,一个是USB USER,另 一个是USB ST-link,下载代码的时候用USB ST-Link,连接电脑运行程序的时候要用USB USER。
7 U2 Q* y: U. y7 N8 J% K3 E& m此时我们的USB枚举就完成了,这个是USB通讯的关键步骤,之后的应用通讯内容都是通过这个枚举工程来进行“规划”的。" t3 H0 I  L7 R; A+ u  L
: y! e2 P1 }5 n
数据发送
/ {# w& k9 x9 u! N! X就类似串口通讯,我们首先做一个数据的发送工作。/ d! A7 s1 x& I4 \+ `: _  m
在Main.c文件中,我们在while(1)的主循环中增加我们的发送函数,主要就是调用发送报文的API:USBD_CUSTOM_HID_SendReport()
4 _9 `2 m! Z* M; O. o. [3 G! G
5 d4 _+ ~% Y; s8 j5 E1 C8 ~2 P" A
! q6 G7 _$ {( Q! R/ f
/ S8 q" `4 ^5 X1 a$ P1 Z; W6 m编译后下载到MCU内,连接上位机软件即可看到如下所示的进度条在不断的增长。: T7 I$ w7 c6 a' _7 N& U$ D
/ f: R: R2 g# I, S/ O  Y* f. |/ D

& c. C2 Y6 F. `$ I
3 X  H# g: E7 u6 w0 k- }这个就是我们上传到的数据在上位机的图形显示,你也可以看Input/output transfer里的数据变化。; V4 i4 A$ l. Q/ S1 o) Y" J
; Q$ S$ ~4 W* d2 n0 r2 |# C3 O2 D
% f  L2 {7 `4 F5 y- e; S  X
+ i0 l  M  o) P. b
这样看起来是不是更像是串口调试助手了?嘿嘿 本来机制就差不多的。
+ Q& l& W  [3 ]: @) z, _* d2 F7 V
数据接收  U2 j' G- x+ X* t! Z  e$ t7 W
MCU的USB数据是如何接收的呢?是不调用一个类似于串口接收的API呢?. Z0 u- R" \  P" p$ w: Y
不是的!USB的数据接收都是在中断中完成的,在新建的工程中,我们在函数CUSTOM_HID_OutEvent_FS内增加如下代码。" h; E: _8 r/ Q

+ \& M) k6 k- {( P' A
  N) T8 I9 d. C% i+ w* t  p3 B2 K- _8 {# |
' T. Z! c: l* F8 l! a编译之后下载到MCU内,通过USB USER连接到PC端,打开Usb Hid Demonstrator,我们可以通过勾选右下角的图形界面来实现控制开发板上的LED电量或者关闭。% J2 @9 @( K) @& p

) R# R3 ^' X% V) J! h  k * I. H  u) x" A4 @1 y" {
, z6 A$ g1 k" |0 f% f, [) J% V' a
当然,这个是通过图像化的界面来进行控制,你也可以通过Input/output transfer中的写入对话框来完成这个操作。注意,写入的第一个字节是ID,表示你想控制的是哪个LED;第二个字节是0或者是1,表示你想让这个LDE的状态变成灭还是亮。
  ]1 Y" Y3 Q, w0 _, \; k8 u3 M  ?; \" s2 w2 r, P; K

) u* b, r  X$ f' x5 E8 I5 q" ~5 M2 s6 y) @! `5 b) N2 J, q3 E
总结: 本范例程序是为了快速实现USB 从机设备与主句设备双向通讯目的,其初始化代码是用STM32CubeMX来生成的,大大降低了工程师开发USB设备的难度(尤其是是入门阶段的难度)。从这个工程的基础上,工程师可以比较方便的建立好框架工程并,对其中的代码进行研究,进而移植或增加自己的应用代码。( U9 `) O& ^8 _/ {, Z9 r

3 p/ O+ Y6 y& c文档和代码下载地址:% k; d6 ~1 J7 M
https://www.stmcu.org.cn/document/detail/index/id-217181
" Z+ y4 ?" K4 ]
- ~) |; Q* t3 Y5 \/ [8 ^9 G
工程
0 W7 }* O3 g+ g1 G, K. }) U2 A3 R* b
' t! [9 L; z2 t9 k2 ?7 g/ E
17.png(37.22 KB, 下载次数: 0)

* @8 n0 X* T0 F4 ^  {$ T! K
- X" d( B9 K& m, m7 @1 f1 p
+ i7 v" e0 L3 F+ ~2 I
16.png(37.78 KB, 下载次数: 0)

  v/ h1 u2 b8 g$ v
% S, M! E  `8 _9 J" Q; E/ H2 M: y5 U. T) ^& `2 Z% o
15.png(52.58 KB, 下载次数: 0)
/ y$ f4 A, d" ~  Z

& p5 P7 S+ ]* v) V; ]1 h- X
/ u8 a! q/ r" \: z3 }! a1 |
14.png(34.56 KB, 下载次数: 0)
9 }! H  r  X- I' i& M. F" S' i

5 G. H1 m' \8 s. L" ], E
2 Q2 T- @6 g2 X2 R
13.png(38.14 KB, 下载次数: 0)
5 i! B( C+ A, e
4 r# x3 g6 _+ i3 I# ]

' B' u' u& f9 Q4 H& I  [) j& C9 k+ g0 e7 G. _/ Z' C
文档和代码下载地址:
. }3 A+ i. b2 n. Ihttps://www.stmcu.org.cn/document/detail/index/id-217181
8 V' P9 o6 p+ Q0 N; E

5 @) y& L$ e' y* s& H* t: v8 M/ [实战经验汇总:% m; n* E/ z8 Q* v7 y
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 编辑
) A& a3 I; b7 u
# D1 r  V: W# k如果不在main函数中的while中调用USBD_CUSTOM_HID_SendReport()这个函数,为什么就不能继续使用HID了,有大神为我指点迷津不
. L" w4 W4 ^7 C' l9 F
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 编辑 5 M7 u: g  \7 M4 q- X+ L2 o

4 z- S: g0 K3 K楼主   移植你的代码 通了,,现在我想修改下使用 发现设备无法被识别  使枚举失败了?& M+ ?, u( G- l2 e
我修改的如下:
7 g7 ~$ B4 V- {我将ADC IN 的描述注释掉,在对应的头文件里将ADC的 ID 宏定义也注释掉,,
. f9 f% H" r7 F, j2 t6 p; c接着我把#define USBD_CUSTOM_HID_REPORT_DESC_SIZE     163//2     这里的163改为144(这里的大小是根据 请求描述里的设置字节数对应的吗?)
3 R2 ]8 H: F$ }3 M编译后下载  HID 设备就无法识别了,但是一换回去 就又可以通信了  。。  是不可以修改么还是哪里改错了??    有了解的也请指点一下  感谢!0 v3 G3 w# c$ U- n7 h$ M) F$ E& e

点评

你好,本文出自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 手机版