ESP32-S3跑tinyusb回报率问题

lty6531600
Posts: 1
Joined: Sat Mar 07, 2026 9:09 am

ESP32-S3跑tinyusb回报率问题

Postby lty6531600 » Sat Mar 07, 2026 9:41 am

从官方的示例https://github.com/espressif/esp-idf/bl ... ple_main.c简单修改了一下,发现在Windows平台可以跑到1000HZ,在安卓只能跑到500HZ,但是接在某些扩展坞上再插安卓又能到1000HZ,不太理解为什么。有什么办法让直插安卓也能1000HZ的配置吗?

Code: Select all

/*
 * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */
// #include "tusb_config.h"
#include <stdlib.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "tinyusb.h"
#include "tinyusb_default_config.h"
#include "class/hid/hid_device.h"
#include "driver/gpio.h"

#define APP_BUTTON (GPIO_NUM_0) // 默认使用BOOT信号
static const char *TAG = "example";

/************* TinyUSB 描述符 ****************/

#define TUSB_DESC_TOTAL_LEN      (TUD_CONFIG_DESC_LEN + CFG_TUD_HID *TUD_HID_DESC_LEN)

/**
 * @brief HID报告描述符
 *
 * 在本示例中,我们实现了键盘+鼠标HID设备,
 * 因此必须定义两个报告描述符
 */
const uint8_t hid_report_descriptor[] = {
    //
    TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(HID_ITF_PROTOCOL_MOUSE))
};

/**
 * @brief 字符串描述符
 */
const char* hid_string_descriptor[5] = {
    // 字符串描述符指针数组
    (char[]){0x09, 0x04},  // 0: 支持的语言是英语 (0x0409)
    "TinyUSB",             // 1: 制造商
    "TinyUSB Device",      // 2: 产品
    "123456",              // 3: 序列号,应该使用芯片ID
    "Example HID interface",  // 4: HID
};

/**
 * @brief 配置描述符
 *
 * 这是一个简单的配置描述符,定义了1个配置和1个HID接口
 */
static const uint8_t hid_configuration_descriptor[] = {
    // 配置编号、接口数量、字符串索引、总长度、属性、功耗(mA)
    TUD_CONFIG_DESCRIPTOR(1, 1, 0, TUSB_DESC_TOTAL_LEN,
    TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),

    //接口编号、字符串索引、启动协议、报告描述符长度、端点输入地址、大小和轮询间隔
    TUD_HID_DESCRIPTOR(0, 4, false, sizeof(hid_report_descriptor), 0x81, 16,
    1),
};

/********* TinyUSB HID 回调函数 ***************/

/**
 * @brief 获取HID报告描述符回调函数
 *
 * 当主机请求获取HID报告描述符时,TinyUSB会调用此函数
 *
 * @param instance HID接口实例编号(本示例中未使用)
 * @return 指向HID报告描述符的指针,其内容必须存在足够长的时间以完成传输
 */
// 当收到GET HID REPORT DESCRIPTOR请求时调用
// 应用程序返回描述符指针,其内容必须存在足够长的时间以完成传输
uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance)
{
    // 我们只使用一个接口和一个HID报告描述符,因此可以忽略参数'instance'
    return hid_report_descriptor;
}

/**
 * @brief 获取HID报告回调函数
 *
 * 当主机请求获取HID报告时,TinyUSB会调用此函数
 *
 * @param instance HID接口实例编号
 * @param report_id 报告ID
 * @param report_type 报告类型
 * @param buffer 用于存储报告内容的缓冲区
 * @param reqlen 请求的报告长度
 * @return 实际填充的报告长度,返回0将导致堆栈暂停请求
 */
// 当收到GET_REPORT控制请求时调用
// 应用程序必须填充缓冲区报告的内容并返回其长度。
// 返回零将导致堆栈暂停请求
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;
    (void) report_id;
    (void) report_type;
    (void) buffer;
    (void) reqlen;

    return 0;
}

/**
 * @brief 设置HID报告回调函数
 *
 * 当主机发送SET_REPORT控制请求或在OUT端点上发送数据时,TinyUSB会调用此函数
 *
 * @param instance HID接口实例编号
 * @param report_id 报告ID(当在OUT端点上接收数据时为0)
 * @param report_type 报告类型(当在OUT端点上接收数据时为0)
 * @param buffer 包含报告内容的缓冲区
 * @param bufsize 缓冲区大小
 */
// 当收到SET_REPORT控制请求或
// 在OUT端点上收到数据时调用(Report ID = 0, Type = 0)
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)
{
}

/********* 应用程序 ***************/

typedef enum {
    MOUSE_DIR_RIGHT,
    MOUSE_DIR_DOWN,
    MOUSE_DIR_LEFT,
    MOUSE_DIR_UP,
    MOUSE_DIR_MAX,
} mouse_dir_t;

#define DISTANCE_MAX        125
#define DELTA_SCALAR        5

static void mouse_draw_square_next_delta(int8_t *delta_x_ret, int8_t
*delta_y_ret)
{
    static mouse_dir_t cur_dir = MOUSE_DIR_RIGHT;
    static uint32_t distance = 0;

    // 计算下一个增量
    if (cur_dir == MOUSE_DIR_RIGHT) {
        *delta_x_ret = DELTA_SCALAR;
        *delta_y_ret = 0;
    } else if (cur_dir == MOUSE_DIR_DOWN) {
        *delta_x_ret = 0;
        *delta_y_ret = DELTA_SCALAR;
    } else if (cur_dir == MOUSE_DIR_LEFT) {
        *delta_x_ret = -DELTA_SCALAR;
        *delta_y_ret = 0;
    } else if (cur_dir == MOUSE_DIR_UP) {
        *delta_x_ret = 0;
        *delta_y_ret = -DELTA_SCALAR;
    }

    // 更新当前方向的累积距离
    distance += DELTA_SCALAR;
    // 检查是否需要改变方向
    if (distance >= DISTANCE_MAX) {
        distance = 0;
        cur_dir++;
        if (cur_dir == MOUSE_DIR_MAX) {
            cur_dir = 0;
        }
    }
}

