首页 > 其他分享 >记录5:ESP32S3的usb使用

记录5:ESP32S3的usb使用

时间:2024-08-10 16:23:56浏览次数:8  
标签:usb 记录 itf void tu rx ESP32S3 callback test

0、前期准备

1、会使用idf开发环境

2、懂得kconfig

1、知识储备

1.1 概述

​ TingUSB是一个开源的跨平台的USB主机/设备的usb协议栈,常用在mcu开发平台,由于不采用动态分配内存以及阻塞所有中断事件,将中断事件

要处理的事情都放在,非中断函数中处理,因此该usb栈内存设计非常安全、线程非常安全。

1.2 功能架构

​ ESP32S3内部集成了一个USB OTG外设,可配置成主机模式(host)或者设备模式(device),符合usb2.0协议规范。支持全速模式(12Mbit/s)和低速模式(1.5Mbit/s),还支持主机协商协议(HNP)和会话请求协议(SRP)。

设备模式特性

  • 端点 0 永远存在(双向控制,由 EP0 IN 和 EP0 OUT 组成)
  • 6 个附加端点 (1 ~ 6),可配置为 IN 或 OUT
  • 最多 5 个 IN 端点同时工作(包括 EP0 IN)
  • 所有 OUT 端点共享一个 RX FIFO
  • 每个 IN 端点都有专用的 TX FIF

主机模式特性

  • 8 个通道(管道) – 由 IN 与 OUT 两个通道组成的一个控制管道,因为 IN 和 OUT 必须分开处理。仅支持控制传输类型。 – 其余 7 个管道可被配置为 IN 或OUT,支持批量、同步、中断中的任意传输类型。

  • 所有通道共用一个 RX FIFO、一个非周期性 TX FIFO、和一个周期性 TX FIFO。每个 FIFO 大小可配置。

1.4 ESP32S3的tinyUSB使用配置(设备模式)流程介绍

1、使用idf.py新建工程

2、导入tinyUSB组件,导入命令如下:

idf.py add-dependency "leeebo/tinyusb_src^0.16.0~2"

3、设置usb的配置描述符并且注册usb驱动

以下对tinyusb_config_t 结构体进行说明

typedef struct {
    union {
        const tusb_desc_device_t *device_descriptor;//设备描述符
        const tusb_desc_device_t *descriptor  __attribute__((deprecated));
    };
    const char **string_descriptor;//字符描述
    int string_descriptor_count; //字符描述的数量
    bool external_phy;//是否使用外部phy,一般为false
    const uint8_t *configuration_descriptor; //配置描述符
    bool self_powered;//是否自供电
    int vbus_monitor_io;//自供电电源检测脚
} tinyusb_config_t;

使用例子

tinyusb_config_t ubs_cfg = {
    .device_descriptor = NULL,
    .string_descriptor = NULL,
    .external_phy = false,
    .configuration_descriptor = NULL,
};
//一般为空即可,如果默认为空就会使用系统默认配置
//注册驱动
tinyusb_driver_install(&ubs_cfg);

4、初始化usb设备,并且注册相应的驱动

1.5 介绍如何使用ttyACM

以下是参考源码

#include "esp_log.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
#include "tusb_test.h"

void acm_rx_callback(int itf, cdcacm_event_t* event) {
    size_t rx_size = 0;
    esp_err_t err = tinyusb_cdcacm_read(itf, acm_buff, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size);
    if (err == ESP_OK) {
        ESP_LOG_BUFFER_HEXDUMP(TAG, acm_buff, rx_size, ESP_LOG_INFO);
    } else {
        ESP_LOGE(TAG, "read error(%s:%s)", __FILE__, __func__);
    }
}

