Увеличить частоту дискретизации на плате ESP32
Для моего последнего проекта в университете в качестве системного инженера я работаю над «электронным стетоскопом», который доставляет мне много головной боли.
Я купил Adafruit Feather ESP32 Huzzah (https://learn.adafruit.com/adafruit-huzzah32). -esp32-feather/overview), который имеет двухъядерный процессор, работающий на частоте около 240 МГц, поддерживает беспроводную связь с классическим Bluetooth (который я использую), а также работает с I2S. протокол.
Вот почему я купил микрофон I2S MEMS SPH0645 (https://www.adafruit.com/product/3421), который согласно его документации работает в диапазоне частот 50 Гц-15 кГц.
В качестве первого подхода я подумал, что этого будет достаточно, но у меня очень низкая частота дискретизации, около 800 выборок в секунду. С этими значениями я не могу воспроизвести звук с ожидаемым качеством.
С другой стороны, поскольку ESP32 не имеет высокоуровневой библиотеки для взаимодействия с I2S, такой как ArduinoSound, мне пришлось создать собственный код на основе спецификации (https://esp-idf.readthedocs.io/en/v2.0/api/peripherals/i2s.html).
Мой код в Arduino IDE выглядит так:
#include "libraries/SmappioSound/SmappioSound.cpp"
#include "BluetoothSerial.h"
int *buffer;
BluetoothSerial serialBT;
int value = 0;
int framesRead = 0;
SmappioSound smappioSound(6835); //значение для стабилизации
void setup() {
// поместите сюда код установки для однократного запуска:
pinMode(DATA_PIN, INPUT); // Supuestamente necesario para que no haya ruido
serialBT.begin("smappio_PCM");
Serial.begin(115200);
while (!Serial) {
; // ждем подключения последовательного порта. Требуется только для родного порта USB
}
smappioSound.begin(buffer);
}
void loop() {
//получает количество байтов, прочитанных из буфера.
framesRead = smappioSound.read() / I2S_BITS_PER_SAMPLE_32BIT;
for(int index = 0; index <= framesRead; index++)
{
value = smappioSound.getSampleValue(index);
if(index % 2 == 0)
{
serialBT.print(String(value) + ",");
Serial.println(value);
}
}
}
В библиотеке SmappioSound.h происходит взаимодействие с I2S. Конфигурация для i2S следующая:
#pragma region // Раздел конфигурации
static const print_mode_t PRINT_MODE = BYTES; // Тип впечатления: BITS | БАЙТЫ | ЦЕЛОЕ | ПОЛНОЕ_ДОПОЛНЕНИЕ
static const bool PRINT_BOTH_CHANNELS = false; // Indica si imprime solo el "Canal 0" ó el "Canal 0" y el "Canal 1"
static const int FRAMES_REQUESTED = 1; // Relacionarse Parece relacionarse con la cantidad de buffers
static const int TICKS_TO_WAIT = 0; // Исследователь
static const i2s_bits_per_sample_t BITS_PER_SAMPLE = I2S_BITS_PER_SAMPLE_32BIT; // Технический паспорт: 24. Пример: 32.
static const i2s_config_t SPH_CONFIG = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX), // контроллер является ведущим и принимает, микрофон является подчиненным и передает
.sample_rate = 32000, // между 32 кГц и 64 кГц (техническое описание)
.bits_per_sample = BITS_PER_SAMPLE,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // Формат, который использует микрофон. Даташит не найден
.communication_format = (i2s_comm_format_t) I2S_COMM_FORMAT_PCM, //(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB) // Исследуем
//MSB: старший байт -> БОЛЬШОЙ ENDIAN
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // Высокий приоритет прерывания (исследование)
.dma_buf_count = 2, // Количество буферов, 128 макс.
.dma_buf_len = 32 * 2 // Буфер таманьо де када
};
#pragma endregion Sección de Configuración
Как видите, я пытаюсь получить 32 кГц в качестве частоты дискретизации с глубиной 32 бита (не работает для 16 и 24 бит), и я Я устанавливаю скорость 115200 бод в Arduino IDE.
С другой стороны, я создал приложение .NET Winforms, которое получает сгенерированные значения PCM через Bluetooth 4.0. Через 1 секунду я получаю только 693 образца, что очень мало.
Могу ли я достичь частоты дискретизации не менее 20 кГц с этими аппаратными компонентами? Вы видите что-то не так в моем коде?
@Mauro Bilotti, 👍1
Обсуждение1 ответ
Лучший ответ:
Вот несколько советов по оптимизации.
for(int index = 0; index <= framesRead; index++)
{
value = smappioSound.getSampleValue(index);
if(index % 2 == 0)
{
serialBT.print(String(value) + ",");
Serial.println(value);
}
}
Выполнение
serialBT.print(String(value) + ",");
Serial.println(value);
очень расточительно. Во-первых, вы отлаживаете вывод каждого значения в виде строки ASCII в Serial
, во-вторых, вы делаете то же самое в serialBT
с дополнительным ,
.
Это расточительно, потому что:
- ваша скорость передачи очень низкая (115200 на
Serial
) - преобразование каждого значения в ASCII вызовет накладные расходы
Ваше значение int
составляет 32 бита / 4 байта, которые могут принимать произвольные значения. Скажем, максимальное положительное значение равно 0x7ffffffff
= 214748364. Тогда передача строки ASCII займет 9 байт (10 байт для наибольшего отрицательного значения), отправка необработанного двоичного значения всегда взять 4 байта. Таким образом, вы всегда должны отправлять образцы в необработанном двоичном формате, а не в виде String
ASCII.
При требуемой частоте дискретизации 32 кГц с 32-битными значениями вам потребуется эффективная скорость передачи данных 32 000 * 32 = 1 024 000 бит/с. Ваш Serial
, на котором вы также печатаете каждое второе значение, имеет скорость 115 200 бит/с (и вам все равно придется учитывать накладные расходы UART, то есть стартовые и стоповые биты для каждых 8 битов данных! ).
В зависимости от качества преобразователя USB-UART на вашей плате (FTDI, CP2102: хорошее, CH340: плохое), вы можете получить скорость более 1 мегабода. Периферийный интерфейс UART ESP32 позволит вам установить любой делитель тактовой частоты, на который вы хотите разделить тактовую частоту 80 МГц APB ( источник, глава 13.3.2). В ESP-IDF для UART_BITRATE_MAX
определено значение 5 000 000 (источник), а поскольку Arduino-ESP32 является производным от ESP-IDF, это тоже должно быть возможно.
Как я уже сказал, адаптер USB-UART между ESP32 и вашим ПК имеет решающее значение. Он должен поддерживать такие высокие скорости с достаточно большим буфером TX/RX. Максимальная скорость передачи данных CP2101 составляет 921 600 бод (источник), FT232RL ( источник) до 3 мегабод.
Как правило, вы не хотите передавать свои образцы значений периферийным устройствам. Как ваш Serial
, так и ваш BluetoothSerial
могут поставить в очередь сразу весь буфер (источник). Я не знаю реальной базовой реализации, в которой будут храниться окончательные образцы (и оставаться стабильными некоторое время), но вы можете просто сделать
framesRead = smappioSound.read() / I2S_BITS_PER_SAMPLE_32BIT;
serialBT.write((uint8_t*) buffer, framesRead * sizeof(int32_t));
для отправки всего буфера сразу. Вам также нужно будет проверить, как обрабатывать или устанавливать скорость передачи на периферийном устройстве Bluetooth. Я мало что знаю о BT/BLE-радио ESP32, чтобы ответить на этот вопрос.
Скорость адаптера UART может быть увеличена до неблокирующего варианта с поддержкой DMA. Поскольку и I2S, и периферийное устройство UART поддерживают передачу DMA. Таким образом, будет достигнута максимальная скорость передачи.
Другой возможностью может быть изменение среды передачи. Вы можете подключить свой ESP32 к сети Wi-Fi и передавать свои аудиобуферы через UDP или TCP на какой-либо сервер, прослушивающий ПК. Однако я думаю, что Bluetooth и UART (с правильными настройками) также возможны.
Большое спасибо, Максимилиан, я проверю все детали вашего ответа и дам вам знать, как все пойдет., @Mauro Bilotti
Аппаратное обеспечение CH340 может передавать данные со скоростью до 3 Мбод (к сожалению, с 6 стоповыми битами). Для получения дополнительной информации, пожалуйста, прочитайте мой HOWTO по расчету скорости передачи данных CH341 на https://github.com/nospam2000/ch341-baudrate-calculation., @Michael Dreher
- esp32 Stack canary watchpoint срабатывает
- Adafruit esp32 Feather не удалось скомпилировать
- Использование лямбд в качестве функций обратного вызова
- Преобразование строки C в строку C++
- Считывание данных нескольких датчиков из текстового файла, расположенного на SD-карте в Arduino/ESP32
- Не могу скомпилировать .ino с помощью библиотеки ArduinoJson
- Тот же код работает на Arduino IDE, но не в PlatformIO IDE
- ESP32-WROOM-32 Wifisecureclient read() приводит к тарабарщине данных
Сериал медленный. Очень медленно. Вы не можете сделать звук поверх него вот так., @Majenko
Спасибо Маженко. Какой аппаратный компонент вы можете порекомендовать для потоковой передачи звука с высоким качеством?, @Mauro Bilotti
Usb с изохронной передачей., @Majenko
Я не понимаю. Используете ли вы Bluetooth или последовательный порт USB-UART в качестве среды передачи? Потому что вы передаете значения по обоим каналам., @Maximilian Gerhardt
Я хочу передать только 1 канал. Я только что увидел, что я могу использовать channel_format = I2S_CHANNEL_FMT_ONLY_LEFT для отправки только моно. Я использую Bluetooth в качестве среды передачи., @Mauro Bilotti