【RP2350】香瓜树莓派RP2350之USB HID
本文最后修改时间:2025年05月10日 01:57
一、本节简介
本节以树莓派pico2开发板为例,举例如何写一个USB HID驱动加进工程里。本例程实现可修改树莓派识别到的名称。
二、实验平台
1、硬件平台
1)树莓派pico2开发板
①树莓派pico2开发板(作为仿真器)
②micro usb数据线
2)电脑
3)香瓜RP2350目标板
4)树莓派5
2、软件平台
1)VS CODE
三、版权声明
1)作者:甜甜的大香瓜
2)声明:喝水不忘挖井人,转载请注明出处。
3)纠错/业务合作:897503845@qq.com
4)香瓜嵌入式之树莓派群:512598061
5)本文出处:原创连载资料《简单粗暴学树莓派》
6)完整开源资料下载地址(电脑端打开):
opengua.taobao.com
四、实验前提
1、在进行本文步骤前,请先阅读以下章节:
1)《简单粗暴学树莓派》的“第一章至第二章”章节。
2、在进行本文步骤前,请先实现以下章节:
1)《简单粗暴学树莓派》的《香瓜树莓派RP2350之搭建开发环境(windows)》
2)《简单粗暴学树莓派》的《香瓜树莓派RP2350之新建工程》
五、基础知识
1、HID是什么?
答:HID(Human Interface Device人机接口设备)是USB协议的一种,键盘、鼠标都是USB HID设备。电脑里预装了HID的驱动,所以HID设备可实现在电脑上即插即用。
六、硬件原理
1、硬件连接
实际只接了4根线,3.3V、GND、CLK、DIO
注意: 给pico2供电时接VSYS,是因为pico2会再经过稳压到3.3v供电给pico2目标板的rp2350。而香瓜使用的板子仿真接口是直接供电给rp2350的,所以需要直连3.3v。
2、原理图
RP2350的USB信号连接到板载的USB接口上
七、实验步骤
1、在VS CODE工程文件夹下,添加驱动GUA_USBtoUart.c(VS CODE会自动加载)
//********************************************************************** //name: GUA_USBtoUart.c //introduce: USB转串口驱动 //author: opengua //email: 897503845@qq.com //QQ group: 香瓜嵌入式之树莓派群(512598061) //shop: opengua.taobao.com //changetime: 2025.05.22 //********************************************************************** #include "pico/stdlib.h" #include "GUA_USBtoUart.h" #include "main.h" /*********************全局变量************************/ uint8_t gaGUA_USBtoUART_Rx_Buff[GUA_USB_TO_UART_RX_SIZE] = {0}; volatile uint8_t gGUA_USBtoUART_Rx_Length = 0; //必须加volatile,否则会被优化 /*********************内部函数************************/ static void GUA_USBtoRx_CDC_Rx_Callback(void); //********************************************************************** //name: GUA_USBtoUart_Send //introduce: 初始化 //parameter: pGUA_Data:发送数据缓冲区 // nGUA_Len:数据长度 //return: none //author: opengua //email: 897503845@qq.com //QQ group: 香瓜嵌入式之树莓派群(512598061) //shop: opengua.taobao.com //changetime: 2025.05.22 //********************************************************************** void GUA_USBtoUart_Send(uint8_t *pGUA_Data, uint8_t nGUA_Len) { //如果当前USB连接中 if(tud_cdc_connected()) { //通过usb的cdc发出数据 tud_cdc_write(pGUA_Data, nGUA_Len); //清空cdc传输通道数据 tud_cdc_write_flush(); } } //********************************************************************** //name: GUA_USBtoRx_CDC_Rx_Callback //introduce: 接收数据 //parameter: none //return: none //author: opengua //email: 897503845@qq.com //QQ group: 香瓜嵌入式之树莓派群(512598061) //shop: opengua.taobao.com //changetime: 2025.05.22 //********************************************************************** static void GUA_USBtoRx_CDC_Rx_Callback(void) { gGUA_USBtoUART_Rx_Length = tud_cdc_read(gaGUA_USBtoUART_Rx_Buff, sizeof(gaGUA_USBtoUART_Rx_Buff)); if(gGUA_USBtoUART_Rx_Length > 0) { //清空cdc传输通道的数据 tud_cdc_write_flush(); //到对应的数据处理 gnGUA_Function = FUNC_GUA_COMMUNICAION_PROCESS_EVT; } } //********************************************************************** //name: tud_hid_get_report_cb //introduce: 电脑读报告时的回调 //parameter: none //return: none //author: opengua //email: 897503845@qq.com //QQ group: 香瓜嵌入式之树莓派群(512598061) //shop: opengua.taobao.com //changetime: 2025.02.18 //********************************************************************** uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { (void) instance; return 0; } //********************************************************************** //name: tud_hid_set_report_cb //introduce: 电脑写报告时的回调 //parameter: none //return: none //author: opengua //email: 897503845@qq.com //QQ group: 香瓜嵌入式之树莓派群(512598061) //shop: opengua.taobao.com //changetime: 2025.02.18 //********************************************************************** void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { (void) instance; } //********************************************************************** //name: GUA_USBtoUart_Poll //introduce: 轮询 CDC 数据 //parameter: none //return: none //author: opengua //email: 897503845@qq.com //QQ group: 香瓜嵌入式之树莓派群(512598061) //shop: opengua.taobao.com //changetime: 2025.05.22 //********************************************************************** void GUA_USBtoUart_Poll(void) { //处理USB任务 tud_task(); //检测缓冲区是否有可读数据 if(tud_cdc_available()) { //CDC 接收回调函数 GUA_USBtoRx_CDC_Rx_Callback(); } //定期清空发送缓冲区 tud_cdc_write_flush(); } //********************************************************************** //name: GUA_USBtoUart_Init //introduce: 初始化 //parameter: none //return: none //author: opengua //email: 897503845@qq.com //QQ group: 香瓜嵌入式之树莓派群(512598061) //shop: opengua.taobao.com //changetime: 2025.02.18 //********************************************************************** //需要在CMakeLists.txt文件中打开usb(同时关闭串口为0):pico_enable_stdio_usb(ArgonOne_RP2350 1) void GUA_USBtoUart_Init(void) { //初始化usb转串口 这条不能被调用两次,避免低功耗唤醒后被再次初始化,所以本函数得放到GUA_Init函数外 stdio_init_all(); //初始化板上外设如端口和USB board_init(); //初始化USB设备栈,端口0 tud_init(BOARD_TUD_RHPORT); //不确定是否需要,例程有,不过好像有它没它都一样 if(board_init_after_tusb) { board_init_after_tusb(); } } |
注意USB的回调函数里有香瓜的项目变量,添加后需要增删一些报错内容。
2、在VS CODE工程文件夹下,添加驱动GUA_USBtoUart.h(VS CODE会自动加载)
//********************************************************************** //name: GUA_USBtoUart.h //introduce: USB转串口驱动头文件 //author: opengua //email: 897503845@qq.com //QQ group: 香瓜嵌入式之树莓派群(512598061) //shop: opengua.taobao.com //changetime: 2025.02.18 //********************************************************************** #ifndef _GUA_USB_TO_UART_H_ #define _GUA_USB_TO_UART_H_ /*********************头文件************************/ #include <stdio.h> #include "usb_descriptors.h" #include "tusb.h" #include "pico/stdio.h" #include "bsp/board_api.h" /*********************宏定义************************/ //串口缓冲区大小 #define GUA_USB_TO_UART_RX_SIZE 128 /*********************全局变量************************/ extern uint8_t gaGUA_USBtoUART_Rx_Buff[GUA_USB_TO_UART_RX_SIZE]; volatile extern uint8_t gGUA_USBtoUART_Rx_Length; /*********************外部函数************************/ extern void GUA_USBtoUart_Send(uint8_t *pGUA_Data, uint8_t nGUA_Len); extern void GUA_USBtoUart_Poll(void); extern void GUA_USBtoUart_Init(void); #endif |
3、在VS CODE工程文件夹下,添加驱动usb_descriptors.c(VS CODE会自动加载)
#include "bsp/board_api.h" #include "tusb.h" #include "usb_descriptors.h" #define HID_PD_IPRODUCT 0x01 // FEATURE ONLY #define HID_PD_SERIAL 0x02 // FEATURE ONLY #define HID_PD_MANUFACTURER 0x03 // FEATURE ONLY #define IDEVICECHEMISTRY 0x04 #define IOEMVENDOR 0x05 #define HID_PD_RECHARGEABLE 0x06 // FEATURE ONLY //#define HID_PD_PRESENTSTATUS 0x07 // INPUT OR FEATURE(required by Windows) #define HID_PD_REMAINTIMELIMIT 0x08 #define HID_PD_MANUFACTUREDATE 0x09 #define HID_PD_CONFIGVOLTAGE 0x0A // 10 FEATURE ONLY #define HID_PD_VOLTAGE 0x0B // 11 INPUT (NA) OR FEATURE(implemented) //#define HID_PD_REMAININGCAPACITY 0x0C // 12 INPUT OR FEATURE(required by Windows) #define HID_PD_RUNTIMETOEMPTY 0x0D #define HID_PD_FULLCHRGECAPACITY 0x0E // 14 FEATURE ONLY. Last Full Charge Capacity #define HID_PD_WARNCAPACITYLIMIT 0x0F #define HID_PD_CPCTYGRANULARITY1 0x10 #define HID_PD_REMNCAPACITYLIMIT 0x11 #define HID_PD_DELAYBE4SHUTDOWN 0x12 // 18 FEATURE ONLY #define HID_PD_DELAYBE4REBOOT 0x13 #define HID_PD_AUDIBLEALARMCTRL 0x14 // 20 INPUT OR FEATURE #define HID_PD_CURRENT 0x15 // 21 FEATURE ONLY #define HID_PD_CAPACITYMODE 0x16 #define HID_PD_DESIGNCAPACITY 0x17 #define HID_PD_CPCTYGRANULARITY2 0x18 #define HID_PD_AVERAGETIME2FULL 0x1A #define HID_PD_AVERAGECURRENT 0x1B #define HID_PD_AVERAGETIME2EMPTY 0x1C #define HID_PD_IDEVICECHEMISTRY 0x1F // Feature #define HID_PD_IOEMINFORMATION 0x20 // Feature #define IPRODUCT 0x02 #define ISERIAL 0x03 #define IMANUFACTURER 0x01 /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. * * Auto ProductID layout's Bitmap: * [MSB] HID | MSC | CDC [LSB] */ #define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) #define USB_PID (0x4001 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) #define USB_VID 0xCafe #define USB_BCD 0x0200 //--------------------------------------------------------------------+ // Device Descriptors //--------------------------------------------------------------------+ tusb_desc_device_t const desc_device = { #if 0 .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = USB_BCD, .bDeviceClass = 0x00, .bDeviceSubClass = 0x00, .bDeviceProtocol = 0x00, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, .idVendor = USB_VID, .idProduct = USB_PID, .bcdDevice = 0x0100, .iManufacturer = 0x01, .iProduct = 0x02, .iSerialNumber = 0x03, .bNumConfigurations = 0x01 #else // .bLength = sizeof(tusb_desc_device_t), // .bDescriptorType = TUSB_DESC_DEVICE, // .bcdUSB = 0x0200, // USB 2.0 // .bDeviceClass = 0x00, // .bDeviceSubClass = 0x00, // .bDeviceProtocol = 0x00, // .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, // 通常为 64 // .idVendor = 0x1D6B, // Linux Foundation // .idProduct = 0x0104, // HID UPS // .bcdDevice = 0x0100, // .iManufacturer = 0x01, // .iProduct = 0x02, // .iSerialNumber = 0x03, // .bNumConfigurations = 0x01 .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = 0x0200, .bDeviceClass = TUSB_CLASS_MISC, // 复合设备类 .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, // 使用IAD .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, .idVendor = 0x1D6B, // Linux Foundation VID .idProduct = 0x0104, // HID UPS PID .bcdDevice = 0x0100, .iManufacturer = 0x01, .iProduct = 0x02, .iSerialNumber = 0x03, .bNumConfigurations = 0x01 #endif }; //当接收到GET DEVICE DESCRIPTOR请求时,它会返回设备描述符的指针。 uint8_t const * tud_descriptor_device_cb(void) { return (uint8_t const *) &desc_device; } //--------------------------------------------------------------------+ // HID Report Descriptor //--------------------------------------------------------------------+ //每种类型的设备都有自己特定的HID报告布局。 #if 0 uint8_t const desc_hid_report[] = { TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD )), TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(REPORT_ID_MOUSE )), TUD_HID_REPORT_DESC_CONSUMER( HID_REPORT_ID(REPORT_ID_CONSUMER_CONTROL )), TUD_HID_REPORT_DESC_GAMEPAD ( HID_REPORT_ID(REPORT_ID_GAMEPAD )) }; #else uint8_t const desc_hid_report[] = { 0x05, 0x84, // USAGE_PAGE (Power Device) 0x09, 0x04, // USAGE (UPS) 0xA1, 0x01, // COLLECTION (Application) 0x09, 0x24, // USAGE (Sink) 0xA1, 0x02, // COLLECTION (Logical) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x01, // REPORT_COUNT (1) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM (255) 0x85, HID_PD_IPRODUCT, // REPORT_ID (1) 0x09, 0xFE, // USAGE (iProduct) 0x79, IPRODUCT, // STRING INDEX (2) 0xB1, 0x23, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Nonvolatile, Bitfield) 0x85, HID_PD_SERIAL, // REPORT_ID (2) 0x09, 0xFF, // USAGE (iSerialNumber) 0x79, ISERIAL, // STRING INDEX (3) 0xB1, 0x23, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Nonvolatile, Bitfield) 0x85, HID_PD_MANUFACTURER, // REPORT_ID (3) 0x09, 0xFD, // USAGE (iManufacturer) 0x79, IMANUFACTURER, // STRING INDEX (1) 0xB1, 0x23, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Nonvolatile, Bitfield) 0x05, 0x85, // USAGE_PAGE (Battery System) ==================== 0x85, HID_PD_RECHARGEABLE, // REPORT_ID (6) 0x09, 0x8B, // USAGE (Rechargable) 0xB1, 0x23, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Nonvolatile, Bitfield) 0x85, HID_PD_IDEVICECHEMISTRY, // REPORT_ID (31) 0x09, 0x89, // USAGE (iDeviceChemistry) 0x79, IDEVICECHEMISTRY, // STRING INDEX (4) 0xB1, 0x23, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Nonvolatile, Bitfield) 0x85, HID_PD_IOEMINFORMATION, // REPORT_ID (32) 0x09, 0x8F, // USAGE (iOEMInformation) 0x79, IOEMVENDOR, // STRING INDEX (5) 0xB1, 0x23, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Nonvolatile, Bitfield) #if 0 0x85, HID_PD_CAPACITYMODE, // REPORT_ID (22) 0x09, 0x2C, // USAGE (CapacityMode) 0xB1, 0x23, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Nonvolatile, Bitfield) #else 0x85, HID_PD_CAPACITYMODE, // REPORT_ID (22) 0x09, 0x2C, // USAGE (CapacityMode) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) <- 0表示mAh,1表示百分比 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x01, // REPORT_COUNT (1) 0xB1, 0x22, // FEATURE (Data, Variable, Absolute) #endif 0x85, HID_PD_CPCTYGRANULARITY1, // REPORT_ID (16) 0x09, 0x8D, // USAGE (CapacityGranularity1) 0x26, 0x64,0x00, // LOGICAL_MAXIMUM (100) 0xB1, 0x22, // FEATURE (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Nonvolatile, Bitfield) 0x85, HID_PD_CPCTYGRANULARITY2, // REPORT_ID (24) 0x09, 0x8E, // USAGE (CapacityGranularity2) 0xB1, 0x23, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Nonvolatile, Bitfield) 0x85, HID_PD_FULLCHRGECAPACITY, // REPORT_ID (14) 0x09, 0x67, // USAGE (FullChargeCapacity) 0xB1, 0x83, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x85, HID_PD_DESIGNCAPACITY, // REPORT_ID (23) 0x09, 0x83, // USAGE (DesignCapacity) 0xB1, 0x83, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) #if 0 0x85, HID_PD_REMAININGCAPACITY, // REPORT_ID (12) 0x09, 0x66, // USAGE (RemainingCapacity) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x66, // USAGE (RemainingCapacity) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) #else 0x85, HID_PD_REMAININGCAPACITY, // REPORT_ID (12) 0x09, 0x66, // USAGE (RemainingCapacity) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x64, // LOGICAL_MAXIMUM (100) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x01, // REPORT_COUNT (1) 0x81, 0x02, // INPUT (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x66, // USAGE (RemainingCapacity) 0xB1, 0x02, // FEATURE (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield)
#endif 0x85, HID_PD_WARNCAPACITYLIMIT, // REPORT_ID (15) 0x09, 0x8C, // USAGE (WarningCapacityLimit) 0xB1, 0xA2, // FEATURE (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x85, HID_PD_REMNCAPACITYLIMIT, // REPORT_ID (17) 0x09, 0x29, // USAGE (RemainingCapacityLimit) 0xB1, 0xA2, // FEATURE (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x85, HID_PD_MANUFACTUREDATE, // REPORT_ID (9) 0x09, 0x85, // USAGE (ManufacturerDate) 0x75, 0x10, // REPORT_SIZE (16) 0x27, 0xFF, 0xFF, 0x00, 0x00, // LOGICAL_MAXIMUM (65534) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x85, HID_PD_AVERAGETIME2FULL, // REPORT_ID (26) 0x09, 0x6A, // USAGE (AverageTimeToFull) 0x27, 0xFF, 0xFF, 0x00, 0x00, // LOGICAL_MAXIMUM (65534) 0x66, 0x01, 0x10, // UNIT (Seconds) 0x55, 0x00, // UNIT_EXPONENT (0) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x85, HID_PD_AVERAGETIME2EMPTY, // REPORT_ID (28) 0x09, 0x69, // USAGE (AverageTimeToEmpty) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x69, // USAGE (AverageTimeToEmpty) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x85, HID_PD_RUNTIMETOEMPTY, // REPORT_ID (13) 0x09, 0x68, // USAGE (RunTimeToEmpty) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x68, // USAGE (RunTimeToEmpty) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x85, HID_PD_REMAINTIMELIMIT, // REPORT_ID (8) 0x09, 0x2A, // USAGE (RemainingTimeLimit) 0x75, 0x10, // REPORT_SIZE (16) 0x27, 0x64, 0x05, 0x00, 0x00, // LOGICAL_MAXIMUM (1380) 0x16, 0x78, 0x00, // LOGICAL_MINIMUM (120) 0x81, 0x22, // INPUT (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x2A, // USAGE (RemainingTimeLimit) 0xB1, 0xA2, // FEATURE (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x05, 0x84, // USAGE_PAGE (Power Device) ==================== 0x85, HID_PD_DELAYBE4SHUTDOWN, // REPORT_ID (18) 0x09, 0x57, // USAGE (DelayBeforeShutdown) 0x16, 0x00, 0x80, // LOGICAL_MINIMUM (-32768) 0x27, 0xFF, 0x7F, 0x00, 0x00, // LOGICAL_MAXIMUM (32767) 0xB1, 0xA2, // FEATURE (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x85, HID_PD_DELAYBE4REBOOT, // REPORT_ID (19) 0x09, 0x55, // USAGE (DelayBeforeReboot) 0xB1, 0xA2, // FEATURE (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x85, HID_PD_CONFIGVOLTAGE, // REPORT_ID (10) 0x09, 0x40, // USAGE (ConfigVoltage) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x27, 0xFF, 0xFF, 0x00, 0x00, // LOGICAL_MAXIMUM (65535) 0x67, 0x21, 0xD1, 0xF0, 0x00, // UNIT (Centivolts) 0x55, 0x05, // UNIT_EXPONENT (5) 0xB1, 0x23, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Nonvolatile, Bitfield) 0x85, HID_PD_VOLTAGE, // REPORT_ID (11) 0x09, 0x30, // USAGE (Voltage) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x30, // USAGE (Voltage) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x85, HID_PD_AUDIBLEALARMCTRL, // REPORT_ID (20) 0x09, 0x5A, // USAGE (AudibleAlarmControl) 0x75, 0x08, // REPORT_SIZE (8) 0x15, 0x01, // LOGICAL_MINIMUM (1) 0x25, 0x03, // LOGICAL_MAXIMUM (3) 0x65, 0x00, // UNIT (0) 0x55, 0x00, // UNIT_EXPONENT (0) 0x81, 0x22, // INPUT (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x5A, // USAGE (AudibleAlarmControl) 0xB1, 0xA2, // FEATURE (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0x02, // USAGE (PresentStatus) 0xA1, 0x02, // COLLECTION (Logical) 0x85, HID_PD_PRESENTSTATUS, // REPORT_ID (7) 0x05, 0x85, // USAGE_PAGE (Battery System) ================= 0x09, 0x44, // USAGE (Charging) 0x75, 0x01, // REPORT_SIZE (1) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x44, // USAGE (Charging) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0x45, // USAGE (Discharging) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x45, // USAGE (Discharging) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0xD0, // USAGE (ACPresent) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0xD0, // USAGE (ACPresent) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0xD1, // USAGE (BatteryPresent) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0xD1, // USAGE (BatteryPresent) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0x42, // USAGE (BelowRemainingCapacityLimit) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x42, // USAGE (BelowRemainingCapacityLimit) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0x43, // USAGE (RemainingTimeLimitExpired) 0x81, 0xA2, // INPUT (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x43, // USAGE (RemainingTimeLimitExpired) 0xB1, 0xA2, // FEATURE (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0x4B, // USAGE (NeedReplacement) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x4B, // USAGE (NeedReplacement) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0xDB, // USAGE (VoltageNotRegulated) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0xDB, // USAGE (VoltageNotRegulated) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0x46, // USAGE (FullyCharged) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x46, // USAGE (FullyCharged) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0x47, // USAGE (FullyDischarged) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x47, // USAGE (FullyDischarged) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x05, 0x84, // USAGE_PAGE (Power Device) ================= 0x09, 0x68, // USAGE (ShutdownRequested) 0x81, 0xA2, // INPUT (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x68, // USAGE (ShutdownRequested) 0xB1, 0xA2, // FEATURE (Data, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0x69, // USAGE (ShutdownImminent) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x69, // USAGE (ShutdownImminent) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0x73, // USAGE (CommunicationLost) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x73, // USAGE (CommunicationLost) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x09, 0x65, // USAGE (Overload) 0x81, 0xA3, // INPUT (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Bitfield) 0x09, 0x65, // USAGE (Overload) 0xB1, 0xA3, // FEATURE (Constant, Variable, Absolute, No Wrap, Linear, No Preferred, No Null Position, Volatile, Bitfield) 0x95, 0x02, // REPORT_COUNT (2) // padding bits to make the report byte aligned 0x81, 0x01, // INPUT (Constant, Array, Absolute) 0xB1, 0x01, // FEATURE (Constant, Array, Absolute, No Wrap, Linear, Preferred State, No Null Position, Nonvolatile, Bitfield) 0xC0, // END_COLLECTION 0xC0, // END_COLLECTION 0xC0 // END_COLLECTION }; #endif //当接收到GET HID报告描述符时调用 //应用返回指向描述符的指针 //描述符内容必须存在足够长的时间以完成传输 uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance) { (void) instance; return desc_hid_report; } //--------------------------------------------------------------------+ // Configuration Descriptor //--------------------------------------------------------------------+ enum { ITF_NUM_CDC = 0, ITF_NUM_CDC_DATA, ITF_NUM_HID, ITF_NUM_TOTAL }; // 修改配置描述符 enum { EPNUM_CDC_NOTIF = 1, // 0x81 (IN端点) EPNUM_CDC_OUT = 2, // 0x02 (OUT端点) EPNUM_CDC_IN = 3, // 0x83 (IN端点) EPNUM_HID_IN = 4 // 0x84 (IN端点) }; #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_DESC_LEN) #define EPNUM_HID 0x81 //定义了配置描述符 uint8_t const desc_configuration[] = { // 配置描述符头 TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_DESC_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 500), // CDC接口描述符(使用IAD) TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, 0x81, 8, // EP1 (通知), 8字节大小 0x02, 0x83, 64), // EP2 (OUT), EP3 (IN) // HID接口描述符 TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), 0x84, 64, 10) // EP4 (IN)
}; #if TUD_OPT_HIGH_SPEED // Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration // other speed configuration uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; // device qualifier is mostly similar to device descriptor since we don't change configuration based on speed tusb_desc_device_qualifier_t const desc_device_qualifier = { .bLength = sizeof(tusb_desc_device_qualifier_t), .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, .bcdUSB = USB_BCD, .bDeviceClass = 0x00, .bDeviceSubClass = 0x00, .bDeviceProtocol = 0x00, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, .bNumConfigurations = 0x01, .bReserved = 0x00 }; // Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. // device_qualifier descriptor describes information about a high-speed capable device that would // change if the device were operating at the other speed. If not highspeed capable stall this request. uint8_t const* tud_descriptor_device_qualifier_cb(void) { return (uint8_t const*) &desc_device_qualifier; } // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) { (void) index; // for multiple configurations // other speed config is basically configuration with type = OHER_SPEED_CONFIG memcpy(desc_other_speed_config, desc_configuration, CONFIG_TOTAL_LEN); desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; // this example use the same configuration for both high and full speed mode return desc_other_speed_config; } #endif // highspeed // Invoked when received GET CONFIGURATION DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { (void) index; // for multiple configurations // This example use the same configuration for both high and full speed mode return desc_configuration; } //--------------------------------------------------------------------+ // String Descriptors //--------------------------------------------------------------------+ // String Descriptor Index enum { STRID_LANGID = 0, //语言ID STRID_MANUFACTURER, //制造商字符串索引 STRID_PRODUCT, //产品字符串索引。 STRID_SERIAL, //序列号字符串索引。 }; //指向字符串描述符的指针数组 char const *string_desc_arr[] = { (const char[]) { 0x09, 0x04 }, // 0: 支持的语言是英语(0x0409) "Argon", // 1: 制造商 "Argon_USB", // 2: 产品 NULL, // 3: 如果可以的话,序列将使用唯一的ID }; static uint16_t _desc_str[32 + 1]; //当接收到GET字符串描述符请求时调用 //应用程序返回指向描述符的指针,其内容必须存在足够长的时间以完成传输 uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { (void) langid; size_t chr_count; switch ( index ) { //对于 STRID_LANGID(0),返回语言 ID。 case STRID_LANGID: memcpy(&_desc_str[1], string_desc_arr[0], 2); chr_count = 1; break; //对于 STRID_SERIAL(3),调用 board_usb_get_serial 获取唯一序列号。 case STRID_SERIAL: chr_count = board_usb_get_serial(_desc_str + 1, 32); break; 将名字变成字符串,并转换为UTF-16格式返回 default: if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL; const char *str = string_desc_arr[index]; // 最大长度 chr_count = strlen(str); size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; //-1表示字符串类型 if ( chr_count > max_count ) chr_count = max_count; // 将ASCII字符串转换UTF-16 for ( size_t i = 0; i < chr_count; i++ ) { _desc_str[1 + i] = str[i]; } break; } // 第一个字节是长度(包括头),第二个字节是字符串类型 _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); return _desc_str; } |
4、在VS CODE工程文件夹下,添加驱动usb_descriptors.h(VS CODE会自动加载)
#ifndef USB_DESCRIPTORS_H_ #define USB_DESCRIPTORS_H_ enum { REPORT_ID_KEYBOARD = 1, REPORT_ID_MOUSE, REPORT_ID_CONSUMER_CONTROL, REPORT_ID_GAMEPAD, HID_PD_REMAININGCAPACITY = 0x0C, // 电池剩余容量报告 ID HID_PD_PRESENTSTATUS = 0x07, // 当前状态报告 ID REPORT_ID_COUNT }; #endif /* USB_DESCRIPTORS_H_ */ |
5、添加对应驱动文件名称
6、打开USB开关、关闭串口开关(只能打开一个)
7、添加库
8、添加配置(CMakeLists.txt中)
# 添加TinyUSB配置 target_compile_definitions(ArgonOne_RP2350 PRIVATE CFG_TUD_CDC=1 CFG_TUD_HID=1 CFG_TUD_CDC_RX_BUFSIZE=256 CFG_TUD_CDC_TX_BUFSIZE=256 ) |
9、在应用层中调用
1)添加驱动头文件(main.c中)
#include "GUA_USBtoUart.h" |
2)添加驱动初始化代码1(main.c的main函数中)
//串口转USB初始化 GUA_USBtoUart_Init(); |
注意这个函数不能被调用两次,避免低功耗唤醒后被再次初始化,所以这条放到GUA_Init函数外
八、实验结果
用USB连接设备和树莓派,编译并仿真,全速运行。
因此实验成功。