void app_main(void) {

	tinyusb_config_t ubs_cfg = {
        .device_descriptor = NULL,
        .string_descriptor = NULL,
        .external_phy = false,
        .configuration_descriptor = NULL,
    };
    
    ESP_LOGI(TAG, "acm initialzationning ...");

    ESP_ERROR_CHECK(tinyusb_driver_install(&ubs_cfg));

    tinyusb_config_cdcacm_t acm_cfg = {
        .usb_dev = TINYUSB_USBDEV_0,
        .cdc_port = TINYUSB_CDC_ACM_0,
        .rx_unread_buf_sz = 64,
        .callback_rx = acm_rx_callback,
        .callback_rx_wanted_char = NULL,
        .callback_line_coding_changed = NULL,
        .callback_line_state_changed = NULL,
    };


    ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg));
    ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback(TINYUSB_CDC_ACM_0, CDC_EVENT_LINE_STATE_CHANGED,
                                                     &line_state_changed_callback));
}

到此esp32s3的使用介绍完毕。

以上是直接使用官方写好的usb设备驱动,但是如果想自定义自己的usb外设的话,可以参考以下步骤:

番外

介绍如何注册自定义的esp32s3 usb设备

1、新建components文件,将espressif__ esp_tinyusb 和 espressif__tinyusb两个组件由managed_components拷贝到components,不然在编译的时候,如果有修改过组件的内容会导致编译不过,或者修改的内容会被覆盖

2、在espressif__tinyusb/src/class新建一个文件夹,名字取自定义usb设备名,如test

3、在test文件夹中新建test_device.c和test_device.h两个设备文件,内容可参考cdc_acm

test_device.c内容参考如下:

#include "tusb_option.h"

#if (CFG_TUD_ENABLED && CFG_TUD_TEST)
#include "device/usbd.h"
#include "device/usbd_pvt.h"

#include "test_device.h"

enum
{
  BULK_PACKET_SIZE = (TUD_OPT_HIGH_SPEED ? 512 : 64)
};

/* 该结构体的用途:用于作为通讯节点 */
typedef struct {
    uint8_t ep_in;
    uint8_t ep_out;

    tu_fifo_t rx_ff;
    tu_fifo_t tx_ff;

    uint8_t rx_ff_buf[CFG_TUD_TEXT_RX_BUFSIZE];
    uint8_t tx_ff_buf[CFG_TUD_TEXT_TX_BUFSIZE];

    OSAL_MUTEX_DEF(rx_ff_mutex);
    OSAL_MUTEX_DEF(tx_ff_mutex);

    CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_TEXT_EP_BUFSIZE];
    CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_TEXT_EP_BUFSIZE];

} test_interface_t;

CFG_TUSB_MEM_SECTION tu_static test_interface_t _test_itf;

static bool prep_out_transaction(test_interface_t* p_test) {
    uint8_t const rhport = 0;
    uint16_t available = tu_fifo_remaining(_test_itf.rx_ff);
    TU_VERIFY(available >= sizeof(p_test->epout_buf));

    // claim endpoint
    //判断端点是否在使用
    TU_VERIFY(usbd_edpt_claim(rhport, p_test->ep_out));

    // fifo can be changed before endpoint is claimed
    available = tu_fifo_remaining(p_test->rx_ff);

    if (available >= sizeof(p_test->epout_buf)) {
        return usbd_edpt_xfer(rhport, p_test->ep_out, p_test->epout_buf, sizeof(p_test->epout_buf));
    } else {
        //释放端点
        usbd_edpt_release(rhport, p_test->ep_out);
        return false;
    }
}

void test_init(void) {

    tu_memclr(&_test_itf, sizeof(_test_itf));
    //配置数据传输管道
    tu_fifo_config(_test_itf.rx_ff, p_test->rx_ff_buf, TU_ARRAY_SIZE(p_test->rx_ff_buf), 1, false);
    tu_fifo_config(_test_itf.tx_ff, p_test->tx_ff_buf, TU_ARRAY_SIZE(p_test->tx_ff_buf), 1, true);
    //创建管道锁
    tu_fifo_config_mutex(_test_itf.rx_ff, NULL, osal_mutex_create(_test_itf.rx_ff_mutex));
    tu_fifo_config_mutex(_test_itf.tx_ff, osal_mutex_create(_test_itf.tx_ff_mutex), NULL);
}
void test_reset(uint8_t rhport) {
    
    tu_memclr(&_test_itf, sizeof(uint8_t)*2);
    
    //清空管道
    tu_fifo_clear(_test_itf.rx_ff);
    tu_fifo_clear(_test_itf.tx_ff);

    tu_fifo_set_overwritable(_test_itf.tx_ff, true);
}

