Add usb_msc.

This commit is contained in:
Colin 2025-12-25 00:46:03 +08:00
parent 51b1edb9e6
commit 138552e4eb
7 changed files with 450 additions and 4 deletions

18
.vscode/settings.json vendored
View File

@ -17,6 +17,22 @@
"unordered_map": "c",
"esp_netif_sntp.h": "c",
"sdmmc_cmd.h": "c",
"app_ui.h": "c"
"app_ui.h": "c",
"tinyusb.h": "c",
"tusb_msc_storage.h": "c",
"bitset": "c",
"map": "c",
"set": "c",
"tinyusb_default_config.h": "c",
"tinyusb_msc.h": "c",
"sd_pwr_ctrl_by_on_chip_ldo.h": "c",
"diskio_sdmmc.h": "c",
"__split_buffer": "c",
"__tree": "c",
"array": "c",
"initializer_list": "c",
"list": "c",
"queue": "c",
"stack": "c"
}
}

View File

@ -1,2 +1,2 @@
idf_component_register(SRCS "app_sr.c" "esp32_s3_szp.c" "main.c" "app_ui.c"
idf_component_register(SRCS "app_sr.c" "esp32_s3_szp.c" "main.c" "app_ui.c" "usb_msc.c"
INCLUDE_DIRS ".")

View File

@ -1,6 +1,8 @@
#include "app_sr.h"
#include <errno.h>
#include "app_ui.h"
#include "audio_player.h"
#include "esp32_s3_szp.h"
@ -42,11 +44,13 @@ esp_err_t record_start() {
ESP_LOGI(TAG, "Opening file %s", "/sdcard/aaaaa.wav");
f1 = fopen("/sdcard/aaaaa.wav", "w");
ESP_LOGI(TAG, "fopen error: %s", strerror(errno));
ESP_RETURN_ON_FALSE(f1, ESP_FAIL, TAG, "error while opening wav file");
ESP_RETURN_ON_FALSE(fwrite(&wav_header1, sizeof(wav_header_t), 1, f1),
ESP_FAIL, TAG, "error while writing wav header");
f2 = fopen("/sdcard/bbbbb.wav", "w");
ESP_LOGI(TAG, "fopen error: %s", strerror(errno));
ESP_RETURN_ON_FALSE(f2, ESP_FAIL, TAG, "error while opening wav file");
ESP_RETURN_ON_FALSE(fwrite(&wav_header2, sizeof(wav_header_t), 1, f2),
ESP_FAIL, TAG, "error while writing wav header");
@ -312,4 +316,7 @@ void app_sr_init(void) {
NULL, 1);
xTaskCreatePinnedToCore(&feed_Task, "feed", 8 * 1024, (void*)afe_data, 5,
NULL, 0);
while (wav_written2 != 0xFFFFFFFF || wav_written1 != 0xFFFFFFFF) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}

View File

@ -1,5 +1,7 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp_tinyusb:
version: "^2.0.0"
espressif/es7210: "^1.0.0"
# espressif/esp32-camera: "^2.0.10" # 摄像头驱动
lvgl/lvgl: "~8.3.0"

View File

@ -95,7 +95,7 @@ sdmmc_card_t* mount_sdcard(void) {
ESP_LOGI(TAG, "Mounting SD card");
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 2,
.max_files = 5,
.allocation_unit_size = 8 * 1024};
ESP_LOGI(TAG, "Initializing SD card");
@ -295,8 +295,9 @@ err:
return ret;
}
void init_msc(sdmmc_card_t* card);
void app_main(void) {
// Initialize NVS.
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
@ -315,6 +316,7 @@ void app_main(void) {
// mp3_player_init(); // MP3播放器初始化
app_sr_init(); // 语音识别初始化
init_msc(sdmmc_card);
// i2s_chan_handle_t i2s_rx_chan = es7210_i2s_init();
// es7210_codec_init();

416
main/usb_msc.c Normal file
View File

@ -0,0 +1,416 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* DESCRIPTION:
* This example contains code to make ESP32 based device recognizable by
* USB-hosts as a USB Mass Storage Device. It either allows the embedded
* application i.e. example to access the partition or Host PC accesses the
* partition over USB MSC. They can't be allowed to access the partition at the
* same time. For different scenarios and behaviour, Refer to README of this
* example.
*/
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#include "driver/gpio.h"
#include "esp_check.h"
#include "esp_console.h"
#include "esp_partition.h"
#include "sdkconfig.h"
#include "tinyusb.h"
#include "tinyusb_default_config.h"
#include "tinyusb_msc.h"
/*
* We warn if a secondary serial console is enabled. A secondary serial console
* is always output-only and hence not very useful for interactive console
* applications. If you encounter this warning, consider disabling the secondary
* serial console in menuconfig unless you know what you are doing.
*/
#if SOC_USB_SERIAL_JTAG_SUPPORTED
#if !CONFIG_ESP_CONSOLE_SECONDARY_NONE
#warning \
"A secondary serial console is not useful when using the console component. Please disable it in menuconfig."
#endif
#endif
static const char* TAG = "msc";
static esp_console_repl_t* repl = NULL;
/* Storage global variables */
tinyusb_msc_storage_handle_t storage_hdl = NULL;
tinyusb_msc_mount_point_t mp;
static SemaphoreHandle_t _wait_console_smp = NULL;
/* TinyUSB descriptors
********************************************************************* */
#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,
};
static tusb_desc_device_t descriptor_config = {
.bLength = sizeof(descriptor_config),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x303A, // This is Espressif VID. This needs to be changed
// according to Users / Customers
.idProduct = 0x4002,
.bcdDevice = 0x100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01};
static uint8_t const msc_fs_configuration_desc[] = {
// Config number, interface count, string index, total length, attribute,
// power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 64),
};
#if (TUD_OPT_HIGH_SPEED)
static const tusb_desc_device_qualifier_t device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = 0x0200,
.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[] = {
// Config number, interface count, string index, total length, attribute,
// power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 512),
};
#endif // TUD_OPT_HIGH_SPEED
static char const* string_desc_arr[] = {
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456", // 3: Serials
"Example MSC", // 4. MSC
};
/*********************************************************************** TinyUSB
* descriptors*/
#define BASE_PATH "/sdcard" // base path to mount the partition
#define PROMPT_STR CONFIG_IDF_TARGET
static int console_unmount(int argc, char** argv);
static int console_read(int argc, char** argv);
static int console_write(int argc, char** argv);
static int console_size(int argc, char** argv);
static int console_status(int argc, char** argv);
static int console_exit(int argc, char** argv);
const esp_console_cmd_t cmds[] = {
{
.command = "read",
.help = "read BASE_PATH/README.MD and print its contents",
.hint = NULL,
.func = &console_read,
},
{
.command = "write",
.help = "create file BASE_PATH/README.MD if it does not exist",
.hint = NULL,
.func = &console_write,
},
{
.command = "size",
.help = "show storage size and sector size",
.hint = NULL,
.func = &console_size,
},
{
.command = "expose",
.help = "Expose Storage to Host",
.hint = NULL,
.func = &console_unmount,
},
{
.command = "status",
.help = "Status of storage exposure over USB",
.hint = NULL,
.func = &console_status,
},
{
.command = "exit",
.help = "exit from application",
.hint = NULL,
.func = &console_exit,
}};
// Set mount point to the application and list files in BASE_PATH by filesystem
// API
static void mount(void) {
ESP_LOGI(TAG, "Mount storage...");
ESP_ERROR_CHECK(tinyusb_msc_set_storage_mount_point(
storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_APP));
// List all the files in this directory
ESP_LOGI(TAG, "\nls command output:");
struct dirent* d;
DIR* dh = opendir(BASE_PATH);
if (!dh) {
if (errno == ENOENT) {
// If the directory is not found
ESP_LOGE(TAG, "Directory doesn't exist %s", BASE_PATH);
} else {
// If the directory is not readable then throw error and exit
ESP_LOGE(TAG, "Unable to read directory %s", BASE_PATH);
}
return;
}
// While the next entry is not readable we will print directory files
while ((d = readdir(dh)) != NULL) {
printf("%s\n", d->d_name);
}
return;
}
// unmount storage
static int console_unmount(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG, "Storage is already exposed");
return -1;
}
ESP_LOGI(TAG, "Unmount storage...");
ESP_ERROR_CHECK(tinyusb_msc_set_storage_mount_point(
storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_USB));
return 0;
}
// read BASE_PATH/README.MD and print its contents
static int console_read(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG,
"Storage exposed over USB. Application can't read from storage.");
return -1;
}
ESP_LOGD(TAG, "read from storage:");
const char* filename = BASE_PATH "/README.MD";
FILE* ptr = fopen(filename, "r");
if (ptr == NULL) {
ESP_LOGE(TAG, "Filename not present - %s", filename);
return -1;
}
char buf[1024];
while (fgets(buf, 1000, ptr) != NULL) {
printf("%s", buf);
}
fclose(ptr);
return 0;
}
// create file BASE_PATH/README.MD if it does not exist
static int console_write(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG,
"storage exposed over USB. Application can't write to storage.");
return -1;
}
ESP_LOGD(TAG, "write to storage:");
const char* filename = BASE_PATH "/README.MD";
FILE* fd = fopen(filename, "r");
if (!fd) {
ESP_LOGW(TAG, "README.MD doesn't exist yet, creating");
fd = fopen(filename, "w");
fprintf(fd,
"Mass Storage Devices are one of the most common USB devices. It "
"use Mass Storage Class (MSC) that allow access to their internal "
"data storage.\n");
fprintf(fd,
"In this example, ESP chip will be recognised by host (PC) as Mass "
"Storage Device.\n");
fprintf(fd,
"Upon connection to USB host (PC), the example application will "
"initialize the storage module and then the storage will be seen "
"as removable device on PC.\n");
fclose(fd);
}
return 0;
}
// Show storage size and sector size
static int console_size(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG, "storage exposed over USB. Application can't access storage");
return -1;
}
uint32_t sec_count;
uint32_t sec_size;
ESP_ERROR_CHECK(tinyusb_msc_get_storage_sector_size(storage_hdl, &sec_size));
ESP_ERROR_CHECK(tinyusb_msc_get_storage_capacity(storage_hdl, &sec_count));
// Calculate size in MB or KB
uint64_t total_bytes = (uint64_t)sec_size * sec_count;
if (total_bytes >= (1024 * 1024)) {
uint64_t total_mb = total_bytes / (1024 * 1024);
printf("Storage Capacity %lluMB\n", total_mb);
} else {
uint64_t total_kb = total_bytes / 1024;
printf("Storage Capacity %lluKB\n", total_kb);
}
return 0;
}
// Show storage status
static int console_status(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
printf("storage exposed over USB: %s\n",
(mp == TINYUSB_MSC_STORAGE_MOUNT_USB) ? "Yes" : "No");
return 0;
}
// Exit from application
static int console_exit(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_delete_storage(storage_hdl));
ESP_ERROR_CHECK(tinyusb_driver_uninstall());
xSemaphoreGive(_wait_console_smp);
printf("Application Exit\n");
return 0;
}
/**
* @brief Storage mount changed callback
*
* @param handle Storage handle
* @param event Event information
* @param arg User argument, provided during callback registration
*/
static void storage_mount_changed_cb(tinyusb_msc_storage_handle_t handle,
tinyusb_msc_event_t* event, void* arg) {
switch (event->id) {
case TINYUSB_MSC_EVENT_MOUNT_START:
// Verify that all the files are closed before unmounting
break;
case TINYUSB_MSC_EVENT_MOUNT_COMPLETE:
ESP_LOGI(
TAG, "Storage mounted to application: %s",
(event->mount_point == TINYUSB_MSC_STORAGE_MOUNT_APP) ? "Yes" : "No");
break;
case TINYUSB_MSC_EVENT_MOUNT_FAILED:
case TINYUSB_MSC_EVENT_FORMAT_REQUIRED:
ESP_LOGE(TAG, "Storage mount failed or format required");
break;
default:
break;
}
}
void init_msc(sdmmc_card_t* card) {
ESP_LOGI(TAG, "Initializing storage...");
_wait_console_smp = xSemaphoreCreateBinary();
if (_wait_console_smp == NULL) {
ESP_LOGE(TAG, "Failed to create semaphore");
return;
}
// Initial mount point to APP
tinyusb_msc_storage_config_t storage_cfg = {
.mount_point = TINYUSB_MSC_STORAGE_MOUNT_USB,
.fat_fs =
{
.base_path = NULL, // Use default base path
.config.max_files = 5, // Maximum number of files that can be
// opened simultaneously
.format_flags = 0, // No special format flags
},
};
storage_cfg.medium.card = card;
ESP_ERROR_CHECK(tinyusb_msc_new_storage_sdmmc(&storage_cfg, &storage_hdl));
ESP_ERROR_CHECK(
tinyusb_msc_set_storage_callback(storage_mount_changed_cb, NULL));
// Re-mount to the APP
mount();
ESP_LOGI(TAG, "USB MSC initialization");
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
tusb_cfg.descriptor.device = &descriptor_config;
tusb_cfg.descriptor.full_speed_config = msc_fs_configuration_desc;
tusb_cfg.descriptor.string = string_desc_arr;
tusb_cfg.descriptor.string_count =
sizeof(string_desc_arr) / sizeof(string_desc_arr[0]);
#if (TUD_OPT_HIGH_SPEED)
tusb_cfg.descriptor.high_speed_config = msc_hs_configuration_desc;
tusb_cfg.descriptor.qualifier = &device_qualifier;
#endif // TUD_OPT_HIGH_SPEED
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
ESP_LOGI(TAG, "USB MSC initialization DONE");
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG, "storage exposed over USB. Application can't access.");
}
#if 1
// Init console based on menuconfig settings
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
repl_config.prompt = PROMPT_STR ">";
repl_config.max_cmdline_length = 64;
esp_console_dev_uart_config_t hw_config =
ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
for (int count = 0; count < sizeof(cmds) / sizeof(esp_console_cmd_t);
count++) {
ESP_ERROR_CHECK(esp_console_cmd_register(&cmds[count]));
}
ESP_ERROR_CHECK(esp_console_start_repl(repl));
xSemaphoreTake(_wait_console_smp, portMAX_DELAY);
ESP_ERROR_CHECK(esp_console_stop_repl(repl));
vSemaphoreDelete(_wait_console_smp);
#endif
}

View File

@ -1,6 +1,9 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
#
CONFIG_TINYUSB_MSC_ENABLED=y
CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_FATFS_LFN_HEAP=y