OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 25|回复: 0

《ESP32-P4开发指南— V1.0》第四十九章 USB读卡器(Slave)实验

[复制链接]

1262

主题

1276

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5418
金钱
5418
注册时间
2019-5-8
在线时间
1404 小时
发表于 3 小时前 | 显示全部楼层 |阅读模式
第四十九章 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


2.jpg

3.png

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组件提供的功能,首先需要在代码中导入以下头文件:

  1. #include "tinyusb.h"
  2. #include "tusb_msc_storage.h"
复制代码
接下来,作者将介绍本章节实验用到的esp_tinyusb函数,这些函数的描述及其作用如下:
1,在TinyUSB驱动中注册SD卡存储类型tinyusb_msc_storage_init_sdmmc
该函数用于在TinyUSB驱动中注册SD卡设备,其函数原型如下:

  1. esp_err_t tinyusb_msc_storage_init_sdmmc(
  2. const tinyusb_msc_sdmmc_config_t*config);
复制代码
函数形参:

1.png
表49.3.1.1 tinyusb_msc_storage_init_sdmmc函数形参描述

返回值:
ESP_OK表示注册成功。
ESP_ERR_NO_MEM表示分配内存失败。
config为指向配置SDMMC初始化的结构体。接下来,笔者将详细介绍tinyusb_msc_sdmmc_config_t结构体中的各个成员变量,如下代码所示:

  1. typedef struct {
  2. sdmmc_card_t *card;        /* 指向SDMMC卡配置结构体的指针 */
  3. /* 指向挂载/卸载操作完成后回调函数的指针 */
  4. tusb_msc_callback_t callback_mount_changed;
  5. /* 指向挂载/卸载操作开始前回调函数的指针 */
  6.     tusb_msc_callback_t callback_premount_changed;
  7.     const esp_vfs_fat_mount_config_t mount_config;        /* FATFS挂载配置 */
  8. } 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事件回调函数,其函数原型如下:

  1. esp_err_t tinyusb_msc_register_callback(tinyusb_msc_event_type_t event_type,
  2.                                         tusb_msc_callback_t callback);
复制代码
函数形参:

2.png
表49.3.1.2 tinyusb_msc_register_callback函数形参描述

返回值:
ESP_OK表示注册成功。
ESP_ERR_INVALID_ARG表示参数无效。
3,将存储设备挂载到应用程序固件tinyusb_msc_storage_mount
该函数用于将存储设备挂载到应用程序固件,其函数原型如下:

  1. esp_err_t tinyusb_msc_storage_mount(const char *base_path);
复制代码
函数形参:

3.png
表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设备驱动,其函数原型如下:

  1. esp_err_t tinyusb_driver_install(const tinyusb_config_t *config);
复制代码
函数形参:

4.png
表49.3.1.4 esp_err_t tinyusb_driver_install函数形参描述

返回值:
ESP_OK表示驱动程序和TinyUSB栈成功安装。
ESP_ERR_INVALID_ARG表示参数无效导致驱动程序和TinyUSB栈安装失败。
ESP_FAIL表示内部错误导致驱动程序和TinyUSB栈安装失败。

49.3.2 程序流程图

第四十九章 USB读卡器3449.png
图49.3.2.1 USB读卡器实验程序流程图