uint16_t test_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
    test_interface_t* p_test = NULL;
    uint16_t drv_len = 0;

    TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass, 0);

    if (_test_itf.ep_in == 0) {
        p_test = &_test_itf;
    }

    TU_ASSERT(p_test, 0);

    uint8_t const* p_desc = (uint8_t const*)itf_desc;
    drv_len = tu_desc_len(p_desc);

    /* Interface */
    if (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) {
        /* Endpoint Descriptor */
        p_desc = tu_desc_next(p_desc);

        TU_ASSERT(
            usbd_open_edpt_pair(rhport, p_desc, itf_desc->bNumEndpoints, TUSB_XFER_BULK, _test_itf.ep_out, _test_itf.ep_in),
            0);
        drv_len += itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t);
    }

     prep_out_transaction(p_test);

    return drv_len;
}


bool test_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
    if (ep_addr == _test_itf.ep_out) {

        tu_fifo_write_n(_test_itf.rx_ff, _test_itf.epout_buf, (uint16_t)xferred_bytes);

        if (tud_test_rx_cb && !tu_fifo_empty(_test_itf.rx_ff)) {
            tud_test_rx_cb();
        }

        prep_out_transaction(&_test_itf);
    } else if (ep_addr == _test_itf.ep_in) {
        if ( tud_text_tx_complete_cb ) tud_text_tx_complete_cb();//调用回调
        if ( 0 == tud_test_write_flush())
        {
            if ( !tu_fifo_count(_test_itf.tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) )
            {
                if ( usbd_edpt_claim(rhport, _test_itf.ep_in) )
                {
                	usbd_edpt_xfer(rhport, _test_itf.ep_in, NULL, 0);
                }
            }
        }
    }
    return true;
}

bool test_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request){
    return true;
}

// app api
uint32_t tud_test_read(void* buffer, uint32_t bufsize) {
    uint32_t num_read = tu_fifo_read_n(&_test_itf.rx_ff, buffer, (uint16_t)bufsize);
    prep_out_transaction(&_test_itf);
    return num_read;
}

bool tud_test_peek(uint8_t* chr) {
    return tu_fifo_peek(&_test_itf.rx_ff, chr);
}

void tud_test_read_flush(void) {
    tu_fifo_clear(_test_itf.rx_ff);
    prep_out_transaction(&_test_itf);
}

uint32_t tu_test_write(void*buffer,uint32_t bufsize){
    test_interface_t* p_test = &_test_itf;
    uint16_t ret = tu_fifo_write_n(_test_itf.tx_ff,buffer,(uint16_t)bufsize);
    if ((tu_fifo_count(_test_itf.tx_ff) >= BULK_PACKET_SIZE) || ((CFG_TUD_TEXT_TX_BUFSIZE < BULK_PACKET_SIZE) && tu_fifo_full(_test_itf.tx_ff)))
    {
        tud_test_write_flush();
    }
    
    return ret;
}

