STM32_USB

概述

本文是使用HAL库的USB驱动
因为官方cubeMX生成的hal库做组合设备时过于繁琐

所以这里使用某大神的插件,可以集成在cubeMX里自动生成组合设备

有小bug会覆盖生成文件里自己写的内容,所以生成一次后注意保存

插件安装

下载地址 https://github.com/alambe94/I-CUBE-USBD-Composite/releases/tag/V01.00.03

下载pack文件
打开cubeMX

点击这个 之后选择下载的文件 安装

出现这个即为安装成功

生成代码

打开USB 设为设备模式,打开中断

注意设置时钟树

USB需要较为精确的时钟 建议用外部晶振

选择库文件

目前打勾的这俩必选

其余根据需要选择

根据需要选择,注意要在上步开启的库文件中选

在初始化后加入这个函数

MX_USB_DEVICE_Init();

可以去usbd_desc.c里设置VID/PID 某些名称等参数,不同设备有些许不同

虚拟串口(CDC)

概述

串口名是由PC的驱动来决定的,没法在STM32端设置

设置

打开这个

可以在这里设置虚拟的串口数量

注意一个串口要占用2个IN端点和一个OUT端点

也可以设置AL94.I-CUBE-USBD-COMPOSITE_conf.h文件中的_USBD_CDC_ACM_COUNT设置虚拟的CDC串口数量

发送

开始发送

类型 名称 功能
uint8_t ch 通道
uint8_t * Buf 缓冲区地址
uint16_t Len 发送数量
uint8_t 输出 已经发送的数量
c 复制代码
uint8_t CDC_Transmit(uint8_t ch, uint8_t *Buf, uint16_t Len)

发送完成

类型 名称 功能
uint8_t cdc_ch 通道
uint8_t * Buf 缓冲区地址
uint32_t Len 发送数量
uint8_t epnum 端点号
uint8_t 错误码
c 复制代码
int8_t CDC_TransmitCplt(uint8_t cdc_ch, uint8_t *Buf, uint32_t *Len, uint8_t epnum)

接收

类型 名称 功能
uint8_t cdc_ch 通道
uint8_t * Buf 缓冲区地址
uint32_t Len 发送数量
int8_t 错误码

接收到数据会自动调用这函数

c 复制代码
int8_t CDC_Receive(uint8_t cdc_ch, uint8_t *Buf, uint32_t *Len)

在这个函数里调用这俩句,来接收下个数据包

c 复制代码
  USBD_CDC_SetRxBuffer(cdc_ch, &hUsbDevice, &Buf[0]);
  USBD_CDC_ReceivePacket(cdc_ch, &hUsbDevice);

控制函数

类型 名称 功能
uint8_t cdc_ch 通道
uint8_t cmd 命令类型
uint8_t * pbuf 命令缓冲区
uint16_t length 长度
int8_t 错误码
c 复制代码
int8_t CDC_Control(uint8_t cdc_ch, uint8_t cmd, uint8_t *pbuf, uint16_t length)

cmdCDC_SET_LINE_CODING时收到来自主机的命令
具体内容生成的函数中有注释

人体工学设备(HID)

概述

全部使用自定义HID设备

根据不同设备设置描述符即可

HID间的复合直接复制就行

eg:鼠标+键盘 直接把鼠标的描述符和键盘的描述符写到一起即可

建立工程

设置

usbd_customhid.h

名称 功能
CUSTOM_HID_STR_DESC HID描述
CUSTOM_HID_EPIN_SIZE 输入缓冲大小(一般设为64)
CUSTOM_HID_EPOUT_SIZE 输出缓冲大小(一般设为64)
USBD_CUSTOMHID_OUTREPORT_BUF_SIZE HID缓冲区(一般设为64)
CUSTOM_HID_FS_BINTERVAL 包间隔时间

usbd_custom_hid_if.c
CUSTOM_HID_ReportDesc 设置HID描述符 USBD_CUSTOM_HID_REPORT_DESC_SIZE 同时也要设置配置符大小

