通过STM32CubeMX生成HID双向通讯工程 前言- 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 问题分析* W% G9 X" m" `& K' g7 k; [8 u4 A# X( h USB Host软件 PC端软件使用ST免费提供的Usb Hid Demonstrator。这个软件可以在ST官网上免费下载到。连接地址:STSW-STM32084,此软件调用的是windows标准的HID类驱动,所以无需安装任何驱动程序及可运行。 , V& i8 Q* {0 T! s0 c 下载安装完这个软件之后,我们就可以开始开发STM32的USB 从机程序了。 首先,打开STM32CubeMX,新建工程,选择STM32F072B-DISCOVERY开发板。 / t) ]! ]/ J5 w 9 O' d* _1 S( W2 M. r 8 D( W1 u: u+ l 其次,在Pinout选项中,开打USB的device功能。 , 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 4 u* [- t! A( a2 [1 a 点击“保存”后直接生成工程。我们这里以生成IAR工程为例,项目名叫做HID。" `! {% |# @' B" T3 h $ D7 ], S, ^1 j2 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" ^ ( ^4 c, f2 R4 B* w& b" d $ e' Y( _$ c& r# Q; N & M; G/ w$ F g/ \: T, F 注意:这里一定要覆盖“同名”数组,千万不要覆盖错了。, e- O1 O8 L+ s3 m$ h 之后将如下代码复制到usbd_custom_hid_if_if.h中。 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 为什么这样修改呢? 简单说一下其中关键值的含义。这个HID 的报文描述符其实定义了8个部分(条目)的功能定义,分为LED1,LED2,LED3,LED4,按键输入,篡改按键输入和ADC输入。每一个部分基本的格式都是固定的。以LED1为例(其他条目可自行对照文档解析): / 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 0x09, 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 上边的这两条语句规定了跟在报文ID后边的数据范围,最大值是1,最小值是0.(因为我们的LED也就只有灭和亮两种状态)! O# Y- V. K( a& T8 Q" Z7 Z( U& h a% D+ d* C4 n& `( b5 ` 0x75, 0x08, 这个是报文的大小为8,只要别写错就行了 0x95, LED1_REPORT_COUNT, 这个是说下边有LED1_REPORT_COUNT (宏定义为1)个项目会被添加,即这个功能的数量是1个3 I* h7 y! Y% e 0xB1, 0x82, 这个是规定能够发送给从机设备的数据信息 0x91, 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》的文档来参考。 http://www.usb.org/developers/hidpage- p) \2 g% [; b5 Z3 i' D 这样基本的程序框架就已经成功了。此时我们可以先编译一下,看看是否有任何遗漏的或者笔误。如果编译是正确的,那么我们就可以先下载到硬件开发板上,连接到PC端,看看是否可以枚举出设备。如果您前边的修改都是正确的,那么在PC的设备管理器中会看到如下图所示的内容。 & C+ C8 v: `: e 注意:开发板上有两个一模一样的Mini USB接口,一个是USB USER,另 一个是USB ST-link,下载代码的时候用USB ST-Link,连接电脑运行程序的时候要用USB USER。 此时我们的USB枚举就完成了,这个是USB通讯的关键步骤,之后的应用通讯内容都是通过这个枚举工程来进行“规划”的。" t3 H0 I L7 R; A+ u L : y! e2 P1 }5 n 数据发送 就类似串口通讯,我们首先做一个数据的发送工作。/ d! A7 s1 x& I4 \+ `: _ m 在Main.c文件中,我们在while(1)的主循环中增加我们的发送函数,主要就是调用发送报文的API:USBD_CUSTOM_HID_SendReport() 编译后下载到MCU内,连接上位机软件即可看到如下所示的进度条在不断的增长。: T7 I$ w7 c6 a' _7 N& U$ D / f: R: R2 g# I, S/ O Y* f. |/ D 这个就是我们上传到的数据在上位机的图形显示,你也可以看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 这样看起来是不是更像是串口调试助手了?嘿嘿 本来机制就差不多的。 : @) 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 编译之后下载到MCU内,通过USB USER连接到PC端,打开Usb Hid Demonstrator,我们可以通过勾选右下角的图形界面来实现控制开发板上的LED电量或者关闭。% J2 @9 @( K) @& p * 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的状态变成灭还是亮。 ?; \" s2 w2 r, P; K 5 M2 s6 y) @! `5 b) N2 J, q3 E 总结: 本范例程序是为了快速实现USB 从机设备与主句设备双向通讯目的,其初始化代码是用STM32CubeMX来生成的,大大降低了工程师开发USB设备的难度(尤其是是入门阶段的难度)。从这个工程的基础上,工程师可以比较方便的建立好框架工程并,对其中的代码进行研究,进而移植或增加自己的应用代码。( U9 `) O& ^8 _/ {, Z9 r 文档和代码下载地址:% k; d6 ~1 J7 M https://www.stmcu.org.cn/document/detail/index/id-217181" Z+ y4 ?" K4 ] - ~) |; Q* t3 Y5 \/ [8 ^9 G 工程 17.png(37.22 KB, 下载次数: 0) 16.png(37.78 KB, 下载次数: 0) 2 M: y5 U. T) ^& `2 Z% o 15.png(52.58 KB, 下载次数: 0) / y$ f4 A, d" ~ Z14.png(34.56 KB, 下载次数: 0) 9 }! H r X- I' i& M. F" S' i13.png(38.14 KB, 下载次数: 0) 5 i! B( C+ A, e4 r# x3 g6 _+ i3 I# ] 9 k+ g0 e7 G. _/ Z' C 文档和代码下载地址: https://www.stmcu.org.cn/document/detail/index/id-2171818 V' P9 o6 p+ Q0 N; E 实战经验汇总:% m; n* E/ z8 Q* v7 y https://www.stmcu.org.cn/module/forum/thread-576401-1-1.html |
如果不在main函数中的while中调用USBD_CUSTOM_HID_SendReport()这个函数,为什么就不能继续使用HID了,有大神为我指点迷津不
楼主 移植你的代码 通了,,现在我想修改下使用 发现设备无法被识别 使枚举失败了?& M+ ?, u( G- l2 e
我修改的如下:
我将ADC IN 的描述注释掉,在对应的头文件里将ADC的 ID 宏定义也注释掉,,
接着我把#define USBD_CUSTOM_HID_REPORT_DESC_SIZE 163//2 这里的163改为144(这里的大小是根据 请求描述里的设置字节数对应的吗?)
编译后下载 HID 设备就无法识别了,但是一换回去 就又可以通信了 。。 是不可以修改么还是哪里改错了?? 有了解的也请指点一下 感谢!0 v3 G3 w# c$ U- n7 h$ M) F$ E& e
点评