uint32_t tud_test_write_flush(void){
  // Skip if usb is not ready yet
  TU_VERIFY(tud_ready(), 0);
  // No data to send
  if ( !tu_fifo_count(_test_itf.tx_ff) ) return 0;
  uint8_t const rhport = 0;
  // Claim the endpoint
  TU_VERIFY( usbd_edpt_claim(rhport, _test_itf.ep_in), 0 );
  // Pull data from FIFO
  uint16_t const count = tu_fifo_read_n(_test_itf.tx_ff, _test_itf.epin_buf, sizeof(_test_itf.epin_buf));
  if (count)
  {
    TU_ASSERT( usbd_edpt_xfer(rhport, _test_itf.ep_in, _test_itf.epin_buf, count), 0 );
    return count;
  }else
  {
    // Release endpoint since we don't make any transfer
    // Note: data is dropped if terminal is not connected
    usbd_edpt_release(rhport,_test_itf.ep_in);
    return 0;
  }
}

uint32_t tud_test_write_available(void)
{
  return tu_fifo_remaining(&_test_itf.tx_ff);
}

bool tud_test_write_clear(void)
{
  return tu_fifo_clear(&_test_itf.tx_ff);
}

#endif

test_device.h内容参考如下:

#ifndef _TUSB_TEST_DEVICE_H_
#define _TUSB_TEST_DEVICE_H_

#include "common/tusb_common.h"

#ifdef __cplusplus
 extern "C" {
#endif

void     test_init            (void);
void     test_reset           (uint8_t rhport);
uint16_t test_open            (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool     test_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
bool     test_xfer_cb         (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);

//callback
TU_ATTR_WEAK void tud_test_rx_cb(void);
TU_ATTR_WEAK void tud_test_tx_complete_cb(void);

// App API
uint32_t tud_test_read(void* buffer, uint32_t bufsize);
bool tud_test_peek(uint8_t* chr);
void tud_test_read_flush(void);

uint32_t tu_test_write(void*buffer,uint32_t bufsize);
uint32_t tud_test_write_flush(void);
uint32_t tud_test_write_available(void);
bool tud_test_write_clear(void);

#ifdef __cplusplus
 }
#endif

#endif /* _TUSB_TEST_DEVICE_H_ */

4、在tu_static usbd_class_driver_t const _usbd_driver 数组中注册自定义设备

tu_static usbd_class_driver_t const _usbd_driver[] =
{
...
  #if CFG_TUD_TEST
  {
    DRIVER_NAME("TEST")
    .init             = test_init,
    .reset            = test_reset,
    .open             = test_open,
    .control_xfer_cb  = test_control_xfer_cb,
    .xfer_cb          = test_xfer_cb,
    .sof              = NULL
  },
  #endif
...
}

5、在espressif__ esp_tinyusb/include/tusb_config.h文件中添加自定义设备配置

// TEST EP Size
#define CFG_TUD_TEST_EP_BUFSIZE      64
#define CFG_TUD_TEST_RX_BUFSIZE      CONFIG_TINYUSB_TEST_RX_BUFSIZE
#define CFG_TUD_TEST_TX_BUFSIZE      CONFIG_TINYUSB_TEST_TX_BUFSIZE

#define CFG_TUD_TEST                 CONFIG_TINYUSB_TEST_ENABLED

6、在espressif__ esp_tinyusb/include中新建tusb_test.h文件,内容可参考tusb_cdc_acm.h

内容如下

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
#include "tusb.h"
#include "tinyusb_types.h"

#if (CONFIG_TINYUSB_TEST_ENABLED != 1)
#error "TinyUSB TEST driver must be enabled in menuconfig"
#endif

#define EPNUM_TEST_IN 0x81
#define EPNUM_TEST_OUT 0x01

#define TUSB_CLASS_TEST 0xFF

#define TUD_TEST_DESC_LEN (8 + 9 + 7 + 7)
// Interface number, string index, EP Out & EP In address, EP size
#define TUD_TEST_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \
    /* Interface */ \
    8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 1, TUSB_CLASS_TEST, 0, 0, 0,  \
    9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_TEST, 0, 0, _stridx,     \
    7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,  \
    7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0



typedef enum{
    TEST_EVENT_RX,
    TEST_EVENT_TX_COMPLETE
}test_event_type_t;

typedef void (* tusb_test_callback_t)(void);

typedef struct{
    tusb_test_callback_t callback_rx;
    tusb_test_callback_t callback_tx_complete_callback;
}tinyusb_config_test_t;

esp_err_t tinyusb_test_register_callback(test_event_type_t _event_type,tusb_test_callback_t _callback);
esp_err_t tinyusb_test_read(uint8_t *out_buf, size_t out_buf_sz, size_t *rx_data_size);


#ifdef __cplusplus
extern }
#endif


