一、USB简介

USB(Universal Serial BUS)通用串行总线,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在 PC 领域的接口技术。USB 接口支持设备的即插即用和热插拔功能。USB 是在 1994 年底由英特尔、康柏、IBM、Microsoft 等多家公司联合提出的。

USB 发展到现在已经有 USB1.0/1.1/2.0/3.0 等多个版本。目前用的最多的就是 USB1.1 和 USB2.0,USB3.0 目前已经开始普及。STM32F103 自带的 USB 符合 USB2.0 规范,不过 STM32F103 的 USB 都只能用来做设备,而不能用作主机。

标准 USB 共四根线组成,除 VCC/GND 外,另外为 D+,D-; 这两根数据线采用的是差分电压的方式进行数据传输的。在 USB 主机上,D-和 D+都是接了 15K 的电阻到低的,所以在没有设备接入的时候,D+、D-均是低电平。而在 USB 设备中,如果是高速设备,则会在 D+上接一个 1.5K 的电阻到 VCC,而如果是低速设备,则会在 D-上接一个 1.5K 的电阻到 VCC。这样当设备接入主机的时候,主机就可以判断是否有设备接入,并能判断设备是高速设备还是低速设备。

STM32F103 的 MCU 自带 USB 从控制器,符合 USB 规范的通信连接;PC 主机和微控制器之间的数据传输是通过共享一专用的数据缓冲区来完成的,该数据缓冲区能被 USB 外设直接访问。这块专用数据缓冲区的大小由所使用的端点数目和每个端点最大的数据分组大小所决定,每个端点最大可使用 512 字节缓冲区(专用的 512 字节,和 CAN 共用),最多可用于 16 个单向或 8 个双向端点。USB 模块同 PC 主机通信,根据 USB 规范实现令牌分组的检测,数据发送/接收的处理,和握手分组的处理。整个传输的格式由硬件完成,其中包括 CRC 的生成和校验。

1.1 USB HID简介

USB HID类是USB设备的一个标准设备类,包括的设备非常多。HID类设备定义它属于人机交互操作的设备,用于控制计算机操作的一些方面,如USB鼠标、USB键盘、USB游戏操纵杆等。但HID设备类不一定要有人机接口,只要符合HID类别规范的设备都是HID设备。

USB HID设备的一个好处就是操作系统自带了HID类的驱动程序,而用户无需去开发驱动程序,只要使用API系统调用即可完成通信。

官方资料:http://www.usb.org/developers/hidpage 其中包含最主要的两个说明:

  • 《Device Class Definition for human interface device (HID)》【描述了 HID 的基本组成和格式】
  • 《Universal Serial Bus HID Usage Tables》【对上面文档的补充,将各种不同的 HID 设备的基本组成列举出来】

二、新建工程

1. 打开 STM32CubeMX 软件,点击“新建工程”

BIOS里面DP哪里设置_STM32CubeMX

2. 选择 MCU 和封装

BIOS里面DP哪里设置_stm32_02

3. 配置时钟

RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)

BIOS里面DP哪里设置_STM32CubeMX_03

选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz

修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置

BIOS里面DP哪里设置_USB_04

4. 配置调试模式

非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器

SYS 设置,选择 Debug 为 Serial Wire

BIOS里面DP哪里设置_STM32CubeMX_05

三、USB

3.1 参数配置

在 Connectivity 中选择 USB 设置,并勾选 Device(FS) 激活 USB 设备。

BIOS里面DP哪里设置_USB_06

在 Parameter Settings 进行具体参数配置。

BIOS里面DP哪里设置_stm32_07

  • Speed: Full Speed 12MBit/s(固定为全速)
  • Low Power: 默认 Disabled(在任何不需要使用usb模块的时候,通过写控制寄存器总可以使usb模块置于低功耗模式(low power mode ,suspend模式)。在这种模式下,不产生任何静态电流消耗,同时usb时钟也会减慢或停止。通过对usb线上数据传输的检测,可以在低功耗模式下唤醒usb模块。也可以将一特定的中断输入源直接连接到唤醒引脚上,以使系统能立即恢复正常的时钟系统,并支持直接启动或停止时钟系统。)

3.2 引脚配置

USB 的 DP 引脚必须上拉 1.5K 欧的电阻,电脑才能检测到 USB,否则检测不到。

BIOS里面DP哪里设置_stm32_08