APIs

发送数据
类型 名称 功能
USBD_HandleTypeDef * pdev USB句柄
uint8_t * report 缓冲区
uint16_t len 数据长度
uint8_t 错误码
c 复制代码
  uint8_t USBD_CUSTOM_HID_SendReport(USBD_HandleTypeDef *pdev,uint8_t *report, uint16_t len);
接收数据回调

usbd_custom_hid_if.c

类型 名称 功能
uint8_t event_idx
uint8_t state
uint8_t 错误码
c 复制代码
int8_t CUSTOM_HID_OutEvent(uint8_t event_idx, uint8_t state)

在内部调用以获取数据

c 复制代码
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)HZ_DAP_USB_Handle.pClassData_HID_Custom;
// hhid->Report_buf;
USBD_CUSTOM_HID_ReceivePacket(&HZ_DAP_USB_Handle);

鼠标

鼠标的配置描述符

c 复制代码
0x05, 0x01, 
0x09, 0x02, 
0xa1, 0x01, 

0x85, 0x02, // 报告ID (2)

0x09, 0x01, 
0xa1, 0x00, 
0x05, 0x09, 
0x19, 0x01, 
0x29, 0x03,
0x15, 0x00, 
0x25, 0x01, 
0x95, 0x03, 
0x75, 0x01,
0x81, 0x02, 
0x95, 0x01, 
0x75, 0x05,
0x81, 0x03, 
0x95, 0x03,
0x75, 0x08,
0x05, 0x01,
0x09, 0x30,
0x09, 0x31,
0x09, 0x38,
0x15, 0x81,
0x25, 0x7f,
0x81, 0x06,
0xc0,
0xc0 

需要发送的命令 含义

位置 功能
Bit0 报告ID
Bit10 左键(0未按1按下)
Bit11 右键(0未按1按下)
Bit12 中键(0未按1按下)
Bit2 x轴(正右负左 -127~127)
Bit3 y轴(正下负上 -127~127)
Bit4 滚动(正上负下 -127~127)

这个函数是自己封装的

c 复制代码
extern USBD_HandleTypeDef hUsbDevice;

/**
 * @brief 控制鼠标
 * @param key_l 左键(仅bit0 0未按1按下)
 * @param key_r 右键(仅bit0 0未按1按下)
 * @param key_m 中键(仅bit0 0未按1按下)
 * @param x x轴(正右负左 -127~127)
 * @param y y轴(正下负上 -127~127)
 * @param ec 滚动(正上负下 -127~127)
 * @author HZ12138
 * @date 2025-03-28 20:17:39
 */
void HZ_Mouse_set(uint8_t key_l, uint8_t key_r, uint8_t key_m, int8_t x, int8_t y, int8_t ec)
{
  uint8_t buf[5];
  key_l &= 0x01;
  key_r &= 0x01;
  key_m &= 0x01;

  buf[0] = 0x02; // 报告ID 鼠标是0x02

  buf[1] = (key_m << 2) | (key_r << 1) | (key_l << 0);
  buf[2] = (uint8_t)x;
  buf[3] = (uint8_t)y;
  buf[4] = (uint8_t)ec;
  USBD_CUSTOM_HID_SendReport(&hUsbDevice, buf, 5);
}

键盘

描述符
0x85后面跟的是报告ID 0保留

复制代码
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)

0x85, 0x01, // 报告ID (1)