7、在espressif__ esp_tinyusb/中新建tusb_test.c文件,内容可参考tusb_cdc_acm.c

内容如下:

#include "tusb_test.h"
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "tusb.h"

#include "sdkconfig.h"

static portMUX_TYPE test_lock = portMUX_INITIALIZER_UNLOCKED;
#define TEST_ENTER_CRITICAL() portENTER_CRITICAL(&test_lock)
#define TEST_EXIT_CRITICAL() portEXIT_CRITICAL(&test_lock)

static const char* TAG = "tusb_test";

static tinyusb_config_test_t _test_cfg;

esp_err_t tinyusb_test_read(uint8_t* out_buf, size_t out_buf_sz, size_t* rx_data_size) {
    *rx_data_size = tud_test_read(out_buf, out_buf_sz);
    return ESP_OK;
}

esp_err_t tinyusb_test_register_callback(test_event_type_t _event_type, tusb_test_callback_t _callback) {
    switch (_event_type) {
        case test_EVENT_RX:
            TEST_ENTER_CRITICAL();
            _test_cfg.callback_rx = _callback;
            TEST_EXIT_CRITICAL();
            return ESP_OK;
        case test_EVENT_TX_COMPLETE:
            TEST_ENTER_CRITICAL();
            _test_cfg.callback_tx_complete_callback = _callback;
            TEST_EXIT_CRITICAL();
            return ESP_OK;

        default:
            ESP_LOGE(TAG, "test Callback fun is NULL");
            return ESP_FAIL;
    }
}

void tud_test_rx_cb(void) {
    if (_test_cfg.callback_rx) {
        _test_cfg.callback_rx();
    }
}

void tud_test_tx_complete_cb(void) {
    if (_test_cfg.callback_tx_complete_callback) {
        _test_cfg.callback_tx_complete_callback();
    }
}

void tinyusb_test_relay_out_point(void){
    tud_test_relay_out_point();
}

到此设备新加完毕

使用例子:

#include "esp_log.h"
#include "tinyusb.h"
#include "tusb_test.h"

static uint8_t test_buff[CONFIG_TINYUSB_TEXT_RX_BUFSIZE + 1];

void test_rx_callback(int itf, cdcacm_event_t* event) {
    size_t rx_size = 0;
    esp_err_t err = tinyusb_test_read(itf, acm_buff, CONFIG_TINYUSB_TEXT_RX_BUFSIZE, &rx_size);
    if (err == ESP_OK) {
        ESP_LOG_BUFFER_HEXDUMP(TAG, test_buff, rx_size, ESP_LOG_INFO);
    } else {
        ESP_LOGE(TAG, "read error(%s:%s)", __FILE__, __func__);
    }
}

void app_main(void) {

	tinyusb_config_t ubs_cfg = {
        .device_descriptor = NULL,
        .string_descriptor = NULL,
        .external_phy = false,
        .configuration_descriptor = NULL,
    };
    
    ESP_LOGI(TAG, "test initialzationning ...");

    ESP_ERROR_CHECK(tinyusb_driver_install(&ubs_cfg));
	ESP_ERROR_CHECK(tinyusb_test_register_callback(DCM_EVENT_RX, test_rx_callback));
}

标签:usb,记录,itf,void,tu,rx,ESP32S3,callback,test
From: https://blog.csdn.net/qq_28695769/article/details/141054887

