STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发

发布者:电子科技爱好者最新更新时间:2025-02-18 来源: cnblogs关键字:STM32  Cubemx  数据收发 手机看文章 扫描二维码
随时随地手机看文章

这里记录一下如何做一个USB下位机,这里主要分3部分:1、建立工程;2、添加报文描述符;3、数据的传输。这里就不讲USB的理论知识了,有想要了解的百度一下就可以了。

 

建立工程:工程建立参考:https://www.cnblogs.com/libra13179/p/7193375.html

 

  1、首先打开USB

  

  

  2、接着把USB设置为下图HID模式

  

  3、选择外部时钟

  

  4、配置时钟树

  

  5、配置USB设置

  

  

  下面的USB设置就有点讲究了,

  

  USBD_CUSTOM_HID_REPORT_DESC_SIZE 这个是指报告描述符所用的字节数为34(默认为2);USBD_CUSTOMHID_OUTREPORT_BUF_SIZE 表示输入输出端点一次传输数据大小最大为64个字节(默认为2)

  【 补充:现在新版本的STM32Cumebx在这个配置里还多了一项CUSTOM_HID_FS_BINTERVAL,这个是指数据传输的最小时间间隔(ms)这里修改为跟下面USB传输部分中修改时间间隔一致 】

 

6、确认后,生成代码

  补充:生成代码的时候注意把堆的大小改大一点,不然容易出现设备识别出来出现感叹号的情况

   

  最后生成项目

  

 

添加报文描述符:

 

  这里只是建立了一个工程,你编译不会有错,不过PC还是无法识别你这个是USB设备的,因为缺少了关键的报文描述符,这里我就说一下报文描述符怎么写,这个你可以直接用HID descriptor Tool软件生成,如下图,下图就是一个报文的基本要素了,该描述符主要作用是告诉PC机一下USB的信息,简单来说就是告诉PC我有什么用。

 HID descriptor Tool 百度云下载链接:https://pan.baidu.com/s/1BbEV3pak0KgVXD8GHtzLow ;提取码:nalw 。

  

  这里我说一下这个报文的要素,第一部分你可以理解为报文头;第二部分你可以理解为USB告诉PC机问我要做什么,这里主要告诉PC机我要做一个接收与发送的设备,USAGE~INPUT这里是描述这个USB设备作为输入的时候的数据格式,下一个USAGE~OUTPUT是描述该USB设备输出的数据格式,其中LOGICAL_MINIMUM是指每个字节数据的最小值,LOGICAL_MAXIMUM是指每个字节数据的最大值,REPORT_COUNT是指定每次传输数据最多多少个字节,REPORT_SIZE是指定每次传输数据每个字节的位数;第三部分是结束标志。

============================================================

下面我插入一个对报文描述符进行一个简单的描述:

   

标签解析举例(Usage :0x50, 0x01):

  (Usage Page标签0x0?)0x05=0000 0101 :

  0x05表示前缀,0x01为数据部分,0x05转换成二进制,就是0000 01 01,按照HID类协议5.3 generic item format的定义,这个字节被分成3个部分:

  bit0~bit1代表的是这个前缀后面跟的数据长度,这里就是后面0x01的长度,两位可以表示最大4字节的数据,即bsize

  bit2~bit3代表的是这个前缀的类型,总共可以有三种类型:0=main,1=global,2=local,3=reserved;

  bit4~bit7代表前tag,一般分为input(二进制的1000 00 nn,即bit4~bit7=1000,代表一个tag,bit2~bit3=00,代表main,bit0~bit1=nn,代表这个前缀后面还有nn所代表的数据),output(二进制的 1001 00 nn),feature(1011 00 nn),collection(1010 00 nn),end collection(1100 00 nn)

即:

  0000:Usage Page

  01: bType,全局(bType=0:主项目;bType=1:全局项目;bType=2:区域项目)

  01:bSzie,1字节(bSzie为项目所需数据字节数目,bSzie可为1、2、4,注意bSzie不可为3)

 (Page ID)0x01: 表示该Page为Generalic Desktop Controls(Usage ID 0为保留。ID 1到0x1F为”top level” collection保留,这些ID虽然对于Application不是必须,但可以用于识别通用设备类型)

  

  Usage = (usage page:usage ID):其将数据的操控与它的用途作一对一的对应,所以解读报告后就可以知道每个数据作何种操作。所以“传输的数据”和“操作”只是一事件的两种描述方式。用途是以一个32位卷标(称作usage tag)来表示,高16位称作usage page(用途类页),低16位称为usage ID(用途识别名),文件universal serial Bus HID Usage Table完整列出所有的usage pages(用途类页)和usage ID(用途识别名),使用者必须遵照文件的规范来声明操作的用途。用途卷标只是报告描述符诸多标签的一个,利用这些卷标取可以清楚完整的描述符操作的用途。

  这里其实说的并不清楚,因为作者有点懒就不细说了,想了解更多的可以百度《圈圈教你玩USB》,这里说的比我好。