0x05, 0x07, //   USAGE_PAGE (Keyboard)
0x19, 0xe0, //   USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, //   USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, //   LOGICAL_MINIMUM (0)
0x25, 0x01, //   LOGICAL_MAXIMUM (1)
0x75, 0x01, //   REPORT_SIZE (1)
0x95, 0x08, //   REPORT_COUNT (8)
0x81, 0x02, //   INPUT (Data,Var,Abs)
0x95, 0x01, //   REPORT_COUNT (1)
0x75, 0x08, //   REPORT_SIZE (8)
0x81, 0x03, //   INPUT (Cnst,Var,Abs)
0x95, 0x05, //   REPORT_COUNT (5)
0x75, 0x01, //   REPORT_SIZE (1)
0x05, 0x08, //   USAGE_PAGE (LEDs)
0x19, 0x01, //   USAGE_MINIMUM (Num Lock)
0x29, 0x05, //   USAGE_MAXIMUM (Kana)
0x91, 0x02, //   OUTPUT (Data,Var,Abs)
0x95, 0x01, //   REPORT_COUNT (1)
0x75, 0x03, //   REPORT_SIZE (3)
0x91, 0x03, //   OUTPUT (Cnst,Var,Abs)
0x95, 0x06, //   REPORT_COUNT (6)
0x75, 0x08, //   REPORT_SIZE (8)
0x15, 0x00, //   LOGICAL_MINIMUM (0)
0x25, 0x65, //   LOGICAL_MAXIMUM (101)
0x05, 0x07, //   USAGE_PAGE (Keyboard)
0x19, 0x00, //   USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, //   USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, //   INPUT (Data,Ary,Abs)
0xc0        // END_COLLECTION

数据内容

Bit2~8 发送的是按下的键号 如果不按下写0即可

位置 功能
Bit0 报告ID
Bit10 左CTRL(0未按1按下)
Bit11 左SHIFT(0未按1按下)
Bit12 左ALT(0未按1按下)
Bit13 左GUI(0未按1按下)
Bit14 右CTRL(0未按1按下)
Bit15 右SHIFT(0未按1按下)
Bit16 右ALT(0未按1按下)
Bit17 右GUI(0未按1按下)
Bit2 保留(0x00)
Bit3 按键1
Bit4 按键2
Bit5 按键3
Bit6 按键4
Bit7 按键5
Bit8 按键6

封装的发送按键函数和单个按键按下函数

c 复制代码
extern USBD_HandleTypeDef hUsbDevice;
/**
 * @brief 发送按键
 * @param keys 按键
 * @param key_num 数量最大6个
 * @param ctrl_l 如名(0未按1按下)
 * @param shift_l 如名(0未按1按下)
 * @param alt_l 如名(0未按1按下)
 * @param gui_l 如名(0未按1按下)
 * @param ctrl_r 如名(0未按1按下)
 * @param shift_r 如名(0未按1按下)
 * @param alt_r 如名(0未按1按下)
 * @param gui_r 如名(0未按1按下)
 * @author HZ12138
 * @date 2025-03-28 22:52:09
 */
void HZ_KeyBoard_set(uint8_t *keys, uint8_t key_num,
                     uint8_t ctrl_l, uint8_t shift_l, uint8_t alt_l, uint8_t gui_l,
                     uint8_t ctrl_r, uint8_t shift_r, uint8_t alt_r, uint8_t gui_r)
{
  uint8_t buf[9];
  ctrl_l &= 0x01;
  shift_l &= 0x01;
  alt_l &= 0x01;
  gui_l &= 0x01;
  ctrl_r &= 0x01;
  shift_r &= 0x01;
  alt_r &= 0x01;
  gui_r &= 0x01;

  buf[0] = 0x01;

  buf[1] = (gui_r << 7) | (alt_r << 6) | (shift_r << 5) | (ctrl_r << 4) |
           (gui_l << 3) | (alt_l << 2) | (shift_l << 1) | (ctrl_l << 0);
  buf[2] = 0x00;

  for (int i = 0; i < key_num; i++)
    buf[3 + i] = keys[i];

  USBD_CUSTOM_HID_SendReport(&hUsbDevice, buf, 9);
}
/**
 * @brief 发送单个按键 
 * @param key 按键值
 * @author HZ12138
 * @date 2025-03-28 22:52:12
 */
