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

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

[复制链接]
zero99 发布时间:2016-7-12 14:57
通过STM32CubeMX生成HID双向通讯工程
2 C1 I6 V/ T2 ]7 q  V4 ]4 c" m
前言
3 H3 Y2 l+ [! N) L1 B客户在做USB通讯的时候,基本的需求就是发送某些数据到USB host端,同时接收一些数据从USB Host端,那么如何快速的建立一个工程并验证数据是否正确呢?下边我们就结合STM32F072的评估板(其他的STM32xx系列的实现方式都是类似的)来快速实现一个简单的数据收发实验。
/ o; @2 c9 W8 A2 t: e* v, V
: M% |! |9 ?$ Q7 ]. ]/ c3 D问题分析( t# Q* _& v' F4 M  O7 A: \
USB Host软件5 u4 C5 h# u/ d5 z  r1 s
PC端软件使用ST免费提供的Usb Hid Demonstrator。这个软件可以在ST官网上免费下载到。连接地址:STSW-STM32084,此软件调用的是windows标准的HID类驱动,所以无需安装任何驱动程序及可运行。
# W- _3 L: ^6 [+ V1 l
0 x# W$ E% m6 O0 P- d; b ; a+ n+ j& {' Q

; A1 {$ K3 {. |, J  `7 U下载安装完这个软件之后,我们就可以开始开发STM32的USB 从机程序了。
0 v' p( L9 E- M5 `7 P5 L) O, N- o$ o9 \首先,打开STM32CubeMX,新建工程,选择STM32F072B-DISCOVERY开发板。  }! y1 T! y4 Q
; J- k6 g% R9 A! \& \" c1 T; y

+ C+ E( Z8 k) R/ t, f3 j7 D2 ?4 Y5 W' @" N+ m% Z- ]$ `  n
其次,在Pinout选项中,开打USB的device功能。7 q/ @. b  R' U  d# x1 Y
9 Q. S1 K0 W" {" U, Y6 }0 W% y