==============================================================================

 

言归正传,我们生成了报文描述符后,保存为.h文件,我对这些报文做了一些很浅显的解析,可能表达不够准确,打开可以看到如下代码所示:


char ReportDescriptor[34] = 

{                     //这里34就是前面建立工程第五点说的,报文描述符大小

    0x06, 0x00, 0xff,              // USAGE_PAGE (Vendor Defined Page 1) 表示一个报文标签之类的用途类页

    0x09, 0x01,                    // USAGE (Vendor Usage 1) 表示一个报告ID标志

    0xa1, 0x01,                    // COLLECTION (Application) 表示应用集合,要以下面最后的0xc0结束它



    0x09, 0x01,                    //   USAGE (Vendor Usage 1)同下同名解析

    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)  同下同名解析

    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255) 同下同名解析

    0x95, 0x40,                    //   REPORT_COUNT (64)  同下REPORT_COUNT

    0x75, 0x08,                    //   REPORT_SIZE (8)   同下REPORT_SIZE

    0x81, 0x02,                    //   INPUT (Data,Var,Abs) 表示USB要输入数据到PC的功能



    0x09, 0x01,                    //   USAGE (Vendor Usage 1) 每个功能的一个卷标志

    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)    表示每个传输数据限定为0

    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)    表示每个传输数据的最大值限定为255

    0x95, 0x40,                    //   REPORT_COUNT (64) 每次接收的数据长度,这里是64位

    0x75, 0x08,                    //   REPORT_SIZE (8)        传输字段的宽度为8bit,表示每个传输的数据范围为0~ffff ffff

    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs) 表示USB设备要接收PC的数据的功能

    0xc0                           // END_COLLECTION  结束标志

};


到这里我们就可以下一步了,打开刚才建立的工程,在 usbd_custom_hid_if.c 文件里,找到 CUSTOM_HID_ReportDesc_FS 这个函数,把刚才生成的报文文件覆盖掉函数里面的文件,代码如下:


/** Usb HID report descriptor. */

__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =

{

    0x06, 0x00, 0xff,              // USAGE_PAGE (Vendor Defined Page 1)

    0x09, 0x01,                    // USAGE (Vendor Usage 1)

    0xa1, 0x01,                    // COLLECTION (Application)

    0x09, 0x01,                    //   USAGE (Vendor Usage 1)

    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)

    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)

    0x95, 0x40,                    //   REPORT_COUNT (64)

    0x75, 0x08,                    //   REPORT_SIZE (8)

    0x81, 0x02,                    //   INPUT (Data,Var,Abs)

    0x09, 0x01,                    //   USAGE (Vendor Usage 1)

    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)

    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)

    0x95, 0x40,                    //   REPORT_COUNT (64)

    0x75, 0x08,                    //   REPORT_SIZE (8)

    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)

    0xc0                           // END_COLLECTION

};


然后再修改将usbd_conf.h做对应修改:

  #define USBD_CUSTOMHID_OUTREPORT_BUF_SIZE      64

  #define USBD_CUSTOM_HID_REPORT_DESC_SIZE       34

同时修改usbd_customhid.h文件中的发送与接收长度为64

  #define CUSTOM_HID_EPIN_SIZE                  0x40

  #define CUSTOM_HID_EPOUT_SIZE              0x40

  到这里基本就算做出一个USB设备了,我们编译下载程序看看。

 

========================【注意】===========================================

  如果电脑显示了这个USB设备,但是有黄色感叹号,说明USB枚举成功,可是驱动安装失败,这时我们可以libusb自带的inf-wizard工具生成USB驱动程序,libusb官网下载链接为https://sourceforge.net/projects/libusb-win32/,或者可以去百度云盘下载( 链接:https://pan.baidu.com/s/10GUwwhPZTYONBYbOutVBzA ;提取码:lf87 )。

  下载好文件后解压,找到下图的inf-wizar工具打开。

  

  打开后该工具可以扫描你电脑的USB设备,并获取设备的PID、VID。

  

   根据 PID/VID 找到你自己的USB设备,选择它点击Next。

  

   保持默认配置,Next即可,最后你自己选择你的USB设备驱动的安装路径,你可以自己在C盘建立个文件夹安装在里面即可。

 ===============================================================

 