49.3.3 程序解析
本实验旨在通过调用ESP-TinyUSB组件的API函数,实现USB读卡器功能。实验的代码已经编写在main.c文件中,并提供了两种实现方式:
1,main.c驱动代码(SD卡方式)

  1. sdmmc_card_t *card = NULL;
  2. /* 挂载名称 */
  3. #define BASE_PATH "/0:" /* 挂载分区大小的基本路径 */
  4. /* TinyUSB 描述符 */
  5. #define EPNUM_MSC       1
  6. #define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)

  7. enum {
  8.     ITF_NUM_MSC = 0,
  9.     ITF_NUM_TOTAL
  10. };

  11. enum {
  12.     EDPT_CTRL_OUT = 0x00,
  13.     EDPT_CTRL_IN  = 0x80,

  14.     EDPT_MSC_OUT  = 0x01,
  15.     EDPT_MSC_IN   = 0x81,
  16. };

  17. /**
  18. * @brief             sdmmc
  19. * [url=home.php?mod=space&uid=271674]@param[/url]             无
  20. * @retval           ESP_OK:初始化成功
  21. */
  22. esp_err_t sdmmc_init(void)
  23. {
  24.     /* 使用默认配置SDMMC */
  25.     sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
  26.     /* 传输宽度为4 */
  27.     slot_config.width = 4;
  28.     /* 根据原理图配置管脚 */
  29.     slot_config.d0 = GPIO_NUM_39;
  30.     slot_config.d1 = GPIO_NUM_40;
  31.     slot_config.d2 = GPIO_NUM_41;
  32.     slot_config.d3 = GPIO_NUM_42;
  33.     slot_config.clk = GPIO_NUM_43;
  34.     slot_config.cmd = GPIO_NUM_44;
  35.     /* 配置内部上拉 */
  36.     slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
  37.     card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));
  38.     /* 配置默认 */
  39.     sdmmc_host_t host = SDMMC_HOST_DEFAULT();
  40.     host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;     /* 设置速度最大值为40MHz */
  41.     (*host.init)();

  42.     sdmmc_host_init_slot(host.slot, (const sdmmc_slot_config_t *) &slot_config);

  43.     while (sdmmc_card_init(&host, card))
  44.     {
  45.         vTaskDelay(pdMS_TO_TICKS(3000));
  46.     }

  47.     sdmmc_card_print_info(stdout, card);
  48.     return ESP_OK;
  49. }

  50. /* USB设备描述符 */
  51. static tusb_desc_device_t descriptor_config = {
  52.     .bLength = sizeof(descriptor_config),       /* 描述符的大小 */
  53.     .bDescriptorType = TUSB_DESC_DEVICE,        /* 描述符类型 */
  54.     .bcdUSB = 0x0200,                           /* BUSB规格发布号 */
  55.     .bDeviceClass = TUSB_CLASS_MISC,            /* MSC类 */
  56.     .bDeviceSubClass = MISC_SUBCLASS_COMMON,    /* 子类代码(由USB-IF分配) */
  57.     .bDeviceProtocol = MISC_PROTOCOL_IAD,       /* 协议代码(由USB-IF分配) */
  58.     .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,  /* 端点0的最大数据包大小*/
  59.     .idVendor = 0x303A,                         /* 供应商ID(由USB-IF分配) */
  60.     .idProduct = 0x4002,                        /* 产品ID(由制造商指定) */
  61.     .bcdDevice = 0x100,                         /* 以二进制编码的十进制表示 */
  62.     .iManufacturer = 0x01,                      /* 描述制造商的字符串描述符的索引 */
  63.     .iProduct = 0x02,                           /* 描述产品的字符串描述符的索引 */
  64.     .iSerialNumber = 0x03,                      /* 描述设备序列号的字符串描述符索引 */
  65.     .bNumConfigurations = 0x01                  /* 可能的配置数 */
  66. };

  67. /* 全速配置描述符 */
  68. static uint8_t const msc_fs_configuration_desc[] = {
  69.     /* 配置号,接口计数,字符串索引,总长度,属性,功率(mA) */
  70. TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
  71. TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),

  72.     /* 接口编号,字符串索引,EP Out & EP In地址,EP大小 */
  73.     TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 64),
  74. };

  75. #if (TUD_OPT_HIGH_SPEED)
  76. /* USB设备限定符 */
  77. static const tusb_desc_device_qualifier_t device_qualifier = {
  78.     .bLength = sizeof(tusb_desc_device_qualifier_t),    /* 描述符的大小 */
  79.     .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,      /* 设备限定符类型 */
  80.     .bcdUSB = 0x0200,         /* USB规格版本号(例如,V2.00为0200H) */
  81.     .bDeviceClass = TUSB_CLASS_MISC,                    /* 类代码 */
  82.     .bDeviceSubClass = MISC_SUBCLASS_COMMON,            /* 子类代码 */
  83.     .bDeviceProtocol = MISC_PROTOCOL_IAD,               /* 协议码 */
  84.     .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,          /* 最大数据包大小 */
  85.     .bNumConfigurations = 0x01,                         /* 其他速度配置的个数 */
  86.     .bReserved = 0                                      /* 保留,必须为零 */
  87. };
  88. /* 高速配置描述符 */
  89. static uint8_t const msc_hs_configuration_desc[] = {
  90.     /* 配置号,接口计数,字符串索引,总长度,属性,功率(mA) */
  91. TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
  92. TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),

  93.     /* 接口编号,字符串索引,EP Out & EP In地址,EP大小 */
  94.     TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 512),
  95. };
  96. #endif

  97. /* 字符串描述符 */
  98. static char const *string_desc_arr[] = {
  99.     (const char[]) { 0x09, 0x04 },  /* 0: 支持英文 (0x0409) */
  100.     "TinyUSB",                      /* 1: 生产商 */
  101.     "TinyUSB Device",               /* 2: 产品 */
  102.     "123456",                       /* 3: 序列 */
  103.     "Example MSC",                  /* 4. MSC */
  104. };

  105. void app_main(void)
  106. {
  107.     esp_err_t ret;
  108.    
  109.     ret = nvs_flash_init();     /* 初始化NVS */
  110.     if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  111.     {
  112.         ESP_ERROR_CHECK(nvs_flash_erase());
  113.         ESP_ERROR_CHECK(nvs_flash_init());
  114.     }

  115.     led_init();         /* LED初始化 */
  116.     lcd_init();         /* LCD屏初始化 */
  117.     /* 显示实验信息 */
  118.     lcd_show_string(30, 50, 200, 16, 16, "ESP32-P3", RED);
  119.     lcd_show_string(30, 70, 200, 16, 16, "USB SD TEST", RED);
  120.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);

  121.     ESP_ERROR_CHECK(sdmmc_init());
  122.     /* 配置SDMMC */
  123.     const tinyusb_msc_sdmmc_config_t config_sdmmc = {
  124.         .card = card,                   /* 指向sdmmc卡配置结构的指针 */
  125.         .callback_mount_changed = NULL, /* 注册回调函数,用来初始化子分区表 */
  126.         .mount_config.max_files = 5,    /* 最大文件打开数量 */
  127.     };
  128.     /* 注册存储类型sd卡与tinyusb驱动程序 */
  129.     ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc));
  130.     /* 注册回调的其他方法,即使用单独的API注册 */
  131.     ESP_ERROR_CHECK(tinyusb_msc_register_callback
  132. (TINYUSB_MSC_EVENT_MOUNT_CHANGED, NULL));
  133.     /* 挂载设备 */
  134.     ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH));
  135.     /* 配置USB */
  136.     const tinyusb_config_t tusb_cfg = {
  137.         .device_descriptor = &descriptor_config,    /* 设备描述符 */
  138.         .string_descriptor = string_desc_arr,       /* 字符串描述符 */
  139.         .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]),    /* 字符串描述符大小 */
  140.         .external_phy = false,                      /* 使用内部USB PHY */
  141. #if (TUD_OPT_HIGH_SPEED)
  142.         .fs_configuration_descriptor = msc_fs_configuration_desc,
  143.         .hs_configuration_descriptor = msc_hs_configuration_desc,
  144.         .qualifier_descriptor = &device_qualifier,
  145. #else
  146.         .configuration_descriptor = msc_fs_configuration_desc,         /* 配置描述符 */
  147. #endif
  148.     };
  149.     /* 初始化USB */
  150.     ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));

  151.     while(1)
  152.     {
  153.         LED0_TOGGLE();
  154.         vTaskDelay(500);
  155.     }
  156. }