查看野火指南者开发板原理图可知,需要将 PD6 配置为低电平使能 USB。

BIOS里面DP哪里设置_USB_09

在右边图中找到 PD6 引脚,选择 GPIO_Output。

BIOS里面DP哪里设置_BIOS里面DP哪里设置_10

在GPIO output level 中选择 Low 输出低电平。

BIOS里面DP哪里设置_STM32CubeMX_11

3.3 配置时钟

选择 Clock Configuration,USB 时钟配置为 48MHz,且来源最好是外部晶振分频得到。

BIOS里面DP哪里设置_STM32CubeMX_12

3.4 USB Device

USB有主机(Host)和设备(Device)之分。一般电脑的USB接口为主机接口,而键盘、鼠标、U盘等则为设备。

部分型号的STM32芯片有1~2个USB接口。像STM32F103系列的有一个USB Device接口,STM32F407系列的有2个USB接口,既可以作为HOST,又可以作为Device,还可以作为OTG接口。

在 Middleware 中选择 USB_DEVICE 设置,在 Class For FS IP 设备类别选择 Human Interface Device Class(HID) 人机接口设备。

BIOS里面DP哪里设置_USB_13

参数配置保持默认。

BIOS里面DP哪里设置_BIOS里面DP哪里设置_14

  • HID_FS_BINTERVAL(主机读取设备数据时间间隔): 0xA(STM32将数据发送到一个缓存区,而不是直接发送到上位机,而上位机每隔一端时间会来访问缓冲区读取数据。读取时间间隔过快会导致多次数据发送,过慢会导致数据丢失)
  • USBD_MAX_NUM_INTERFACES (Maximum number of supported interfaces)(最大支持HID设备的接口数): 1(应为现在只有鼠标,所以1就行,如果是需要同时键盘,鼠标,手柄啥的,根据数量选择即可)

设备描述符保持默认。

BIOS里面DP哪里设置_stm32_15

四、生成代码

输入项目名和项目路径

BIOS里面DP哪里设置_STM32CubeMX_16

选择应用的 IDE 开发环境 MDK-ARM V5

BIOS里面DP哪里设置_HID_17

每个外设生成独立的 ’.c/.h’ 文件

不勾:所有初始化代码都生成在 main.c

勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。

BIOS里面DP哪里设置_USB_18

点击 GENERATE CODE 生成代码

BIOS里面DP哪里设置_STM32CubeMX_19

五、了解usbd_hid.c

打开工程文件夹Middlewares/USB_Device_Library下usbd_hid.c文件

BIOS里面DP哪里设置_USB_20

5.1 USB HID描述符

HID设备的描述符除了**5个USB的标准描述符(设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符)**外,还包括三个HID设备类特定的描述符:HID描述符、报告描述符(Report)、实体描述符(Physical)。

他们之间的层次关系如图:

BIOS里面DP哪里设置_BIOS里面DP哪里设置_21

打开usbd_hid.c文件,找到USBD_HID_CfgFSDesc配置全速描述符数组定义处。

  • 配置描述符bNumInterfaces表示这个设备有多少个接口。MaxPower 100 mA表示这个设备需要从总线上获取100mA电流。
/* USB HID device FS Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_HID_CfgFSDesc[USB_HID_CONFIG_DESC_SIZ]  __ALIGN_END =
{
  0x09, /* bLength: Configuration Descriptor size */
  USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
  USB_HID_CONFIG_DESC_SIZ,
  /* wTotalLength: Bytes returned */
  0x00,
  0x01,         /*bNumInterfaces: 1 interface*/
  0x01,         /*bConfigurationValue: Configuration value*/
  0x00,         /*iConfiguration: Index of string descriptor describing
  the configuration*/
  0xE0,         /*bmAttributes: bus powered and Support Remote Wake-up */
  0x32,         /*MaxPower 100 mA: this current is used for detecting Vbus*/
  • 接口描述符bInterfaceClass的值必须是 0x03bInterfaceSubClass的值为 0 或 1, 为1表示HID设备是一个启动设备(BootDevice, 一般对PC机有意义,意思是BIOS启动时能识别您使用的HID设备,切只有标准鼠标或者键盘才能称为BootDevice),为0表示HID设备是操作系统启动厚才能识别使用的设备。bInterfaceProtocol的值为 2 表示鼠标接口:(0 — NONE,1 — Keyboard(键盘),2 — Mouse (鼠标),3~255 Reserved)