* d, u7 {, k3 l7 d( n/ x# S, P/ r; @3 p
并在Middleware中选择开启class for IP中的 custom Human Interface Device(HID).
+ h. Z- I$ J# I  v6 W+ O# w# \, W( `; C$ O. A! y$ u

/ M8 ~* c1 i: D, B9 X7 W- C
# x! ]& ~/ E' ~9 N3 U, W/ L; U5 M点击“保存”后直接生成工程。我们这里以生成IAR工程为例,项目名叫做HID。
2 B8 G! ?! X! a; J! s! G5 R* G) i. L

/ R0 H3 D9 j5 @' P7 {0 p3 [3 k% u* ^
这样我们的工程就基本成功了,但是还缺少最最关键的一步,就是USB主机和从机的通讯“协议”,这个协议在那里实现呢?因为我们Host端软件已经是Usb Hid Demonstrator,那么这边的协议就已经固定了(其实在实际的开发中大多是主机端和从机相互沟通后,软件自行修改的),从机只需要对应这套协议即可。
4 r6 M/ d( x, t2 t将如下代码复制,替换掉usbd_custom_hid_if.c文件中的同名数组。# l2 A/ _4 N; h

% l; E: {" x3 D3 i9 G
; e4 b$ l, e% k1 y % f- G( |( s+ p6 b- @( H

4 v, @6 b4 F, q) y
: s; U9 t- A+ C4 v) n/ W* A6 m4 W3 ], Y8 i4 z# i" u1 J/ J
注意:这里一定要覆盖“同名”数组,千万不要覆盖错了。( ^' Y& I( B7 ~! a7 z/ p
之后将如下代码复制到usbd_custom_hid_if_if.h中。 9 c# P0 a  |6 d0 _9 }- o  m1 k3 z' u
' r* |, W+ F5 K5 f7 K' r
% c% z! a& p% L# |' {8 u. [

, V9 ?4 d: c3 h" Q& m  ^7 u" S3 ?* o最后在usbd_conf.h文件中将USBD_CUSTOM_HID_REPORT_DESC_SIZE的定义值修改为163(默认值是2)+ F6 x% r) H2 p) @/ J2 e

% W; n4 Q+ E7 o% W2 Y% M为什么这样修改呢? 简单说一下其中关键值的含义。这个HID 的报文描述符其实定义了8个部分(条目)的功能定义,分为LED1,LED2,LED3,LED4,按键输入,篡改按键输入和ADC输入。每一个部分基本的格式都是固定的。以LED1为例(其他条目可自行对照文档解析):
8 r2 i& \: T7 Y+ [1 y1 {% G" R+ y5 }
0x85, LED1_REPORT_ID, 含义是这个功能的ID号是LED1_REPORT_ID(宏定义为0x01)1 h: y+ l! X7 j
这个ID号是每次报文发送的时候最先被发送出去的(USB都是LSB)字节,之后跟着的才是我们实际有效的数据/指令,到底是数据还是指令,就看你的应用程序如何去解析这个数据了。
2 A4 f9 S1 z/ g- z5 i, H% o" ?7 m# U# v1 N. j$ A/ ^$ j! b* M/ z
0x09, 0x01, 这个功能序号为1,后边的序号依次递加,没什么好说的
2 @  Q3 s9 Q* ^- j0x15, 0x00, 这个是规定逻辑最小值为0
# H. [7 L' @+ {% E4 z5 e  `, e5 F9 ]6 i0x25, 0x01, 这个是规定逻辑最大值为17 |6 w* J/ O# y6 R* V# X1 S2 Y
上边的这两条语句规定了跟在报文ID后边的数据范围,最大值是1,最小值是0.(因为我们的LED也就只有灭和亮两种状态)6 _8 Y4 k! u4 W9 A; O

, F3 X# d' M* ^: I+ P6 E7 ~0x75, 0x08, 这个是报文的大小为8,只要别写错就行了
% r4 z5 D4 z0 s- {# l' `0x95, LED1_REPORT_COUNT, 这个是说下边有LED1_REPORT_COUNT (宏定义为1)个项目会被添加,即这个功能的数量是1个2 l& J  X8 f6 y/ ^+ _
0xB1, 0x82, 这个是规定能够发送给从机设备的数据信息9 _4 h. i7 u' B* ~& ?
0x91, 0x82, 这个规定了这个功能的数据方向是输出(USB的方向都是针对主机来说的)
0 e) a/ g2 W+ j1 l总结一下,通过这个报文描述符,我们就告诉了主机,在HID中有一个功能ID为1的功能,其方向是从主机到从机,每次发送1个有效数据(前边的ID是都要含有的),这个数据可以是0或者是1.
" W- z' ^$ S1 s关于HID 报文描述符的详细信息,您可以在下边的网址下载一篇叫做《Device Class Definition for HID》的文档来参考。
% P$ r( q: s* Q' Phttp://www.usb.org/developers/hidpage7 Z/ J- a) J9 o5 f' c+ e% t3 D
1 H7 u4 a) D  z/ {1 m+ _1 P9 c0 x9 N4 Q
这样基本的程序框架就已经成功了。此时我们可以先编译一下,看看是否有任何遗漏的或者笔误。如果编译是正确的,那么我们就可以先下载到硬件开发板上,连接到PC端,看看是否可以枚举出设备。如果您前边的修改都是正确的,那么在PC的设备管理器中会看到如下图所示的内容。
3 B" `8 z. O+ x7 V
" R8 |% L- I% o7 w9 ] 8 _" k, X; v- v
# q  O: `7 t2 R1 U
注意:开发板上有两个一模一样的Mini USB接口,一个是USB USER,另 一个是USB ST-link,下载代码的时候用USB ST-Link,连接电脑运行程序的时候要用USB USER。" b7 `* _: z4 A4 E* @+ [) Z
此时我们的USB枚举就完成了,这个是USB通讯的关键步骤,之后的应用通讯内容都是通过这个枚举工程来进行“规划”的。
* H& w* P- \; W: y! T1 J/ Q% R& W4 o* c) B$ }2 P
数据发送3 J  M+ }" V$ _/ n3 d( h5 p/ [
就类似串口通讯,我们首先做一个数据的发送工作。
$ N/ f! \. \1 I, k在Main.c文件中,我们在while(1)的主循环中增加我们的发送函数,主要就是调用发送报文的API:USBD_CUSTOM_HID_SendReport(). \, ]  B2 e' ?, o( [! Y8 R

- D; X$ I* G. e: g ) o* P5 G9 d2 a& f. t
/ o2 o6 Y, ]6 y5 D) f
编译后下载到MCU内,连接上位机软件即可看到如下所示的进度条在不断的增长。
5 c* w9 c+ c! {8 F" e- s( N3 k4 M8 f
# C2 Y4 @0 x6 Z+ A; M  A# V" S  q & i5 E  a& K+ l

) U# |( n3 ?  j3 \7 W+ v1 W这个就是我们上传到的数据在上位机的图形显示,你也可以看Input/output transfer里的数据变化。
2 S5 {; U0 ^; K
4 ?# x8 |+ z* i6 c6 J  z% U2 M" D6 d9 ~+ r+ j: G+ y' _

$ s( ?$ x8 ]: L4 ?/ }( [; a这样看起来是不是更像是串口调试助手了?嘿嘿 本来机制就差不多的。
6 z2 e; O. l% i
$ B3 _$ h$ X2 C5 ]: v/ ~7 S数据接收4 Z% a( w; B$ H, n8 M
MCU的USB数据是如何接收的呢?是不调用一个类似于串口接收的API呢?
- x' \4 o, ^- ?# b. P9 D4 K: A不是的!USB的数据接收都是在中断中完成的,在新建的工程中,我们在函数CUSTOM_HID_OutEvent_FS内增加如下代码。
0 f8 m9 ?& B# w9 F# L) K6 f
3 n- }% Q7 C- ]  T7 l8 @$ o5 h; Y( J" x9 \; ]+ j
# Q/ `+ x7 ~4 t1 l( I: ?, P
编译之后下载到MCU内,通过USB USER连接到PC端,打开Usb Hid Demonstrator,我们可以通过勾选右下角的图形界面来实现控制开发板上的LED电量或者关闭。  P/ F3 ]7 w+ Z0 M* J
# L4 F" d# z0 x- j8 R  K4 [! Y; K6 r
) X' N) h% \/ Q* S
' g' B- I6 T* ~7 D0 s) d7 E
当然,这个是通过图像化的界面来进行控制,你也可以通过Input/output transfer中的写入对话框来完成这个操作。注意,写入的第一个字节是ID,表示你想控制的是哪个LED;第二个字节是0或者是1,表示你想让这个LDE的状态变成灭还是亮。6 g2 n8 s6 g/ m2 s5 c3 |! G$ u  t

  D% C: b- p* l6 @$ d2 I9 E* P ( ?6 ]! Y/ u/ s8 f: L3 [3 x: o' m
8 J5 ?, y) U% ^
总结: 本范例程序是为了快速实现USB 从机设备与主句设备双向通讯目的,其初始化代码是用STM32CubeMX来生成的,大大降低了工程师开发USB设备的难度(尤其是是入门阶段的难度)。从这个工程的基础上,工程师可以比较方便的建立好框架工程并,对其中的代码进行研究,进而移植或增加自己的应用代码。
1 I" Y' {: ~4 \3 w' D' l  H: {# A5 f' y' j- p. m3 r0 J1 K! l
文档和代码下载地址:
9 G+ A/ T( p9 K( [2 y- ^https://www.stmcu.org.cn/document/detail/index/id-217181

$ W$ O( Y( r. P! X/ v% A. K: q4 d* \% X! p; y3 b
工程5 {* H9 E" ?4 Z! f6 }

3 ?4 x: N! f, @  B
17.png(37.22 KB, 下载次数: 0)
- v) @0 `. O3 m) N! C: o& O
, F9 v, H* X8 J8 |+ m

" y% c  K3 p+ ]" q. @; _
16.png(37.78 KB, 下载次数: 0)

. L0 u& f& ?& M9 p% l
+ J! M- r: |7 y/ c9 T0 a) T1 o4 H0 h* ]" Y) H7 s
15.png(52.58 KB, 下载次数: 0)
9 g1 s% C# n& n0 e' |  @
# O; x! b$ [( [/ {8 [; G& Q) p
# }# j* S- I0 w" X# O: y2 x( ]
14.png(34.56 KB, 下载次数: 0)
% X1 m7 L$ w/ ]1 m5 ?  z/ w
# ?- m# Q0 z% k
: g8 l3 z0 `4 A
13.png(38.14 KB, 下载次数: 0)

( ~5 ?' _7 K6 w% f' d; t, K# X
- H7 M* S6 q7 \. K7 r/ z, d' P. s
/ J: I4 ~- G9 Z, k7 K, M. X7 k- h
文档和代码下载地址:
$ M8 F' v' i# Y  J5 C; y% g. jhttps://www.stmcu.org.cn/document/detail/index/id-217181
6 H6 R3 s, ?; K9 r0 |

4 e1 S8 Z2 c" f+ {实战经验汇总:8 K2 u/ Q! ?: f. S0 f# T$ c5 U
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 编辑
* n" C6 U  t' |% R$ j$ v% A- |+ q* K( d
如果不在main函数中的while中调用USBD_CUSTOM_HID_SendReport()这个函数,为什么就不能继续使用HID了,有大神为我指点迷津不
" z" s$ ]7 J; J1 Z  o6 G3 S. E
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 编辑 ) }- R& c& z  y; k. m
( X% Y1 {6 D* Z1 |9 y
楼主   移植你的代码 通了,,现在我想修改下使用 发现设备无法被识别  使枚举失败了?; V# `& B% ^$ K+ J
我修改的如下:( f$ o, q5 E% I( ~' D4 z
我将ADC IN 的描述注释掉,在对应的头文件里将ADC的 ID 宏定义也注释掉,,
# h/ A! W; Q* c) ?/ L: q接着我把#define USBD_CUSTOM_HID_REPORT_DESC_SIZE     163//2     这里的163改为144(这里的大小是根据 请求描述里的设置字节数对应的吗?)
8 n+ Q, y/ {0 k9 I3 a编译后下载  HID 设备就无法识别了,但是一换回去 就又可以通信了  。。  是不可以修改么还是哪里改错了??    有了解的也请指点一下  感谢!
0 [! R0 H! P* q* y, Q" |) Z

点评

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