Увеличить частоту дискретизации на плате 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 кГц с этими аппаратными компонентами? Вы видите что-то не так в моем коде?

, 👍1

Обсуждение

Сериал медленный. Очень медленно. Вы не можете сделать звук поверх него вот так., @Majenko

Спасибо Маженко. Какой аппаратный компонент вы можете порекомендовать для потоковой передачи звука с высоким качеством?, @Mauro Bilotti

Usb с изохронной передачей., @Majenko

Я не понимаю. Используете ли вы Bluetooth или последовательный порт USB-UART в качестве среды передачи? Потому что вы передаете значения по обоим каналам., @Maximilian Gerhardt

Я хочу передать только 1 канал. Я только что увидел, что я могу использовать channel_format = I2S_CHANNEL_FMT_ONLY_LEFT для отправки только моно. Я использую Bluetooth в качестве среды передачи., @Mauro Bilotti


1 ответ


Лучший ответ:

3

Вот несколько советов по оптимизации.

  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