static void app_send_hid_demo(void)
{
    // // 键盘输出:发送按键'a/A'的按下和释放
    // ESP_LOGI(TAG, "发送键盘报告");
    // uint8_t keycode[6] = {HID_KEY_A};
    // /**
    //  * @brief 发送键盘报告
    //  *
    //  * 向主机发送键盘状态报告,包括按键和修饰键状态
    //  *
    //  * @param report_id 报告ID,使用HID_ITF_PROTOCOL_KEYBOARD
    //  * @param modifier 修饰键状态(如Ctrl、Shift等)
    //  * @param keycode 按键代码数组,最多6个按键
    //  */
    // tud_hid_keyboard_report(HID_ITF_PROTOCOL_KEYBOARD, 0, keycode);
    // vTaskDelay(pdMS_TO_TICKS(50));
    // tud_hid_keyboard_report(HID_ITF_PROTOCOL_KEYBOARD, 0, NULL);

    // // 鼠标输出:以正方形轨迹移动鼠标光标
    // ESP_LOGI(TAG, "发送鼠标报告");
    int8_t delta_x;
    int8_t delta_y;
    for (int i = 0; i < (DISTANCE_MAX / DELTA_SCALAR) * 4; i++) {
        // 获取正方形绘制模式中的下一个x和y增量
        mouse_draw_square_next_delta(&delta_x, &delta_y);
        /**
         * @brief 发送鼠标报告
         *
         * 向主机发送鼠标状态报告,包括按键状态和移动增量
         *
         * @param report_id 报告ID,使用HID_ITF_PROTOCOL_MOUSE
         * @param buttons 鼠标按键状态
         * @param delta_x X轴移动增量
         * @param delta_y Y轴移动增量
         * @param delta_wheel 滚轮移动增量
         * @param delta_pan 平移移动增量(通常未使用)
         */
        while( !tud_hid_ready())
        {
            // vTaskDelay(pdMS_TO_TICKS(1));
        }
        tud_hid_mouse_report(HID_ITF_PROTOCOL_MOUSE, 0x00, delta_x, delta_y,
        0, 0);
    }
}

/**
 * @brief 设备描述符
 * bcdUSB = 0x0200 表示 USB 2.0
 */
static const tusb_desc_device_t my_device_descriptor = {
    .bLength            = sizeof(tusb_desc_device_t),
    .bDescriptorType    = TUSB_DESC_DEVICE,
    .bcdUSB             = 0x0200, // 修改这里:0x0200 = USB 2.0, 0x0110 = USB 1.1 .bDeviceClass       = 0x00,   // 设备类由接口定义
    .bDeviceSubClass    = 0x00,
    .bDeviceProtocol    = 0x00,
    .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
    .idVendor           = 0x303A, // Espressif VID
    .idProduct          = 0x4000,
    .bcdDevice          = 0x0100,

    .iManufacturer      = 0x01,
    .iProduct           = 0x02,
    .iSerialNumber      = 0x03,

    .bNumConfigurations = 0x01
};

void app_main(void)
{
    // 初始化将触发HID报告的按钮
    const gpio_config_t boot_button_config = {
        .pin_bit_mask = BIT64(APP_BUTTON),
        .mode = GPIO_MODE_INPUT,
        .intr_type = GPIO_INTR_DISABLE,
        .pull_up_en = true,
        .pull_down_en = false,
    };
    ESP_ERROR_CHECK(gpio_config(&boot_button_config));

    ESP_LOGI(TAG, "USB初始化");
    tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();

    tusb_cfg.descriptor.device = &my_device_descriptor;
    tusb_cfg.descriptor.full_speed_config = hid_configuration_descriptor;
    tusb_cfg.descriptor.string = hid_string_descriptor;
    tusb_cfg.descriptor.string_count = sizeof(hid_string_descriptor) /
    sizeof(hid_string_descriptor[0]);
#if (TUD_OPT_HIGH_SPEED)
    tusb_cfg.descriptor.high_speed_config = hid_configuration_descriptor;
#endif // TUD_OPT_HIGH_SPEED

    /**
     * @brief 安装TinyUSB驱动
     *
     * 初始化并安装TinyUSB驱动,配置USB设备参数
     *
     * @param cfg TinyUSB配置结构体指针
     * @return 成功返回ESP_OK,失败返回错误代码
     */
    ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
    ESP_LOGI(TAG, "USB初始化完成");

    while (1) {
        /**
         * @brief 检查USB设备是否已挂载
         *
         * 检查USB设备是否已被主机识别并挂载
         *
         * @return 如果设备已挂载返回true,否则返回false
         */
        if (tud_mounted()) {
            static bool send_hid_data = true;
            if (send_hid_data) {
                for(int i = 0; i < 50; i++){
                    app_send_hid_demo();
                }
            }
            send_hid_data = !gpio_get_level(APP_BUTTON);
        }
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

Who is online

Users browsing this forum: No registered users and 2 guests