Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions components/usb/usb_device_uac/include/usb_device_uac.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ typedef esp_err_t (*uac_output_cb_t)(uint8_t *buf, size_t len, void *cb_ctx);
typedef esp_err_t (*uac_input_cb_t)(uint8_t *buf, size_t len, size_t *bytes_read, void *cb_ctx);
typedef void (*uac_set_mute_cb_t)(uint32_t mute, void *cb_ctx);
typedef void (*uac_set_volume_cb_t)(uint32_t volume, void *cb_ctx);
typedef void (*uac_set_volume_db_cb_t)(int32_t volume_db, void *cb_ctx);


/**
* @brief USB UAC Device Config
Expand All @@ -29,6 +31,7 @@ typedef struct {
uac_input_cb_t input_cb; /*!< callback function for UAC data input, if NULL, input will be disabled */
uac_set_mute_cb_t set_mute_cb; /*!< callback function for set mute, if NULL, the set mute request will be ignored */
uac_set_volume_cb_t set_volume_cb; /*!< callback function for set volume, if NULL, the set volume request will be ignored */
uac_set_volume_db_cb_t set_volume_db_cb; /*!< callback function for set volume in dB, if NULL, the set volume request will be ignored */
void *cb_ctx; /*!< callback context, for user specific usage */
#if CONFIG_USB_DEVICE_UAC_AS_PART
int spk_itf_num; /*!< If CONFIG_USB_DEVICE_UAC_AS_PART is enabled, you need to provide the speaker interface number */
Expand Down
5 changes: 4 additions & 1 deletion components/usb/usb_device_uac/usb_device_uac.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req
if (s_uac_device->user_cfg.set_mute_cb) {
s_uac_device->user_cfg.set_mute_cb(s_uac_device->mute[request->bChannelNumber], s_uac_device->user_cfg.cb_ctx);
}

return true;
} else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t));
Expand All @@ -259,6 +258,9 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req
if (s_uac_device->user_cfg.set_volume_cb) {
s_uac_device->user_cfg.set_volume_cb(volume, s_uac_device->user_cfg.cb_ctx);
}
if (s_uac_device->user_cfg.set_volume_db_cb) {
s_uac_device->user_cfg.set_volume_db_cb(volume_db, s_uac_device->user_cfg.cb_ctx);
}
return true;
} else {
TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n",
Expand Down Expand Up @@ -501,6 +503,7 @@ esp_err_t uac_device_init(uac_device_config_t *config)
s_uac_device->user_cfg.cb_ctx = config->cb_ctx;
s_uac_device->user_cfg.set_mute_cb = config->set_mute_cb;
s_uac_device->user_cfg.set_volume_cb = config->set_volume_cb;
s_uac_device->user_cfg.set_volume_db_cb = config->set_volume_db_cb;
s_uac_device->current_sample_rate = DEFAULT_SAMPLE_RATE;
s_uac_device->mic_buf_write = s_uac_device->mic_buf1;
s_uac_device->mic_buf_read = s_uac_device->mic_buf2;
Expand Down
9 changes: 5 additions & 4 deletions docs/en/usb/usb_device/usb_device_uac.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ USB Device UAC API Reference
.. code:: c

uac_device_config_t config = {
.output_cb = uac_device_output_cb, // Speaker output callback
.input_cb = uac_device_input_cb, // Microphone input callback
.set_mute_cb = uac_device_set_mute_cb, // Set mute callback
.set_volume_cb = uac_device_set_volume_cb, // Set volume callback
.output_cb = uac_device_output_cb, // Speaker output callback
.input_cb = uac_device_input_cb, // Microphone input callback
.set_mute_cb = uac_device_set_mute_cb, // Set mute callback
.set_volume_cb = uac_device_set_volume_cb, // Set volume callback
.set_volume_db_cb = uac_device_set_volume_db_cb, // Set volume in dB callback
.cb_ctx = NULL,
};
uac_device_init(&config);
Expand Down
9 changes: 5 additions & 4 deletions docs/zh_CN/usb/usb_device/usb_device_uac.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ USB Device UAC API 参考
.. code:: c

uac_device_config_t config = {
.output_cb = uac_device_output_cb, // Speaker output callback
.input_cb = uac_device_input_cb, // Microphone input callback
.set_mute_cb = uac_device_set_mute_cb, // Set mute callback
.set_volume_cb = uac_device_set_volume_cb, // Set volume callback
.output_cb = uac_device_output_cb, // Speaker output callback
.input_cb = uac_device_input_cb, // Microphone input callback
.set_mute_cb = uac_device_set_mute_cb, // Set mute callback
.set_volume_cb = uac_device_set_volume_cb, // Set volume callback
.set_volume_db_cb = uac_device_set_volume_db_cb, // Set volume in dB callback
.cb_ctx = NULL,
};
uac_device_init(&config);
Expand Down
10 changes: 10 additions & 0 deletions examples/usb/device/usb_uac_i2s/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)

# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

add_compile_options(-Wno-ignored-qualifiers)

project(usb_uac_i2s)
52 changes: 52 additions & 0 deletions examples/usb/device/usb_uac_i2s/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## USB UAC I2S Example

This example demonstrates how to utilize the USB function of ESP32-Sx to implement a UAC (USB Audio Class) device with an I2S PDM microphone and I2S PCM amplifier

1. Supports 2 channels of analog microphone output.
2. Supports 2 channels of speaker input.
3. Supports volume control and mute function.

For information about the [usb_device_uac](https://docs.espressif.com/projects/esp-iot-solution/zh_CN/latest/usb/usb_device/usb_device_uac.html) component.

## How to build the example

### Hardware Required

For ESP32-S3

* An ESP32-S3 development board
* At least one USB Type-C cable for Power supply, programming and USB communication.
* An I2S PDM microphone
* An I2S PCM amplifier

### UAC Device Configuration

1. Using `idf.py menuconfig`, through `USB Device UAC` users can configure the num of SPK and MIC channels.

Note: This example supports one channel of microphone input.

### Example Output

After the programming is completed, insert the USB interface on the development board into the computer. An audio device will be displayed. Select this device for recording or for playback.

### Build and Flash

1. Make sure `ESP-IDF` is setup successfully

2. Set up the `ESP-IDF` environment variables, please refer [Set up the environment variables](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#step-4-set-up-the-environment-variables), Linux can use:

```bash
. $HOME/esp/esp-idf/export.sh
```

3. Set ESP-IDF build target to `esp32s3` or `esp32-p4`

```bash
idf.py set-target esp32s3
```

4. Build, Flash, output log

```bash
idf.py build flash monitor
```
2 changes: 2 additions & 0 deletions examples/usb/device/usb_uac_i2s/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS ".")
61 changes: 61 additions & 0 deletions examples/usb/device/usb_uac_i2s/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
menu "Example Configuration"

menu "Speaker I2S Pin Configuration"

config SPEAKER_I2S_DOUT
int "Speaker I2S Data Out Pin"
default 13
help
GPIO pin for I2S speaker data out (DOUT).

config SPEAKER_I2S_BCLK
int "Speaker I2S Bit Clock Pin"
default 14
help
GPIO pin for I2S speaker bit clock (BCLK).

config SPEAKER_I2S_LRC
int "Speaker I2S Left/Right Clock Pin"
default 21
help
GPIO pin for I2S speaker word select/left-right clock (LRC/WS).

config SPEAKER_SD_MODE_ENABLE
bool "Enable Speaker Shutdown/Mode Control Pin"
default y
help
Enable control of speaker amplifier shutdown/mode pin.
Disable this if your amplifier doesn't have a shutdown control pin.

config SPEAKER_SD_MODE
int "Speaker Shutdown/Mode Control Pin"
default 12
depends on SPEAKER_SD_MODE_ENABLE
help
GPIO pin for speaker amplifier shutdown/mode control.

endmenu

menu "Microphone I2S Pin Configuration"

config MIC_I2S_CLK
int "Microphone I2S Clock Pin"
default 9
help
GPIO pin for microphone PDM clock.

config MIC_I2S_LR
int "Microphone I2S L/R Pin"
default 10
help
GPIO pin for microphone L/R select.

config MIC_I2S_DATA
int "Microphone I2S Data Pin"
default 11
help
GPIO pin for microphone PDM data input.

endmenu

endmenu
9 changes: 9 additions & 0 deletions examples/usb/device/usb_uac_i2s/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## IDF Component Manager Manifest File
version: "0.2.0"
targets:
- esp32s3
dependencies:
idf: ">=5.1"
usb_device_uac:
version: "1.*"
override_path: "../../../../../components/usb/usb_device_uac"
175 changes: 175 additions & 0 deletions examples/usb/device/usb_uac_i2s/main/usb_uac_i2s_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include "driver/gpio.h"
#include "driver/i2s_pdm.h"
#include "driver/i2s_std.h"
#include "driver/ledc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
#include "usb_device_uac.h"
#include <math.h>

#define SPEAKER_I2S_DOUT CONFIG_SPEAKER_I2S_DOUT
#define SPEAKER_I2S_BCLK CONFIG_SPEAKER_I2S_BCLK
#define SPEAKER_I2S_LRC CONFIG_SPEAKER_I2S_LRC
#ifdef CONFIG_SPEAKER_SD_MODE_ENABLE
#define SPEAKER_SD_MODE CONFIG_SPEAKER_SD_MODE
#endif

#define MIC_I2S_CLK CONFIG_MIC_I2S_CLK
#define MIC_I2S_LR CONFIG_MIC_I2S_LR
#define MIC_I2S_DATA CONFIG_MIC_I2S_DATA

static i2s_chan_handle_t rx;
static i2s_chan_handle_t tx;

static bool is_muted = false;
/* default to 0dB (full volume) */
static uint32_t gain = 100;

static esp_err_t usb_uac_device_output_cb(uint8_t *buf, size_t len, void *arg) {
if (!tx) {
return ESP_FAIL;
}
/* process the samples */
int16_t *samples = (int16_t *)buf;
for (size_t i = 0; i < len / 2; i++) {
/* if muted - set the sample to 0 so no sound is played */
if (is_muted) {
samples[i] = 0;
continue;
}
/* apply the gain */
int32_t sample = samples[i];
sample = (sample * gain) / 100;
/* clip the sample to the range of -32768 to 32767 (16 bits) */
if (sample > 32767) {
sample = 32767;
} else if (sample < -32768) {
sample = -32768;
}
samples[i] = (int16_t)sample;
}
/* write the samples to the I2S TX channel */
size_t total_bytes_written = 0;
while (total_bytes_written < len) {
size_t bytes_written = 0;
i2s_channel_write(tx, (uint8_t *)buf + total_bytes_written,
len - total_bytes_written, &bytes_written, portMAX_DELAY);
total_bytes_written += bytes_written;
}
if (total_bytes_written < len) {
return ESP_FAIL;
}
return ESP_OK;
}

static esp_err_t usb_uac_device_input_cb(uint8_t *buf, size_t len,
size_t *bytes_read, void *arg) {
if (!rx) {
return ESP_FAIL;
}
/* read directly from the I2S RX channel */
return i2s_channel_read(rx, buf, len, bytes_read, portMAX_DELAY);
}

static void usb_uac_device_set_mute_cb(uint32_t mute, void *arg) {
is_muted = mute;
}

static void usb_uac_device_set_volume_cb_db(int32_t volume_db, void *arg) {
/* convert from dB to a percentage */
gain = pow(10, volume_db / 20.0f) * 100.0f;
}

static void usb_uac_device_init(void) {
uac_device_config_t config = {
.output_cb = usb_uac_device_output_cb,
.input_cb = usb_uac_device_input_cb,
.set_mute_cb = usb_uac_device_set_mute_cb,
.set_volume_db_cb = usb_uac_device_set_volume_cb_db,
.cb_ctx = NULL,
};
/* Init UAC device, UAC related configurations can be set by the menuconfig */
ESP_ERROR_CHECK(uac_device_init(&config));
}

void init_pdm_rx(void) {
i2s_chan_config_t chan_cfg =
I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
i2s_new_channel(&chan_cfg, NULL, &rx);

i2s_pdm_rx_config_t pdm_cfg = {
.clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(CONFIG_UAC_SAMPLE_RATE),
.slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_MONO),
.gpio_cfg =
{
.clk = MIC_I2S_CLK, // PDM clock
// QUESTION - what about the LR clock pin? No longer relevant? Do
// we tie it high or low?
.din = MIC_I2S_DATA, // PDM data
.invert_flags = {.clk_inv = false},
},
};
pdm_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_MONO; // single mic

i2s_channel_init_pdm_rx_mode(rx, &pdm_cfg);
i2s_channel_enable(rx);
}

