概述
本文是使用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 | 輸出 | 已經發送的數量 |
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 | 錯誤碼 |
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 | 錯誤碼 |
接收到數據會自動調用這函數
int8_t CDC_Receive(uint8_t cdc_ch, uint8_t *Buf, uint32_t *Len)
在這個函數里調用這倆句,來接收下個數據包
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 | 錯誤碼 |
int8_t CDC_Control(uint8_t cdc_ch, uint8_t cmd, uint8_t *pbuf, uint16_t length)
在
cmd
為CDC_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 | 錯誤碼 |
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 | 錯誤碼 |
int8_t CUSTOM_HID_OutEvent(uint8_t event_idx, uint8_t state)
在內部調用以獲取數據
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);
鼠標
鼠標的配置描述符
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 |
Bit1[0] | 左鍵(0未按1按下) |
Bit1[1] | 右鍵(0未按1按下) |
Bit1[2] | 中鍵(0未按1按下) |
Bit2 | x軸(正右負左 -127~127) |
Bit3 | y軸(正下負上 -127~127) |
Bit4 | 滾動(正上負下 -127~127) |
這個函數是自己封裝的
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 鼠標是0x02buf[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 |
Bit1[0] | 左CTRL(0未按1按下) |
Bit1[1] | 左SHIFT(0未按1按下) |
Bit1[2] | 左ALT(0未按1按下) |
Bit1[3] | 左GUI(0未按1按下) |
Bit1[4] | 右CTRL(0未按1按下) |
Bit1[5] | 右SHIFT(0未按1按下) |
Bit1[6] | 右ALT(0未按1按下) |
Bit1[7] | 右GUI(0未按1按下) |
Bit2 | 保留(0x00) |
Bit3 | 按鍵1 |
Bit4 | 按鍵2 |
Bit5 | 按鍵3 |
Bit6 | 按鍵4 |
Bit7 | 按鍵5 |
Bit8 | 按鍵6 |
封裝的發送按鍵函數和單個按鍵按下函數
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.c
的STORAGE_BLK_NBR
為最小操作單元數量(一般寫扇區數量)
usbd_storage_if.c
的STORAGE_BLK_SIZ
為最小操作單元大小 (一般寫扇區大小) 單位(Byte)這倆相乘即可得到總大小 單位(Byte)
注意這倆的blk所指的塊與FLASH的不同,也是我寫成最小操作單元的原因
可以修改這個最后三項來更改顯示名稱
usbd_storage_if.c
的STORAGE_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
中需要根據自己內容填寫
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
中需要根據自己內容填寫
int8_t STORAGE_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)