相关文章

  • 记录3:ESP32-C3的串口使用
    0、前期准备1、参考首篇文章搭建好esp32环境2、准备好一块esp32开发开发板(本作者使用了esp32c3作为开发平台)1、知识储备1.1概述​UART称为通用异步收发器,可以进行全双工/半双工数据通讯数据通讯,通讯距离取决于上拉驱动能力、波特率,一般只在电路板上使用,如果需要长距......
  • 大一暑假学习记录6
    这一周我基本完成了刘立嘉老师布置的暑假作业,其中通讯录的录入与显示,整数分解为若干项之和是我认为最难做的题目,前者的难点是sample有查询越界、最大N,反复查询同一记录等等。后者则是样例等价,多行输出难以解决。于是我又重新学习了结构体部分的内容,定义了Contact结构体来存储......
  • Java学习记录第六周
    今天在进行数组反转时第一次用了这个代码![](https://img2024.cnblogs.com/blog/3475598/202408/3475598-20240808230516048-1627660110.png)没有考虑到第一个循环只进行一次时,第二个循环进行完一轮了。第二次用了这个代码没有考虑到数组直接赋值会使原先的值丢失,所以最后又定......
  • MSPM0G3057学习记录(二)电机PID闭环控制
    目录一、PID原理讲解  1.比例增益(Proportional): 2.  积分时间(Integral): 3.  微分时间(Derivative):二、PID控制原理图三、位置式PID公式  四、参考代码(一)五、参考代码(二) 一、PID原理讲解          PID(Proportional-Integral-Derivative)闭环控......
  • 记录一些除OI外的其他知识
    rt,未来可能会记录些本人折腾arch的经验,资源,歌单,游戏等等之前看b说chrome的体验最很好,然而我之前实测linux下firefox表现最好,评论说windows下chrome好,linux下firefox好,恰巧前几天我为了在pc玩扒鸡回了win10,实测chrome体验确实不错(在可以用tz的情况),但是同步数据不太好所以有了在......
  • 【VSCode】《VSCode安装本地历史记录插件并配置搜索忽略》
    前言VSCode本地会记录修改和保存的历史文件信息,当没有使用git管理的时候,就可以通过本地历史搜索快速比对还原历史代码。插件安装目前最新版本停留在1.8.1更新时间为2020/3/4号;估计作者后续也没有更新计划了。安装后会在左下角和左上角出现LOCALHISTORY和./history两个......
  • 外部服务采购主记录和条件
    外部服务采购需求确定:企业部门可能会产生某些外部服务的需求。可能涉及到特定项目(例如软件文档的翻译)或维修工作(例如公司必需电子工程的外包)。服务规范的创建:需求确定后,在系统中创建凭证作为采购流程的基础(可以为采购申请)。该凭证可包含一组服务规范,其中详细列出所需服务。......
  • 前端vue3学习记录二
    ref和reactive的补充在使用reactive 进行对象数据的响应化时,要注意,将响应式对象分配给一个新的对象的时候,新的对象是不具有响应式性质的functionChangecar(){car={brand:'红旗',price:20000}//没有响应式性质car=reactive({brand:'红旗',price:20......
  • 电脑外接设备管理软件有哪些(三款USB外设管理软件推荐)
    “小张,你上次用U盘拷贝资料时,有没有担心过数据安全问题?”“是啊,李姐,我总感觉用U盘传文件不太放心,万一数据被误删或者泄露就麻烦了。”正是这样的担忧,促使了电脑外接设备管理软件的出现。这些软件通过提供设备识别、访问控制、数据加密等功能,为企业的数据安全保驾护航。本......
  • 性能数据采集分析(Guider开源项目集成)(第一次发帖,用于个人经验记录)
    https://github.com/iipeace/guiderpython环境下集成guider的经验分享初始化Guider环境:步骤一:PushPython-Guider环境假设python-guider环境是一个文件夹或者压缩文件,需要将其推送到Android设备上。假设该环境位于本地路径/path/to/python-guider。adbpush/path/to......