/************** Descriptor of Joystick Mouse interface ****************/
  /* 09 */
  0x09,         /*bLength: Interface Descriptor size*/
  USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
  0x00,         /*bInterfaceNumber: Number of Interface*/
  0x00,         /*bAlternateSetting: Alternate setting*/
  0x01,         /*bNumEndpoints*/
  0x03,         /*bInterfaceClass: HID*/
  0x01,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
  0x02,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
  0,            /*iInterface: Index of string descriptor*/
  • HID描述符 HID描述符关联于接口描述符,因而如果一个设备只有一个接口描述符,则无论它有几个端点描述符,HID设备只有一个HID描述符。HID设备描述符主要描述HID规范的版本号, HID通信所使用的额外描述符,报告描述符的长度等。
/******************** Descriptor of Joystick Mouse HID ********************/
  /* 18 */
  0x09,         /*bLength: HID Descriptor size*/
  HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
  0x11,         /*bcdHID: HID Class Spec release number*/
  0x01,
  0x00,         /*bCountryCode: Hardware target country*/
  0x01,         /*bNumDescriptors: Number of HID class descriptors to follow*/
  0x22,         /*bDescriptorType*/
  HID_MOUSE_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
  0x00,

下表为HID描述符的结构。

偏移量


大小(Byte)


描述

0

bLength

1

数字

此描述符的长度,以字节为单位

1

bDescriptorType

1

常量

描述符种类(此处 0x21为HID类)

2

bcdHID

2

数字

HID规范版本号(BCD码),采用4个16进制的BCD格式编码

4

bCountryCode

1

数字

硬件目的国家的识别码

5

bNumDescriptors

1

数字

支持的附属描述符数目

6

bDescriptorType

1

常量

0x21-HID描述符,0x22-报告描述符,0x23-实体描述符

7

wDescriptorLength

2

数字

报告描述符的总长度

9

bDescriptorType

1

常量

用于识别描述符类型的常量,使用有一个以上描述符的设备

10

wDescriptorLength

2

数字

描述符总长度,使用在有一个以上描述符的设备

  • 端点描述符bEndpointAddress表示端点地址,表示当前这个接口所需要的端点资源,输入(相对于主机而言)端点最高位为1,输出(相对于主机而言)端点最高位为0。HID设备一般都是使用中断端点进行数据传输。wMaxPacketSize表示该端点上数据传输的数量。bInterval表示主机查询设备数据的时间间隔,如果设置的太长,则键盘输入延迟很高。
/******************** Descriptor of Mouse endpoint ********************/
  /* 27 */
  0x07,          /*bLength: Endpoint Descriptor size*/
  USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/

  HID_EPIN_ADDR,     /*bEndpointAddress: Endpoint Address (IN)*/
  0x03,          /*bmAttributes: Interrupt endpoint*/
  HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
  0x00,
  HID_FS_BINTERVAL,          /*bInterval: Polling Interval */
  /* 34 */
};

5.2 报告描述符

下载 HID Descriptor Tool (DT) HID描述符工具: 官网下载:https://usb.org/sites/default/files/documents/dt2_4.zip 百度网盘:https://pan.baidu.com/s/1ayjdQtc7e9NWwYJqdp0pXA?pwd=4ghb 提取码:4ghb

打开 File——》Open...——》mouse.hid

BIOS里面DP哪里设置_BIOS里面DP哪里设置_22

我们可以看到HID鼠标的描述符情况:

BIOS里面DP哪里设置_HID_23

打开usbd_hid.c文件,找到HID_MOUSE_ReportDesc数组定义处(默认生产HID设备为Mouse,所以我们不需要修改)。

BIOS里面DP哪里设置_STM32CubeMX_24

六、修改main.c

添加头文件和USB设备句柄。

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usbd_hid.h"
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
extern USBD_HandleTypeDef hUsbDeviceFS;