USB数据传输:参考http://www.stm32cube.com/article/138

 

  关于数据传输,HID设备是采用轮询方式传输的,ST默认20ms速度实在不敢恭维,还得要改一下枚举时的声明(这里修改跟CUSTOM_HID_FS_BINTERVAL一致,旧版STM32Cumebx中没有这项配置修改可以直接修改usbd_conf.h文件里的CUSTOM_HID_FS_BINTERVAL),修改usbd_customhid.c文件(如果是新版STM32cubemx生成的工程的话,已经可以直接在工程里修改CUSTOM_HID_FS_BINTERVAL了,该宏就是这里使用的宏,如果你是用新版STM32Cubemx,并在里面修改了CUSTOM_HID_FS_BINTERVAL,这里不做修改都行,因为新版生成的项目这里直接就是使用CUSTOM_HID_FS_BINTERVAL的宏,你修改了CUSTOM_HID_FS_BINTERVAL的宏这里就跟着修改了)。


__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END =

{

 ....

 ....


  0x20,          /*bInterval: Polling Interval (20 ms)*/


  /* 34 */


 ....

 ....


  0x20,/* bInterval: Polling Interval (20 ms) */


  /* 41 */

}


这两个地方随心来改,最小可以改到0x01。这就快很多啦。到这里数据传输准备工作就做好了,下面我们先来说一下USB发送:


  先定义个发送BUFF:


uint8_t send_buf[64] = {//定义一个USB的发送BUFF

                         1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,

                         17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,

                         40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64}; 

  再包括发送函数头文件以及声明一个外部定义:


#include 'usbd_customhid.h' //包括发送函数头文件

