通过STM32CubeMX生成HID双向通讯工程: ]6 E8 w7 N" p 前言客户在做USB通讯的时候,基本的需求就是发送某些数据到USB host端,同时接收一些数据从USB Host端,那么如何快速的建立一个工程并验证数据是否正确呢?下边我们就结合STM32F072的评估板(其他的STM32xx系列的实现方式都是类似的)来快速实现一个简单的数据收发实验。0 Q( m' e! Q5 [8 [/ V. F ; X& i- B' g9 |8 O 问题分析 USB Host软件 PC端软件使用ST免费提供的Usb Hid Demonstrator。这个软件可以在ST官网上免费下载到。连接地址:STSW-STM32084,此软件调用的是windows标准的HID类驱动,所以无需安装任何驱动程序及可运行。 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 其次,在Pinout选项中,开打USB的device功能。: M3 a2 @8 t" H" q6 H 并在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 点击“保存”后直接生成工程。我们这里以生成IAR工程为例,项目名叫做HID。0 o F4 ?$ s: l: j' O* H$ O & Z- a5 J: j8 u/ G2 ^6 u! ^/ L& R5 } ! e# L, ~1 U8 A5 L5 H2 T 这样我们的工程就基本成功了,但是还缺少最最关键的一步,就是USB主机和从机的通讯“协议”,这个协议在那里实现呢?因为我们Host端软件已经是Usb Hid Demonstrator,那么这边的协议就已经固定了(其实在实际的开发中大多是主机端和从机相互沟通后,软件自行修改的),从机只需要对应这套协议即可。 将如下代码复制,替换掉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 ! s2 e. Z9 M. j; g( l; Z 最后在usbd_conf.h文件中将USBD_CUSTOM_HID_REPORT_DESC_SIZE的定义值修改为163(默认值是2) 为什么这样修改呢? 简单说一下其中关键值的含义。这个HID 的报文描述符其实定义了8个部分(条目)的功能定义,分为LED1,LED2,LED3,LED4,按键输入,篡改按键输入和ADC输入。每一个部分基本的格式都是固定的。以LED1为例(其他条目可自行对照文档解析): * 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 0x09, 0x01, 这个功能序号为1,后边的序号依次递加,没什么好说的 0x15, 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》的文档来参考。 http://www.usb.org/developers/hidpage8 ?4 H, v. i9 `9 T 这样基本的程序框架就已经成功了。此时我们可以先编译一下,看看是否有任何遗漏的或者笔误。如果编译是正确的,那么我们就可以先下载到硬件开发板上,连接到PC端,看看是否可以枚举出设备。如果您前边的修改都是正确的,那么在PC的设备管理器中会看到如下图所示的内容。& R# A x: P4 Q3 S: V* J. P# p * |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 数据发送( X" M+ j5 h+ j9 v# s) g O 就类似串口通讯,我们首先做一个数据的发送工作。 在Main.c文件中,我们在while(1)的主循环中增加我们的发送函数,主要就是调用发送报文的API:USBD_CUSTOM_HID_SendReport()- [# G- o: M7 e% ~5 i- T$ h/ k 编译后下载到MCU内,连接上位机软件即可看到如下所示的进度条在不断的增长。8 [: o: ~5 t6 P- g' B + G" [$ V; m& \( Y9 ~! J 这个就是我们上传到的数据在上位机的图形显示,你也可以看Input/output transfer里的数据变化。9 d# L) U- S4 n7 N9 E 这样看起来是不是更像是串口调试助手了?嘿嘿 本来机制就差不多的。 数据接收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 * z' K: n$ d4 }9 P# j# Q 当然,这个是通过图像化的界面来进行控制,你也可以通过Input/output transfer中的写入对话框来完成这个操作。注意,写入的第一个字节是ID,表示你想控制的是哪个LED;第二个字节是0或者是1,表示你想让这个LDE的状态变成灭还是亮。 总结: 本范例程序是为了快速实现USB 从机设备与主句设备双向通讯目的,其初始化代码是用STM32CubeMX来生成的,大大降低了工程师开发USB设备的难度(尤其是是入门阶段的难度)。从这个工程的基础上,工程师可以比较方便的建立好框架工程并,对其中的代码进行研究,进而移植或增加自己的应用代码。 j: w4 ?; P( {' B' [ ( J q0 u# V$ e: I- U$ W7 d4 ]. T" R 文档和代码下载地址: https://www.stmcu.org.cn/document/detail/index/id-2171817 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) 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 15.png(52.58 KB, 下载次数: 0) % \) h6 x: i- a- W# H }14.png(34.56 KB, 下载次数: 0) * @( a7 @0 S% V9 o, F, N 13.png(38.14 KB, 下载次数: 0) * X* b1 V. \; B * w* W0 ~% F! g0 `' C* m* S Z' a 文档和代码下载地址: https://www.stmcu.org.cn/document/detail/index/id-217181 实战经验汇总:* P+ U; R' d5 S6 k D9 U9 j https://www.stmcu.org.cn/module/forum/thread-576401-1-1.html |
如果不在main函数中的while中调用USBD_CUSTOM_HID_SendReport()这个函数,为什么就不能继续使用HID了,有大神为我指点迷津不0 {; Q, E+ z1 Y0 j' Z/ R
0 G% `3 J& m, z) ]5 j
楼主 移植你的代码 通了,,现在我想修改下使用 发现设备无法被识别 使枚举失败了?
我修改的如下:% 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(这里的大小是根据 请求描述里的设置字节数对应的吗?)
编译后下载 HID 设备就无法识别了,但是一换回去 就又可以通信了 。。 是不可以修改么还是哪里改错了?? 有了解的也请指点一下 感谢!
点评