复制代码
上述代码实现了在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方式)

  1. static const char *TAG = "example_main";
  2. sdmmc_card_t *card = NULL;
  3. /* 挂载名称 */
  4. #define BASE_PATH "/0:" /* 挂载分区大小的基本路径 */
  5. /* TinyUSB 描述符 */
  6. #define EPNUM_MSC       1
  7. #define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)

  8. enum {
  9.     ITF_NUM_MSC = 0,
  10.     ITF_NUM_TOTAL
  11. };

  12. enum {
  13.     EDPT_CTRL_OUT = 0x00,
  14.     EDPT_CTRL_IN  = 0x80,

  15.     EDPT_MSC_OUT  = 0x01,
  16.     EDPT_MSC_IN   = 0x81,
  17. };

  18. /**
  19. * @brief           子分区初始化
  20. * @param            wl_handle:wear levelling handle
  21. * @retval            ESP_OK:初始化成功
  22. */
  23. static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle)
  24. {
  25.     ESP_LOGI(TAG, "Initializing wear levelling");

  26. const esp_partition_t *data_partition = esp_partition_find_first
  27. (ESP_PARTITION_TYPE_DATA,
  28. ESP_PARTITION_SUBTYPE_DATA_SPIFFS,
  29. "storage");
  30.    
  31.     if (data_partition == NULL)
  32.     {
  33.         return ESP_ERR_NOT_FOUND;
  34.     }

  35.     return wl_mount(data_partition, wl_handle);
  36. }

  37. /* USB设备描述符 */
  38. static tusb_desc_device_t descriptor_config = {
  39.     .bLength = sizeof(descriptor_config),       /* 描述符的大小 */
  40.     .bDescriptorType = TUSB_DESC_DEVICE,        /* 描述符类型 */
  41.     .bcdUSB = 0x0200,                           /* BUSB规格发布号 */
  42.     .bDeviceClass = TUSB_CLASS_MISC,            /* MSC类 */
  43.     .bDeviceSubClass = MISC_SUBCLASS_COMMON,    /* 子类代码(由USB-IF分配) */
  44.     .bDeviceProtocol = MISC_PROTOCOL_IAD,       /* 协议代码(由USB-IF分配) */
  45.     .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,  /* 端点0的最大数据包大小 */
  46.     .idVendor = 0x303A,                         /* 供应商ID(由USB-IF分配) */
  47.     .idProduct = 0x4002,                        /* 产品ID(由制造商指定) */
  48.     .bcdDevice = 0x100,                         /* 以二进制编码的十进制表示 */
  49.     .iManufacturer = 0x01,                      /* 描述制造商的字符串描述符的索引 */
  50.     .iProduct = 0x02,                           /* 描述产品的字符串描述符的索引 */
  51.     .iSerialNumber = 0x03,                      /* 描述设备序列号的字符串描述符索引 */
  52.     .bNumConfigurations = 0x01                  /* 可能的配置数 */
  53. };

  54. /* 全速配置描述符 */
  55. static uint8_t const msc_fs_configuration_desc[] = {
  56.     /* 配置号,接口计数,字符串索引,总长度,属性,功率(mA) */
  57. TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
  58. TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),

  59.     /* 接口编号,字符串索引,EP Out & EP In地址,EP大小 */
  60.     TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 64),
  61. };

  62. #if (TUD_OPT_HIGH_SPEED)
  63. /* USB设备限定符 */
  64. static const tusb_desc_device_qualifier_t device_qualifier = {
  65.     .bLength = sizeof(tusb_desc_device_qualifier_t),    /* 描述符的大小 */
  66.     .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,      /* 设备限定符类型 */
  67.     .bcdUSB = 0x0200,                                   /* USB规格版本号*/
  68.     .bDeviceClass = TUSB_CLASS_MISC,                    /* 类代码 */
  69.     .bDeviceSubClass = MISC_SUBCLASS_COMMON,            /* 子类代码 */
  70.     .bDeviceProtocol = MISC_PROTOCOL_IAD,               /* 协议码 */
  71.     .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,          /* 最大数据包大小 */
  72.     .bNumConfigurations = 0x01,                         /* 其他速度配置的个数 */
  73.     .bReserved = 0                                      /* 保留,必须为零 */
  74. };
  75. /* 高速配置描述符 */
  76. static uint8_t const msc_hs_configuration_desc[] = {
  77.     /* 配置号,接口计数,字符串索引,总长度,属性,功率(mA) */
  78. TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
  79. TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),

  80.     /* 接口编号,字符串索引,EP Out & EP In地址,EP大小 */
  81.     TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 512),
  82. };
  83. #endif

  84. /* 字符串描述符 */
  85. static char const *string_desc_arr[] = {
  86.     (const char[]) { 0x09, 0x04 },  /* 0: 支持英文 (0x0409) */
  87.     "TinyUSB",                      /* 1: 生产商 */
  88.     "TinyUSB Device",               /* 2: 产品 */
  89.     "123456",                       /* 3: 序列 */
  90.     "Example MSC",                  /* 4. MSC */
  91. };

  92. void app_main(void)
  93. {
  94.     esp_err_t ret;
  95.     ret = nvs_flash_init();          /* 初始化NVS */
  96.     if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  97.     {
  98.         ESP_ERROR_CHECK(nvs_flash_erase());
  99.         ESP_ERROR_CHECK(nvs_flash_init());
  100.     }
  101.     led_init();                             /* LED初始化 */
  102.     lcd_init();                             /* LCD屏初始化 */
  103.    
  104.     lcd_show_string(30, 50, 200, 16, 16, "ESP32-P4", RED);
  105.     lcd_show_string(30, 70, 200, 16, 16, "USB Flash TEST", RED);
  106.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  107.     static wl_handle_t wl_handle = WL_INVALID_HANDLE;
  108.     ESP_ERROR_CHECK(storage_init_spiflash(&wl_handle));
  109.     const tinyusb_msc_spiflash_config_t config_spi = {
  110.         .wl_handle = wl_handle,
  111.         .callback_mount_changed = NULL,
  112.         .mount_config.max_files = 5,
  113.     };
  114.     ESP_ERROR_CHECK(tinyusb_msc_storage_init_spiflash(&config_spi));
  115.     ESP_ERROR_CHECK(tinyusb_msc_register_callback
  116. (TINYUSB_MSC_EVENT_MOUNT_CHANGED, NULL));
  117.     /* 挂载设备 */
  118.     ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH));
  119.     /* 配置USB */
  120.     const tinyusb_config_t tusb_cfg = {
  121.         .device_descriptor = &descriptor_config,    /* 设备描述符 */
  122.         .string_descriptor = string_desc_arr,       /* 字符串描述符 */
  123.         .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]),    /* 字符串描述符大小 */
  124.         .external_phy = false,                      /* 使用内部USB PHY */
  125. #if (TUD_OPT_HIGH_SPEED)
  126.         .fs_configuration_descriptor = msc_fs_configuration_desc,
  127.         .hs_configuration_descriptor = msc_hs_configuration_desc,
  128.         .qualifier_descriptor = &device_qualifier,
  129. #else
  130.         .configuration_descriptor = msc_fs_configuration_desc,   /* 配置描述符 */
  131. #endif
  132.     };
  133.     /* 初始化USB */
  134.     ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));

  135.     while(1)
  136.     {
  137.         LED0_TOGGLE();
  138.         vTaskDelay(500);
  139.     }
  140. }
复制代码
上述代码实现了在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作为存储载体。


第四十九章 USB读卡器16678.png
图49.4.1 电脑找到USB读卡器的盘符

注意:上图是笔者使用两个实验现象合并再一起的。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-2-13 12:59

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表