void HZ_KeyBoard_one_key(uint8_t key)
{
  uint8_t temp[6];
  temp[0] = key;
  HZ_KeyBoard_set(temp, 6, 0, 0, 0, 0, 0, 0, 0, 0);
}

键码对应表,使用HID码

按键名称 HID码 虚拟键码
ESC 41 0X29 27 0x1B
F1 58 0X3a 112 0x70
F2 59 0X3b 113 0x71
F3 60 0X3c 114 0x72
F4 61 0X3d 115 0x73
F5 62 0X3e 116 0x74
F6 63 0X3f 117 0x75
F7 64 0X40 118 0x76
F8 65 0X41 119 0x77
F9 66 0X42 120 0x78
F10 67 0X43 121 0x79
F11 68 0X44 122 0x7A
F12 69 0X45 123 0x7B
Esc 41 0X29 27 0x1B
Back (回退) 42 0X2a 8 0x08
Tab 43 0X2b 9 0x09
CapLck (大小写) 57 0X39 20 0x14
Enter (回车) 40 0X28 13 0x0D
Space (空格) 44 0X2c 32 0x20
Scroll 71 0X47 145 0x91
Pause(暂停) 72 0X48 19 0x13
Insert (插入) 73 0X49 45 0x2D
PrintScr (截屏) 70 0X46 44 0x2C
Delete (删除) 76 0X4c 46 0x2E
Home (首页) 74 0X4a 36 0x24
End (结尾) 77 0X4d 35 0x23
PageUp (上一页) 75 0X4b 33 0x21
PageDn (下一页) 78 0X4e 34 0x22
Left (左) 80 0X50 37 0x25
Up (上) 82 0X52 38 0x26
Right (右) 79 0X4f 39 0x27
Down (下) 81 0X51 40 0x28
Num0 (小键盘) 98 0X62 96 0x60
Num1 (小键盘) 89 0X59 97 0x61
Num2 (小键盘) 90 0X5a 98 0x62
Num3 (小键盘) 91 0X5b 99 0x63
Num4 (小键盘) 92 0X5c 100 0x64
Num5 (小键盘) 93 0X5d 101 0x65
Num6 (小键盘) 94 0X5e 102 0x66
Num7 (小键盘) 95 0X5f 103 0x67
Num8 (小键盘) 96 0X60 104 0x68
Num9 (小键盘) 97 0X61 105 0x69
NumAdd (加号) 87 0X57 107 0x6B
NumSub (减号) 86 0X56 109 0x6D
NumMult (乘号) 85 0X55 106 0x6A
NumDiv (除号) 84 0X54 111 0x6F
NumDecim (点) 99 0X63 110 0x6E
NumLock (数字锁定键) 83 0X53 144 0x90
Ctrl 1 0X01 17 0x11
LCtrl (左CTR) 1 0X01 162 0xA2
RCtrl 16 0X10 163 0xA3
Shift 2 0X02 16 0x10
LShift 2 0X02 160 0xA0
RShift 32 0X20 161 0xA1
Alt 4 0X04 18 0x12
LAlt 4 0X04 164 0xA4
RAlt 64 0X40 165 0xA5
WIN 8 0X08 91 0x5B
LWIN 8 0X08 91 0x5B
RWIN 128 0X80 92 0x5C
A 4 0X04 65 0x41
B 5 0X05 66 0x42
C 6 0X06 67 0x43
D 7 0X07 68 0x44
E 8 0X08 69 0x45
F 9 0X09 70 0x46
G 10 0X0a 71 0x47
H 11 0X0b 72 0x48
I 12 0X0c 73 0x49
J 13 0X0d 74 0x4A
K 14 0X0e 75 0x4B
L 15 0X0f 76 0x4C
M 16 0X10 77 0x4D
N 17 0X11 78 0x4E
O 18 0X12 79 0x4F
P 19 0X13 80 0x50
Q 20 0X14 81 0x51
R 21 0X15 82 0x52
S 22 0X16 83 0x53
T 23 0X17 84 0x54
U 24 0X18 85 0x55
V 25 0X19 86 0x56
W 26 0X1a 87 0x57
X 27 0X1b 88 0x58
Y 28 0X1c 89 0x59
Z 29 0X1d 90 0x5A
0 39 0X27 48 0x30
1 30 0X1e 49 0x31
2 31 0X1f 50 0x32
3 32 0X20 51 0x33
4 33 0X21 52 0x34
5 34 0X22 53 0x35
6 35 0X23 54 0x36
7 36 0X24 55 0x37
8 37 0X25 56 0x38
9 38 0X26 57 0x39