static void init_pcm_tx(void) {
i2s_chan_config_t chan_cfg =
I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx, NULL));

i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(CONFIG_UAC_SAMPLE_RATE),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_MONO),
.gpio_cfg =
{
.mclk = I2S_GPIO_UNUSED, // set this if your amp needs MCLK
.bclk = SPEAKER_I2S_BCLK,
.ws = SPEAKER_I2S_LRC,
.dout = SPEAKER_I2S_DOUT,
.din = I2S_GPIO_UNUSED,
.invert_flags =
{
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false, // if L/R are swapped or silent, try true
},
},
};

ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(tx));
}

void app_main(void) {
/* set the LR pin to low */
gpio_reset_pin(MIC_I2S_LR);
gpio_set_direction(MIC_I2S_LR, GPIO_MODE_OUTPUT);
gpio_set_level(MIC_I2S_LR, 0);

#ifdef CONFIG_SPEAKER_SD_MODE_ENABLE
// enable the amplifier
gpio_reset_pin(SPEAKER_SD_MODE);
gpio_set_direction(SPEAKER_SD_MODE, GPIO_MODE_OUTPUT);
gpio_set_level(SPEAKER_SD_MODE, 1);
#endif

// init the I2S peripherals
init_pdm_rx();
init_pcm_tx();

// init the USB audio device
usb_uac_device_init();

// Nothing to do here - the USB audio device will take care of everything
}
5 changes: 5 additions & 0 deletions examples/usb/device/usb_uac_i2s/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
#
CONFIG_FREERTOS_HZ=1000
CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE=n
Loading