extern USBD_HandleTypeDef hUsbDeviceFS; //外部声明USB的句柄

  现在可以在main函数里添加发送代码了,我这里设置按一下按键就发送一次,同时led亮1s:


  while (1)

  {

    /* USER CODE BEGIN 3 */

        if(HAL_GPIO_ReadPin(GPIOE,k1_Pin)==0)

        {    

            //按键消抖

            while(HAL_GPIO_ReadPin(GPIOE,k1_Pin)==0);

            //点亮指示灯

            HAL_GPIO_WritePin(GPIOE,led1_Pin,GPIO_PIN_RESET);

[1] [2]
关键字:STM32  Cubemx  数据收发 引用地址:STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发

上一篇:STM32使用HAL库实现ADC单通道转换
下一篇:STM32 Cubemx 输出可调频率与占空比的PWM

推荐阅读最新更新时间:2026-03-21 00:18

利用STM32CubeMx的串口DMA收发数据
一,代码生成 按以前的方法设置好时钟和调试方式,这里就不多说了。 2.设置串口1。 3.在DMA Setting里点击Add添加USART1_TX,Mode有两种模式,一种是普通模式,使用一次发送语句就发一次,另一种是循环模式,使用一次发送会一直发送。这里发送我选择普通模式,接收选择循环模式。 4.在中断设置里打开串口1的中断。 5.时钟和文件路径等设置好,然后点生成代码。 二,代码编写 1.先定义发送和接收的数组。 /* USER CODE BEGIN 0 */uint8_t aRxBuffer ;uint8_t aTxBuffer = ok ;/* USER CODE END
[单片机]
利用STM32<font color='red'>CubeMx</font>的串口DMA<font color='red'>收发</font><font color='red'>数据</font>
STM32如何收发float类型数据
在之前文章里提到了共用体用来传输浮点数的用法,但那篇笔记中没有详细介绍,这篇笔记我们一起来看一看具体实例。 实际应用中,我们可能需要两个设备通过串口传输浮点数据: 本篇笔记为了方便演示,使用串口助手模拟其中一个设备,本篇笔记内容如下: 我们创建一个用于管理float类型数据的共用体: unionfloat_data { floatf_data; uint8_tbyte ; }; 数据的流向如: 本次使用串口助手模拟发送设备,省略了第一步,主要看第②、③步。 创建两个共用体变量,用于发送与接收: unionfloat_datarx_float_data,tx_float_data; 收发相关代码: 左
[单片机]
<font color='red'>STM32</font>如何<font color='red'>收发</font>float类型<font color='red'>数据</font>?
STM32 | STM32如何收发float类型数据
实际应用中,我们可能需要两个设备通过串口传输浮点数据: 本篇笔记为了方便演示,使用串口助手模拟其中一个设备,本篇笔记内容如下: 我们创建一个用于管理float类型数据的共用体: union float_data { float f_data; uint8_t byte ; }; 数据的流向如: 本次使用串口助手模拟发送设备,省略了第一步,主要看第②、③步。 创建两个共用体变量,用于发送与接收: union float_data rx_float_data, tx_float_data; 收发相关代码: 左右滑动查看全部代码 if(HAL_UART_Receive(&huart3, rx_float_
[单片机]
<font color='red'>STM32</font> | <font color='red'>STM32</font>如何<font color='red'>收发</font>float类型<font color='red'>数据</font>?
STM32 HAL库学习(二) 串口收发数据
上一篇实现了LED的点亮和串口轮询发送数据,这章想着实现串口接收数据,不得不说,在开始使用STM32的外设才对HAL库的框架有更准确的理解。之前一直不懂HAL库的优越性在哪,这次对它的msp层有了一定认识。 简单来说,HAL库有一个特点就是对于许多外设的初始化以及功能操作,都提供有一个weak版本的函数,例如串口的HAL_UART_MspInit()函数和HAL_UART_MspDeInit()函数等,这些都可以供用户在需要时在stm32f0xx_hal_msp.c中进行重写实现功能。 用串口初始化来举例子,用Cube配置UART1使能并生成代码后可以看到有三个关键函数: 1、void MX_USART1_UART_I
[单片机]
STM32 SPI 收发数据 ---规则 + 问题解析
规则: 1) 高速同步串行口。3~4线接口(CS ,CLK ,MOSI,MISO),收发独立、可同步进行。 2)SPI分为主从模式,主模式提供时钟和片选选择信号. 3) 模式控制:CPOL用来控制时钟信号(clk)在空闲时候的状态;CPHA用来控制采样时刻时CLK的边缘动作。 CPOL CPHA 模式: 0 0 CLK空闲时为低电平,CLK上升沿采样数据。 0 1 CLK空闲为低电平,CLK下降沿采样数据。 1 0 CLK空闲时为高电平,CLK上升沿采样数据。 1 1 CLK空闲时为高电平,CLK下降沿采样数据。 1)SPI配置(3.01库): SPI_InitStructure.SPI_Direction = SPI_D
[单片机]
STM32 IIC 详解 之 stm32 IIC 从模式(中断方式收发数据)
1、IIC简介 第二节代码会用到该部分内容,对于IIC来说,从机是不能主动发送数据的,开始条件都是由主机生成。 1.1、主机发送数据流程 1) 主机在检测到总线为“空闲状态”(即 SDA、SCL 线均为高电平)时,发送一个启动信号“S”,开始一次通信的开始 2) 主机接着发送一个命令字节。该字节由 7 位的外围器件地址和 1 位读写控制位 R/W组成(此时 R/W=0) 3) 相对应的从机收到命令字节后向主机回馈应答信号 ACK(ACK=0) 4) 主机收到从机的应答信号后开始发送第一个字节的数据 5) 从机收到数据后返回一个应答信号 ACK 6) 主机收到应答信号后再发送下一个数据字节 7) 当主机发送
[单片机]
STM32学习之串口DMA收发数据-Lincoln
前言:从接触单片机到现在,从PIC到STM32,从来没有写过任何技术日记。忽然感觉很空虚,既然学了技术,就该留点什么东西来。这篇是我从事技术行业的第一篇技术文章。目的是想与大家交流学习,当中有不当的技术错误,还望看过的朋友指教。在此不胜感激。 这两天利用手头的开发板,做了个简易的利用DMA接收和DMA发送串口数据。目的很简单,利用串口助手在PC上向单片机发送一组数据,由单片机通过串口将这组数据反馈回PC显示。 做这个实验之前,先来了解下STM32的DMA,以及DMA的作用。在做这个实验之前,伍哥曾经问过我,DMA的工作方式有哪几种。我没回答上来。这里我和大家一起回顾下,这个简单的问题。DMA的工作方式有三种:内存- 外
[单片机]
<font color='red'>STM32</font>学习之串口DMA<font color='red'>收发</font><font color='red'>数据</font>-Lincoln
STM32外设AD/DA转换器基础教程与CubeMX配置详解
一,什么是AD/DA 我们已经掌握了如何让单片机通过 UART 与外界 交流 。但是,现实世界充满了连续变化的模拟信号,比如温度、光线强度、声音大小等等。单片机内部处理的是离散的数字信号,如何让它们互相理解呢? 这时,ADC (Analog-to-Digital Converter, 模数转换器) 和 DAC (Digital-to-Analog Converter, 数模转换器) 就派上用场了! ADC: 就像一位 翻译官 ,把现实世界的模拟语言(连续变化的电压)翻译成单片机能懂的数字语言(离散的数值)。 现实世界的模拟信号比如温度、光线强度、声音大小,其实都可以转化为连续的电压信号 DAC: 则反过来,把单片机的数字指令翻
[单片机]
<font color='red'>STM32</font>外设AD/DA转换器基础教程与<font color='red'>CubeMX</font>配置详解
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved