I'm working on a project where I need to interface an ESP32 with an M66 module using I2S for audio playback. I've encountered a problem with audio quality – specifically, distortion – when the ESP32 is set in I2S slave mode using APLL(because quectel m66 can only operate in master mode).
Here’s a brief overview of my setup:
ESP32 Configuration: I2S Slave Mode
Target Device: M66 module (acting as the I2S master)
Application: Audio playback from the ESP32 to the Quectel M66
The i2s driver part of my configuration for the ESP32 is as follows:
Code: Select all
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_SLAVE | I2S_MODE_TX | I2S_MODE_RX), //only I2S_MODE_MASTER was changed to I2S_MODE_SLAVE
.sample_rate = 8000,
.bits_per_sample = (i2s_bits_per_sample_t)16,
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
.communication_format = (i2s_comm_format_t) (I2S_COMM_FORMAT_STAND_I2S),
.intr_alloc_flags = 0, // default interrupt priority
.dma_buf_count = 3,
.dma_buf_len = 300,//64,
.use_apll = true
};
When audio is played back, there's noticeable distortion. The same audio plays fine on a different device that can operate in I2S slave mode, indicating that the issue is likely with the ESP32's configuration or capability in slave mode with APLL.
Questions for the Community:
Has anyone experienced similar issues with I2S slave mode and APLL on the ESP32?
Are there known limitations or configurations with the APLL that might lead to audio distortion?
Any suggestions on buffer configurations or alternative approaches that might resolve this distortion?
I appreciate any insights or advice you can offer. Thanks in advance for your help!
My actual code:
Code: Select all
#include "driver/i2s.h"
#include "driver/uart.h"
#include "audio_data.h"
#define I2S_NUM I2S_NUM_0 // I2S port number
#define I2S_BCK_IO (27) // BCK pin
#define I2S_WS_IO (25) // WS pin
#define I2S_DO_IO (26) // SD_OUT pin
#define I2S_DI_IO (32) // SD_IN pin
#define UART_NUM UART_NUM_1 // UART port number
#define TXD_PIN (17) // UART TX pin
#define RXD_PIN (33) // UART RX pin
#define BUF_SIZE (1024) // UART buffer size
#define UART_BUF_SIZE 1024
#define SAMPLE_RATE 8000
#define TONE_FREQ 5000 // Frequency in Hz (A4 note)
#define DURATION 2 // Duration in seconds
void setup() {
Serial.begin(115200);
//generateTone();
Serial.print("Initializing...");
// Initialize I2S
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_SLAVE | I2S_MODE_TX | I2S_MODE_RX), //only I2S_MODE_MASTER was changed to I2S_MODE_SLAVE
.sample_rate = 8000,
.bits_per_sample = (i2s_bits_per_sample_t)16,
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
.communication_format = (i2s_comm_format_t) (I2S_COMM_FORMAT_STAND_I2S),
.intr_alloc_flags = 0, // default interrupt priority
.dma_buf_count = 3,
.dma_buf_len = 300,//64,
.use_apll = true
};
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_BCK_IO,
.ws_io_num = I2S_WS_IO,
.data_out_num = I2S_DO_IO,
.data_in_num = I2S_DI_IO,
};
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM, &pin_config);
// Initialize UART
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
uart_driver_install(UART_NUM, BUF_SIZE * 2, 0, 0, NULL, 0);
uart_param_config(UART_NUM, &uart_config);
uart_set_pin(UART_NUM, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
//sendSMS(phoneNumber, messageText);
//playAudio(output_raw, output_raw_len);
xTaskCreate(&audio_play_task, "audio_play_task", 4096, NULL, 8, NULL);
}
void playAudio(const unsigned char* data, size_t size) {
i2s_set_clk(I2S_NUM, 8000, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
size_t bytes_written;
for (size_t i = 0; i < size; i++) {
uint16_t sample = data[i];
//sample = sample << 8; // Convert 8-bit to 16-bit if necessary
i2s_write(I2S_NUM, &sample, sizeof(sample), &bytes_written, portMAX_DELAY);
}
}
void loop() {
// Relay data from Serial Monitor to M66
while (Serial.available()) {
size_t len = Serial.available();
uint8_t data[len];
Serial.readBytes(data, len);
uart_write_bytes(UART_NUM, (const char *)data, len);
// Echo the sent data back to Serial Monitor for confirmation
Serial.print("> ");
Serial.write(data, len);
}
// Relay data from M66 to Serial Monitor
uint8_t data[UART_BUF_SIZE];
int length = uart_read_bytes(UART_NUM, data, UART_BUF_SIZE, 20 / portTICK_RATE_MS);
if (length > 0) {
Serial.print("< ");
Serial.write(data, length);
}
// Play beep every second
}
void sendATCommand(const char* cmd) {
uart_write_bytes(UART_NUM, cmd, strlen(cmd));
uart_write_bytes(UART_NUM, "\r\n", 2); // Send newline character
delay(1000); // Wait for command execution
}
void audio_play_task (void *pvParameter)
{
while(1) {
const char *ptr = (const char *)output_raw;
unsigned int offset = 0;
signed short samples[256], i;
size_t written;
// Play array from signed, 16-bit stereo file
size_t bytes_written;
for (size_t i = 0; i < output_raw_len; i++) {
uint16_t sample = output_raw[i];
sample = sample << 8; // Convert 8-bit to 16-bit if necessary
i2s_write(I2S_NUM, &sample, sizeof(sample), &bytes_written, portMAX_DELAY);
}
size_t i2=0;
// Silence here
while (i2<5)
{
for (i=0; i<256; i++)
samples[i] = 0;
i2s_write (I2S_NUM, (const char*) samples, 512, &written, portMAX_DELAY);
i2++;
}
}
}