超级版主
 
- 积分
- 5418
- 金钱
- 5418
- 注册时间
- 2019-5-8
- 在线时间
- 1404 小时
|
|
第四十九章 USB读卡器(Slave)实验
1)实验平台:正点原子DNESP32P4开发板
2)章节摘自【正点原子】ESP32-P4开发指南— V1.0
3)购买链接:https://detail.tmall.com/item.htm?id=873309579825
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32P4.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子DNESP32S3开发板技术交流群:132780729
ESP32-P4芯片都自带了USB OTG HS和FS,支持USB Host和USB Device,所有USB相关例程,均可以使用USB OTG HS和FS实现。下面,我们将介绍如何使用USB OTG HS在DNESP32P4开发板上实现一个USB读卡器。
本章分为如下几个小节:
49.1 USB读卡器简介
49.2 硬件设计
49.3 程序设计
49.4 下载验证
49.1 USB读卡器简介
USB读卡器是一种通过USB接口连接计算机或其他设备,用于读取和写入存储卡(如SD卡、TF卡、CF卡等)数据的设备。它是一种便携、易用的外设,广泛应用于数据存储、传输和管理场景。本章节,我们使用板载的SD卡和内置Flash作为存储卡,实现USB读卡器功能。对于SD卡相关知识,请读者参考本书籍的第38章节。
ESP32-P4的USB OTG HS接口是支持USB读卡器功能的。Espressif官方在ESP-IDF中提供了针对USB device大容量存储设备(MSC)的示例代码,路径为:esp-idf\examples\peripherals\usb\device\tusb_msc。本章节实验将参考官方示例代码,通过ESP32-P4的USB HOST接口实现对USB读卡器设备读写操作。
49.2 硬件设计
49.2.1 程序功能
通过使用USB A转A数据线,将开发板的HOST端口与PC的USB端口连接。连接成功后,PC会识别并显示开发板中挂载的“SD卡”和“SPI Flash”磁盘,用户即可对其进行文件读写操作。
本章节实验包含两个示例:
1)39_usb_sd_u实验:将SD卡挂载为磁盘,实现PC的文件系统交互功能。
2)40_usb_flash_u实验:将开发板内置的Flash挂载为磁盘,实现PC的文件系统交互功能。
读者可以根据实际需求选择对应的实验进行操作。
49.2.2 硬件资源
1)LED灯
LED 0 - IO51
2)RGBLCD/MIPILCD(引脚太多,不罗列出来)
3)SPIFFS
4)SD卡
CMD - IO44
CLK - IO43
D0 - IO39
D1 - IO40
D2 - IO41
D3 - IO42
49.2.3 原理图
USB HOST原理图已在48.2.3小节中详细阐述,为避免重复,此处不再赘述。
49.3 程序设计
49.3.1 esp_tinyusb的IDF驱动
esp_tinyusb组件驱动位于ESP-IDF在线组件注册表中。如果需要将该组件添加到项目工程中,可按照以下步骤操作:
1)打开ESP-IDF注册表。
2)搜索 “esp_tinyusb”组件。
3)将组件安装到项目中。
组件安装完成后,系统会自动更新main文件夹中的特殊组件清单文件idf_component.yml在项目编译时,系统会根据清单文件从注册表中下载并集成该组件到工程中。关于上述操作流程,可参考本书籍第八章的内容。
为了使用 esp_tinyusb组件提供的功能,首先需要在代码中导入以下头文件:
- #include "tinyusb.h"
- #include "tusb_msc_storage.h"
复制代码 接下来,作者将介绍本章节实验用到的esp_tinyusb函数,这些函数的描述及其作用如下:
1,在TinyUSB驱动中注册SD卡存储类型tinyusb_msc_storage_init_sdmmc
该函数用于在TinyUSB驱动中注册SD卡设备,其函数原型如下:
- esp_err_t tinyusb_msc_storage_init_sdmmc(
- const tinyusb_msc_sdmmc_config_t*config);
复制代码 函数形参:
表49.3.1.1 tinyusb_msc_storage_init_sdmmc函数形参描述
返回值:
ESP_OK表示注册成功。
ESP_ERR_NO_MEM表示分配内存失败。
config为指向配置SDMMC初始化的结构体。接下来,笔者将详细介绍tinyusb_msc_sdmmc_config_t结构体中的各个成员变量,如下代码所示:
- typedef struct {
- sdmmc_card_t *card; /* 指向SDMMC卡配置结构体的指针 */
- /* 指向挂载/卸载操作完成后回调函数的指针 */
- tusb_msc_callback_t callback_mount_changed;
- /* 指向挂载/卸载操作开始前回调函数的指针 */
- tusb_msc_callback_t callback_premount_changed;
- const esp_vfs_fat_mount_config_t mount_config; /* FATFS挂载配置 */
- } tinyusb_msc_sdmmc_config_t;
复制代码 上述结构体用于配置SDMMC初始化参数,以下对各个成员做简单介绍。
1)card:
定义sdmmc_card_t结构体指针变量,用于指向SDMMC句柄。
2)callback_mount_changed:
挂载/卸载操作完成后回调函数的指针,一般我们配置为NULL。若读者向配置这一个回调函数,则可根据tusb_msc_callback_t类型自定义回调函数。
3)callback_premount_changed:
挂载/卸载操作开始前回调函数的指针,一般我们配置为NULL。若读者向配置这一个回调函数,则可根据tusb_msc_callback_t类型自定义回调函数。
4)mount_config:
FATFS挂载配置,一般我们配置mount_config.max_files字段为5。
2,注册MSC事件回调函数tinyusb_msc_register_callback
该函数用于注册MSC事件回调函数,其函数原型如下:
- esp_err_t tinyusb_msc_register_callback(tinyusb_msc_event_type_t event_type,
- tusb_msc_callback_t callback);
复制代码 函数形参:
表49.3.1.2 tinyusb_msc_register_callback函数形参描述
返回值:
ESP_OK表示注册成功。
ESP_ERR_INVALID_ARG表示参数无效。
3,将存储设备挂载到应用程序固件tinyusb_msc_storage_mount
该函数用于将存储设备挂载到应用程序固件,其函数原型如下:
- esp_err_t tinyusb_msc_storage_mount(const char *base_path);
复制代码 函数形参:
表49.3.1.3 tinyusb_msc_storage_mount函数形参描述
返回值:
ESP_OK表示挂载成功。
ESP_ERR_NOT_FOUND表示已挂载数达到最大数量。
ESP_ERR_NO_MEM表示内存不足或注册太多VFS。
4,安装USB设备驱动tinyusb_driver_install
该函数用于安装USB设备驱动,其函数原型如下:
- esp_err_t tinyusb_driver_install(const tinyusb_config_t *config);
复制代码 函数形参:
表49.3.1.4 esp_err_t tinyusb_driver_install函数形参描述
返回值:
ESP_OK表示驱动程序和TinyUSB栈成功安装。
ESP_ERR_INVALID_ARG表示参数无效导致驱动程序和TinyUSB栈安装失败。
ESP_FAIL表示内部错误导致驱动程序和TinyUSB栈安装失败。
49.3.2 程序流程图
图49.3.2.1 USB读卡器实验程序流程图
49.3.3 程序解析
本实验旨在通过调用ESP-TinyUSB组件的API函数,实现USB读卡器功能。实验的代码已经编写在main.c文件中,并提供了两种实现方式:
1,main.c驱动代码(SD卡方式)
- sdmmc_card_t *card = NULL;
- /* 挂载名称 */
- #define BASE_PATH "/0:" /* 挂载分区大小的基本路径 */
- /* TinyUSB 描述符 */
- #define EPNUM_MSC 1
- #define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
- enum {
- ITF_NUM_MSC = 0,
- ITF_NUM_TOTAL
- };
- enum {
- EDPT_CTRL_OUT = 0x00,
- EDPT_CTRL_IN = 0x80,
- EDPT_MSC_OUT = 0x01,
- EDPT_MSC_IN = 0x81,
- };
- /**
- * @brief sdmmc
- * [url=home.php?mod=space&uid=271674]@param[/url] 无
- * @retval ESP_OK:初始化成功
- */
- esp_err_t sdmmc_init(void)
- {
- /* 使用默认配置SDMMC */
- sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
- /* 传输宽度为4 */
- slot_config.width = 4;
- /* 根据原理图配置管脚 */
- slot_config.d0 = GPIO_NUM_39;
- slot_config.d1 = GPIO_NUM_40;
- slot_config.d2 = GPIO_NUM_41;
- slot_config.d3 = GPIO_NUM_42;
- slot_config.clk = GPIO_NUM_43;
- slot_config.cmd = GPIO_NUM_44;
- /* 配置内部上拉 */
- slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
- card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));
- /* 配置默认 */
- sdmmc_host_t host = SDMMC_HOST_DEFAULT();
- host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; /* 设置速度最大值为40MHz */
- (*host.init)();
- sdmmc_host_init_slot(host.slot, (const sdmmc_slot_config_t *) &slot_config);
- while (sdmmc_card_init(&host, card))
- {
- vTaskDelay(pdMS_TO_TICKS(3000));
- }
- sdmmc_card_print_info(stdout, card);
- return ESP_OK;
- }
- /* USB设备描述符 */
- static tusb_desc_device_t descriptor_config = {
- .bLength = sizeof(descriptor_config), /* 描述符的大小 */
- .bDescriptorType = TUSB_DESC_DEVICE, /* 描述符类型 */
- .bcdUSB = 0x0200, /* BUSB规格发布号 */
- .bDeviceClass = TUSB_CLASS_MISC, /* MSC类 */
- .bDeviceSubClass = MISC_SUBCLASS_COMMON, /* 子类代码(由USB-IF分配) */
- .bDeviceProtocol = MISC_PROTOCOL_IAD, /* 协议代码(由USB-IF分配) */
- .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, /* 端点0的最大数据包大小*/
- .idVendor = 0x303A, /* 供应商ID(由USB-IF分配) */
- .idProduct = 0x4002, /* 产品ID(由制造商指定) */
- .bcdDevice = 0x100, /* 以二进制编码的十进制表示 */
- .iManufacturer = 0x01, /* 描述制造商的字符串描述符的索引 */
- .iProduct = 0x02, /* 描述产品的字符串描述符的索引 */
- .iSerialNumber = 0x03, /* 描述设备序列号的字符串描述符索引 */
- .bNumConfigurations = 0x01 /* 可能的配置数 */
- };
- /* 全速配置描述符 */
- static uint8_t const msc_fs_configuration_desc[] = {
- /* 配置号,接口计数,字符串索引,总长度,属性,功率(mA) */
- TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
- TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
- /* 接口编号,字符串索引,EP Out & EP In地址,EP大小 */
- TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 64),
- };
- #if (TUD_OPT_HIGH_SPEED)
- /* USB设备限定符 */
- static const tusb_desc_device_qualifier_t device_qualifier = {
- .bLength = sizeof(tusb_desc_device_qualifier_t), /* 描述符的大小 */
- .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, /* 设备限定符类型 */
- .bcdUSB = 0x0200, /* USB规格版本号(例如,V2.00为0200H) */
- .bDeviceClass = TUSB_CLASS_MISC, /* 类代码 */
- .bDeviceSubClass = MISC_SUBCLASS_COMMON, /* 子类代码 */
- .bDeviceProtocol = MISC_PROTOCOL_IAD, /* 协议码 */
- .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, /* 最大数据包大小 */
- .bNumConfigurations = 0x01, /* 其他速度配置的个数 */
- .bReserved = 0 /* 保留,必须为零 */
- };
- /* 高速配置描述符 */
- static uint8_t const msc_hs_configuration_desc[] = {
- /* 配置号,接口计数,字符串索引,总长度,属性,功率(mA) */
- TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
- TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
- /* 接口编号,字符串索引,EP Out & EP In地址,EP大小 */
- TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 512),
- };
- #endif
- /* 字符串描述符 */
- static char const *string_desc_arr[] = {
- (const char[]) { 0x09, 0x04 }, /* 0: 支持英文 (0x0409) */
- "TinyUSB", /* 1: 生产商 */
- "TinyUSB Device", /* 2: 产品 */
- "123456", /* 3: 序列 */
- "Example MSC", /* 4. MSC */
- };
- void app_main(void)
- {
- esp_err_t ret;
-
- ret = nvs_flash_init(); /* 初始化NVS */
- if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
- {
- ESP_ERROR_CHECK(nvs_flash_erase());
- ESP_ERROR_CHECK(nvs_flash_init());
- }
- led_init(); /* LED初始化 */
- lcd_init(); /* LCD屏初始化 */
- /* 显示实验信息 */
- lcd_show_string(30, 50, 200, 16, 16, "ESP32-P3", RED);
- lcd_show_string(30, 70, 200, 16, 16, "USB SD TEST", RED);
- lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
- ESP_ERROR_CHECK(sdmmc_init());
- /* 配置SDMMC */
- const tinyusb_msc_sdmmc_config_t config_sdmmc = {
- .card = card, /* 指向sdmmc卡配置结构的指针 */
- .callback_mount_changed = NULL, /* 注册回调函数,用来初始化子分区表 */
- .mount_config.max_files = 5, /* 最大文件打开数量 */
- };
- /* 注册存储类型sd卡与tinyusb驱动程序 */
- ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc));
- /* 注册回调的其他方法,即使用单独的API注册 */
- ESP_ERROR_CHECK(tinyusb_msc_register_callback
- (TINYUSB_MSC_EVENT_MOUNT_CHANGED, NULL));
- /* 挂载设备 */
- ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH));
- /* 配置USB */
- const tinyusb_config_t tusb_cfg = {
- .device_descriptor = &descriptor_config, /* 设备描述符 */
- .string_descriptor = string_desc_arr, /* 字符串描述符 */
- .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]), /* 字符串描述符大小 */
- .external_phy = false, /* 使用内部USB PHY */
- #if (TUD_OPT_HIGH_SPEED)
- .fs_configuration_descriptor = msc_fs_configuration_desc,
- .hs_configuration_descriptor = msc_hs_configuration_desc,
- .qualifier_descriptor = &device_qualifier,
- #else
- .configuration_descriptor = msc_fs_configuration_desc, /* 配置描述符 */
- #endif
- };
- /* 初始化USB */
- ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
- while(1)
- {
- LED0_TOGGLE();
- vTaskDelay(500);
- }
- }
复制代码 上述代码实现了在ESP32-P4上通过SD卡模拟USB存储设备(Mass Storage Device)的功能,并利用TinyUSB库完成设备描述符和配置描述符的注册,使设备能与主机通信。为了更好地了解上述代码,这里笔者分为四个部分讲解。
1)SD卡初始化:
通过sdmmc_init函数完成对SD卡的初始化,包括SDMMC管脚的配置、数据传输宽度(4位)、内部上拉电阻的启用等。程序检测到SD卡插入后,完成卡信息的打印,为后续的文件系统挂载和USB存储功能提供支持。
2)TinyUSB设备描述符配置:
定义了USB设备描述符descriptor_config,包括USB协议版本、设备类(MSC类)、厂商ID、产品ID等内容。通过该描述符,设备向主机描述自身的功能和特点。同时,代码根据不同速度(全速/高速)定义了配置描述符(msc_fs_configuration_desc和msc_hs_configuration_desc)以支持不同的USB数据传输模式。
3)文件系统挂载与存储注册:
利用TinyUSB的API函数tinyusb_msc_storage_init_sdmmc注册SD卡作为USB存储设备。通过tinyusb_msc_storage_mount将SD卡的分区挂载为文件系统(挂载路径为/0:)。代码还支持注册回调函数,用于监听挂载事件。
4)TinyUSB驱动初始化:
使用tinyusb_driver_install函数完成TinyUSB驱动的安装,将USB设备描述符和字符串描述符注册到TinyUSB栈中,并设置是否使用外部USB PHY。设备启动后,ESP32-P4作为USB存储设备可以被主机识别和访问。
2,main.c驱动代码(Flash方式)
- static const char *TAG = "example_main";
- sdmmc_card_t *card = NULL;
- /* 挂载名称 */
- #define BASE_PATH "/0:" /* 挂载分区大小的基本路径 */
- /* TinyUSB 描述符 */
- #define EPNUM_MSC 1
- #define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
- enum {
- ITF_NUM_MSC = 0,
- ITF_NUM_TOTAL
- };
- enum {
- EDPT_CTRL_OUT = 0x00,
- EDPT_CTRL_IN = 0x80,
- EDPT_MSC_OUT = 0x01,
- EDPT_MSC_IN = 0x81,
- };
- /**
- * @brief 子分区初始化
- * @param wl_handle:wear levelling handle
- * @retval ESP_OK:初始化成功
- */
- static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle)
- {
- ESP_LOGI(TAG, "Initializing wear levelling");
- const esp_partition_t *data_partition = esp_partition_find_first
- (ESP_PARTITION_TYPE_DATA,
- ESP_PARTITION_SUBTYPE_DATA_SPIFFS,
- "storage");
-
- if (data_partition == NULL)
- {
- return ESP_ERR_NOT_FOUND;
- }
- return wl_mount(data_partition, wl_handle);
- }
- /* USB设备描述符 */
- static tusb_desc_device_t descriptor_config = {
- .bLength = sizeof(descriptor_config), /* 描述符的大小 */
- .bDescriptorType = TUSB_DESC_DEVICE, /* 描述符类型 */
- .bcdUSB = 0x0200, /* BUSB规格发布号 */
- .bDeviceClass = TUSB_CLASS_MISC, /* MSC类 */
- .bDeviceSubClass = MISC_SUBCLASS_COMMON, /* 子类代码(由USB-IF分配) */
- .bDeviceProtocol = MISC_PROTOCOL_IAD, /* 协议代码(由USB-IF分配) */
- .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, /* 端点0的最大数据包大小 */
- .idVendor = 0x303A, /* 供应商ID(由USB-IF分配) */
- .idProduct = 0x4002, /* 产品ID(由制造商指定) */
- .bcdDevice = 0x100, /* 以二进制编码的十进制表示 */
- .iManufacturer = 0x01, /* 描述制造商的字符串描述符的索引 */
- .iProduct = 0x02, /* 描述产品的字符串描述符的索引 */
- .iSerialNumber = 0x03, /* 描述设备序列号的字符串描述符索引 */
- .bNumConfigurations = 0x01 /* 可能的配置数 */
- };
- /* 全速配置描述符 */
- static uint8_t const msc_fs_configuration_desc[] = {
- /* 配置号,接口计数,字符串索引,总长度,属性,功率(mA) */
- TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
- TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
- /* 接口编号,字符串索引,EP Out & EP In地址,EP大小 */
- TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 64),
- };
- #if (TUD_OPT_HIGH_SPEED)
- /* USB设备限定符 */
- static const tusb_desc_device_qualifier_t device_qualifier = {
- .bLength = sizeof(tusb_desc_device_qualifier_t), /* 描述符的大小 */
- .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, /* 设备限定符类型 */
- .bcdUSB = 0x0200, /* USB规格版本号*/
- .bDeviceClass = TUSB_CLASS_MISC, /* 类代码 */
- .bDeviceSubClass = MISC_SUBCLASS_COMMON, /* 子类代码 */
- .bDeviceProtocol = MISC_PROTOCOL_IAD, /* 协议码 */
- .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, /* 最大数据包大小 */
- .bNumConfigurations = 0x01, /* 其他速度配置的个数 */
- .bReserved = 0 /* 保留,必须为零 */
- };
- /* 高速配置描述符 */
- static uint8_t const msc_hs_configuration_desc[] = {
- /* 配置号,接口计数,字符串索引,总长度,属性,功率(mA) */
- TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
- TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
- /* 接口编号,字符串索引,EP Out & EP In地址,EP大小 */
- TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 512),
- };
- #endif
- /* 字符串描述符 */
- static char const *string_desc_arr[] = {
- (const char[]) { 0x09, 0x04 }, /* 0: 支持英文 (0x0409) */
- "TinyUSB", /* 1: 生产商 */
- "TinyUSB Device", /* 2: 产品 */
- "123456", /* 3: 序列 */
- "Example MSC", /* 4. MSC */
- };
- void app_main(void)
- {
- esp_err_t ret;
- ret = nvs_flash_init(); /* 初始化NVS */
- if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
- {
- ESP_ERROR_CHECK(nvs_flash_erase());
- ESP_ERROR_CHECK(nvs_flash_init());
- }
- led_init(); /* LED初始化 */
- lcd_init(); /* LCD屏初始化 */
-
- lcd_show_string(30, 50, 200, 16, 16, "ESP32-P4", RED);
- lcd_show_string(30, 70, 200, 16, 16, "USB Flash TEST", RED);
- lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
- static wl_handle_t wl_handle = WL_INVALID_HANDLE;
- ESP_ERROR_CHECK(storage_init_spiflash(&wl_handle));
- const tinyusb_msc_spiflash_config_t config_spi = {
- .wl_handle = wl_handle,
- .callback_mount_changed = NULL,
- .mount_config.max_files = 5,
- };
- ESP_ERROR_CHECK(tinyusb_msc_storage_init_spiflash(&config_spi));
- ESP_ERROR_CHECK(tinyusb_msc_register_callback
- (TINYUSB_MSC_EVENT_MOUNT_CHANGED, NULL));
- /* 挂载设备 */
- ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH));
- /* 配置USB */
- const tinyusb_config_t tusb_cfg = {
- .device_descriptor = &descriptor_config, /* 设备描述符 */
- .string_descriptor = string_desc_arr, /* 字符串描述符 */
- .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]), /* 字符串描述符大小 */
- .external_phy = false, /* 使用内部USB PHY */
- #if (TUD_OPT_HIGH_SPEED)
- .fs_configuration_descriptor = msc_fs_configuration_desc,
- .hs_configuration_descriptor = msc_hs_configuration_desc,
- .qualifier_descriptor = &device_qualifier,
- #else
- .configuration_descriptor = msc_fs_configuration_desc, /* 配置描述符 */
- #endif
- };
- /* 初始化USB */
- ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
- while(1)
- {
- LED0_TOGGLE();
- vTaskDelay(500);
- }
- }
复制代码 上述代码实现了在ESP32-P4上通过内部Flash模拟USB存储设备(Mass Storage Device)的功能,并利用TinyUSB库完成设备描述符和配置描述符的注册,使设备能与主机通信。为了更好地了解上述代码,这里笔者分为四个部分讲解。
1)Flash中的子分区初始化:
通过storage_init_spiflash函数完成对Flash子分区的初始化,为后续的文件系统挂载和USB存储功能提供支持。
2)TinyUSB设备描述符配置:
定义了USB设备描述符descriptor_config,包括USB协议版本、设备类(MSC类)、厂商ID、产品ID等内容。通过该描述符,设备向主机描述自身的功能和特点。同时,代码根据不同速度(全速/高速)定义了配置描述符(msc_fs_configuration_desc和msc_hs_configuration_desc)以支持不同的USB数据传输模式。
3)文件系统挂载与存储注册:
利用TinyUSB的API函数tinyusb_msc_storage_init_sdmmc注册内部Flash子分区作为USB存储设备。通过tinyusb_msc_storage_mount将内部Flash的分区挂载为文件系统(挂载路径为/0:)。代码还支持注册回调函数,用于监听挂载事件。
4)TinyUSB驱动初始化:
使用tinyusb_driver_install函数完成TinyUSB驱动的安装,将USB设备描述符和字符串描述符注册到TinyUSB栈中,并设置是否使用外部USB PHY。设备启动后,ESP32-P4作为USB存储设备可以被主机识别和访问。
49.4 下载验证
下载程序后,使用USB A对A数据线连接开发板和PC,一端插入开发板上的HOST口,另一端插入PC的USB接口。此时,PC会识别并显示一个新的可操作磁盘(见下图所示)。
1)如果运行的是39_usb_sd_u实验,需插入一张正常工作的SD卡作为存储载体,磁盘内容基于 SD 卡。
2)如果运行的是40_usb_flash_u实验,无需插入SD卡,系统会自动使用开发板的Flash作为存储载体。
图49.4.1 电脑找到USB读卡器的盘符
注意:上图是笔者使用两个实验现象合并再一起的。 |
|