大容量存储(MSC)

建立工程

设置

usbd_msc.h 中的 MSC_MEDIA_PACKET 要设为扇区(sector)大小

w25Qxx为4096 SD卡为512
STORAGE_LUN_NBR为虚拟磁盘卷数量 一般把一个设备设为一个卷 设置这个可以虚拟出多个磁盘

usbd_storage_if.cSTORAGE_BLK_NBR 为最小操作单元数量(一般写扇区数量)

usbd_storage_if.cSTORAGE_BLK_SIZ 为最小操作单元大小 (一般写扇区大小) 单位(Byte)

这俩相乘即可得到总大小 单位(Byte)

注意这俩的blk所指的块与FLASH的不同,也是我写成最小操作单元的原因
可以修改这个最后三项来更改显示名称

usbd_storage_if.cSTORAGE_Inquirydata

APIs

存储读取(必写)

描述 名称 功能
uint8_t lun 卷标
uint8_t * buf 缓冲区
uint32_t blk_addr 最小操作单元起始地址(*STORAGE_BLK_SIZ后得到Byte起始地址 )
uint16_t blk_len 最小操作单元的数量(*STORAGE_BLK_SIZ后得到Byte数量 )
int8_t 输出 错误码

usbd_storage_if.c

需要根据自己内容填写

c 复制代码
int8_t STORAGE_Read(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)

存储写入(必写)

描述 名称 功能
uint8_t lun 卷标
uint8_t * buf 缓冲区
uint32_t blk_addr 最小操作单元起始地址(*STORAGE_BLK_SIZ后得到Byte起始地址 )
uint16_t blk_len 最小操作单元的数量(*STORAGE_BLK_SIZ后得到Byte数量 )
int8_t 输出 错误码

usbd_storage_if.c

需要根据自己内容填写

c 复制代码
int8_t STORAGE_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
相关推荐
sramdram7 分钟前
基于MCU微控制器的电子血压计应用解决方案
单片机·嵌入式硬件·mcu·mcu微控制器
Szime18 分钟前
AD9218 国产替代方向:双通道 10 位 105MSPS ADC 选型支持
单片机·嵌入式硬件·fpga开发·汽车
凡人叶枫29 分钟前
Effective C++ 条款15:在资源管理类中提供对原始资源的访问
linux·开发语言·c++·stm32·单片机
数智工坊1 小时前
机器人控制总线深度解析:CAN与EtherCAT,谁在决定机器人的稳定性?
嵌入式硬件·学习·机器人
张海森-1688202 小时前
库里搜索函数 api接口__grep命令
单片机
mmmayang2 小时前
从简单的 CC 显示器入门嵌入式
嵌入式硬件·计算机外设
雯宝2 小时前
2.串口 IAP
stm32
xxwxx__2 小时前
51单片机定时器/计数器中断详解(T0和T1)——从入门到精通
c语言·单片机·嵌入式硬件·51单片机
飞猿_SIR3 小时前
RK3288 Android11平台移植RTL8733BU-WiFi模组
android·嵌入式硬件
国产化创客3 小时前
嵌入式视觉完整技术体系--ESP32/K230/RDK-X5/树莓派四层架构全解析
嵌入式硬件·物联网·架构·开源·智能硬件