添加一个数组变量,用于传输鼠标动作信息的。BYTE1 – |–bit7: 1 表示 Y 坐标的变化量超出-256 ~ 255的范围,0表示没有溢出 |–bit6: 1 表示 X 坐标的变化量超出-256 ~ 255的范围,0表示没有溢出 |–bit5: Y 坐标变化的符号位,1表示负数,即鼠标向下移动 |–bit4: X 坐标变化的符号位,1表示负数,即鼠标向左移动 |–bit3: 恒为1 |–bit2: 1表示中键按下 |–bit1: 1表示右键按下 |–bit0: 1表示左键按下BYTE2 – X坐标变化量,与byte的bit4组成9位符号数,负数表示向左移,正数表右移。用补码表示变化量BYTE3 – Y坐标变化量,与byte的bit5组成9位符号数,负数表示向下移,正数表上移。用补码表示变化量BYTE4 – 滚轮变化。

/* USER CODE BEGIN PV */
/*
 * buffer[0] - bit0: Left button 左键
 *             bit1: Right button 右键
 *             bit2: Middle button 中键
 * buffer[1] - Cursor movement X axis 水平移动
 * buffer[2] - Cursor movement Y axis 垂直移动
 * buffer[3] - Wheel vertical movement 滚轮转动
 */
uint8_t buffer[4] = {0x00, 0x00, 0x00, 0x00};
/* USER CODE END PV */

添加鼠标单击左/右/中键、鼠标滚动和鼠标移动函数。

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void Click_L()
{
    memset(buffer, 0, 4);
	buffer[0] = 1;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//按下鼠标左键
	HAL_Delay(10);
    buffer[0] = 0;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//松开鼠标按键
	HAL_Delay(10);
}
void Click_R()
{
    memset(buffer, 0, 4);
	buffer[0] = 2;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//按下鼠标右键
	HAL_Delay(10);
    buffer[0] = 0;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//松开鼠标按键
	HAL_Delay(10);
}
void Click_M()
{
    memset(buffer, 0, 4);
	buffer[0] = 4;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//按下鼠标右键
	HAL_Delay(10);
    buffer[0] = 0;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//松开鼠标按键
	HAL_Delay(10);
}
void Scroll(int8_t x)
{
    memset(buffer, 0, 4);
    buffer[3] = x;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//鼠标滚动
	HAL_Delay(10);
}
void Move(int8_t x,int8_t y)
{
    memset(buffer, 0, 4);
	buffer[1]=x;
	buffer[2]=y;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//鼠标移动
	HAL_Delay(10);
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
//    usb_printf("\r\n****** USB-HID Mouse Example ******\r\n\r\n");
    Move(100,0);//向右移动100个像素
    Click_R();//单击右键
    HAL_Delay(1000);//延时1秒
		
    Move(0,100);//向下移动100个像素
    Click_R();//单击右键
    HAL_Delay(1000);//延时1秒
		
    Move(-100,0);//向左移动100个像素
    Click_R();//单击右键
    HAL_Delay(1000);//延时1秒
		
    Move(0,-100);//向上移动100个像素
    Click_R();//单击右键
    HAL_Delay(1000);//延时1秒
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

USBD_HID_SendReport()函数为发送报文。

BIOS里面DP哪里设置_HID_25

七、查看效果

编译工程,下载到板子上,插上USB线连接到电脑上,识别出为鼠标设备。

BIOS里面DP哪里设置_BIOS里面DP哪里设置_26

注意: 如果设备带有感叹号,则参考下面九、注意事项

BIOS里面DP哪里设置_STM32CubeMX_27

每1秒进行一次鼠标操作。

BIOS里面DP哪里设置_STM32CubeMX_28

八、工程代码

链接:https://pan.baidu.com/s/1ils5-zhObSP5t-gfM4voYQ?pwd=p5ll 提取码:p5ll

九、注意事项

用户代码要加在 USER CODE BEGIN N 和 USER CODE END N 之间,否则下次使用 STM32CubeMX 重新生成代码后,会被删除。

BIOS里面DP哪里设置_BIOS里面DP哪里设置_29

如果USB端口出现感叹号设备无法启动的问题,可适当将堆改大,如0x400

BIOS里面DP哪里设置_HID_30

BIOS里面DP哪里设置_stm32_31


• 由 Leung 写于 2022 年 10 月 27 日

• 参考:STM32F042F6P6生成HID工程模拟USB鼠标     USB 协议分析之 HID 设备     STM32CUBEMX简单几步,DIY基于STM32的可编程USB鼠标,硬件自动化助手     STM32-USB学习系列(四):USB-HID模拟鼠标功能     STM32 基础系列教程 25 - USB_HID_mouse