Имеется необходимость измерения спектральных параметров звука с нескольких микрофонов установленных на закрытом канале. В первую очередь интересно замерить задержку во времени прохождения звуковой волны между двумя и более микрофонами с высокой точностью. При этом измерение необходимо осуществлять достаточно локально, то есть микрофон должен иметь малые размеры приёмника по сравнению с длиной волны. Диапазон частот 100..2000 Гц (длины волн при нормальных условиях около 3-0.15м). В качестве микрофонов могут быть использованы следующие микрофоны: Электронные микрофоны малого диаметра (∅6-10 мм, соотношение диаметра и длины волны в худшем случае 15), пример EM6050; Микрофоны на технологии MEMS, (∅1 мм, соотношение диаметра и длины волны в худшем случае 150), пример SMD02718C4O4C-38VSM2718AB-N30-B3F; Цифровые микрофоны на технологии MEMS со в строенным усилителем и АЦП, пример im69d130. Пьезоэлектрические датчики (Пьезоэлектрические излучатели) (∅26мм, соотношение диаметра и длины волны в худшем случае 5,7), пример Спойлер: фото Микрофонам, естественно, нужна обвязка 1 и 2 могут потребоваться отдельные усилители, а 3 достаточно лишь нескольких конденсаторов в обвязке для подключения к микроконтроллеру сбора информации. В связи с этим возникает вопрос выбора подходящего микрофона. Подключение электретного микрофона к МК Согласно https://qna.habr.com/q/669863, для схемы на рисунке 1 требуется резистор 2 кОм для 3,3в и конденсатор неполярный ёмкостью более 1 мкф. Спойлер: Рисунок 1 Если методики и рекомендации по расчёту номинала резистора и конденсатора? Микроконтроллер. Были испытаны возможности двух разных микроконтроллеров без задействования всего потенциала за счёт использования оптимизация обращения к регистрам. Atmega2560 в составе Arduino mega (16 МГц); STM32F401RE в составе STM NUCLEO (84 МГц); Arduino mega, среда Arduino IDE, соответствующим набором библиотек, частота дискретизации составила 8927 Гц. Спойлер: Код для теста: Код (C++): #define LEN_SMPL 3000 int* sample = new int[LEN_SMPL]; int* sample2 = new int[LEN_SMPL]; int n = 0, sm = 0; unsigned long t1 = 0, t2 = 0; void setup() { // put your setup code here, to run once: Serial.begin(2000000); ///921600 analogReference(INTERNAL1V1); } void loop() { t1 = micros(); for (n = 0; n < LEN_SMPL; n++) { sample[n] = analogRead(A0); sample2[n] = analogRead(A1); } t2 = micros(); Serial.println("Time read " + String(t2 - t1)); t1 = micros(); /* for (n = 0; n < LEN_SMPL; n++) { Serial.println(analogRead(A0)); } t2 = micros(); Serial.println( String(t2 - t1)); //Serial.println("Time rw " + String(t2 - t1)); //delay(10000); // for (n = 0; n < LEN_SMPL; n++) { // sm = analogRead(A0); // Serial.write(sm, sizeof( sm )); // } // t2 = micros(); // Serial.println("Time rwrite" + String(t2 - t1)); // Serial.println("sizeof" + String(sizeof( sm ))); // delay(10000); */ t1 = micros(); for (n = 0; n < LEN_SMPL; n++) { Serial.print(sample[n]); Serial.print(sample2[n]); } t2 = micros(); Serial.println("\nTime write " + String(t2 - t1)); // put your main code here, to run repeatedly: } STM32F401RE, среда Mbed, библиотека Mbed v6.17, частота дискретизации составила 82370 Гц. Спойлер: Код для теста: Код (C++): /* mbed Microcontroller Library * Copyright (c) 2019 ARM Limited * SPDX-License-Identifier: Apache-2.0 */ #include "mbed.h" #include "mbed_mem_trace.h" #include <string> #include <cstdint> #include <cstdio> #include <cstdlib> #include <cstring> #defineLEN_SMPL10000 // Blinking rate in milliseconds #defineBLINKING_RATE5000ms #definemicrosus_timestamp_t Timer timer; AnalogIn ain0(A0); AnalogIn ain1(A1); AnalogIn vrefint(ADC_VREF); AnalogIn tempint(ADC_TEMP); BufferedSerial pc(USBTX, USBRX,115200); uint64_t t1 = 0, t2 = 0; uint16_t* sample0 = new uint16_t[LEN_SMPL]; uint16_t* sample1 = new uint16_t[LEN_SMPL]; int n = 0, sm = 0; Kernel::Clock::time_point start_time,end_time; int main() { mbed_stats_heap_t heap_stats; CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // DWT->LAR = 0xC5ACCE55; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; printf("Hello, World!\n"); printf("Vref(f): %f, Vref : %u, Temperature : %u\r\n", vrefint.read(), vrefint.read_u16(), tempint.read_u16()); // Initialise the digital pin LED1 as an output DigitalOut led(LED1); while (true) { uint32_t start_cycles = DWT->CYCCNT; start_time = Kernel::Clock::now(); for (n = 0; n < LEN_SMPL; n++) { sample0[n] = ain0.read_u16(); sample1[n] = ain1.read_u16(); } end_time = Kernel::Clock::now(); uint32_t end_cycles = DWT->CYCCNT; auto elapsed_time = end_time - start_time; ThisThread::sleep_for(100ms); printf("Time read: %lld milliseconds\n", elapsed_time.count()); // Вычисляем время выполнения в тактах uint32_t elapsed_cycles = end_cycles - start_cycles; printf("Time read: %lu CPU cycles\n", elapsed_cycles); // Если нужно перевести такты в микросекунды: uint32_t cpu_frequency = SystemCoreClock; // Частота процессора в Гц float elapsed_time_us = (float)elapsed_cycles / (cpu_frequency / 1000000.0f); printf("Time read: %f microseconds\n", elapsed_time_us); printf("cpu_frequency %lu \n", cpu_frequency); mbed_stats_heap_get(&heap_stats); printf("Heap size: %lu / %lu bytes max %lu \r\n", heap_stats.current_size, heap_stats.reserved_size, heap_stats.max_size); t1 = micros(); led = !led; //ThisThread::sleep_for(BLINKING_RATE); start_time = Kernel::Clock::now(); for (n = 0; n < LEN_SMPL; n++) { printf("%d %d\n",sample0[n],sample1[n]); } end_time = Kernel::Clock::now(); elapsed_time = end_time - start_time; printf("\nTime write: %lld microseconds\n", elapsed_time.count()); } } У данной реализации считывателя сигнала с микрофонов есть два ограничения, расширение которых является перспективным. Увеличение частоты дискретизации, за счёт оптимизации считывания сигнала; Уменьшение времени вывода считанных цифровых значений в com port, либо за счёт повышения скорости передачи у серийного порта, либо за счёт отказа от кодирования чисел в ASCII. Повышение скорости передачи у серийного порта пробовал, но выше 115200, У STM появляются потери данных. Сигналы с разных микрофонов, уже пробовал получать и обрабатывать в Matlab, кому нужно могу выложить код скрипта. Спектры в векторном формате emf для лучшего качества сохранил в pdf. По итогу есть ряд вопросов: Какой микрофон лучше использовать для имеющейся задачи? Если методики и рекомендации по расчёту номинала резистора и конденсатора при подключении электретного микрофона? Как увеличить частоту дискретизации и стоит ли? Как увеличить скорость передачи данных на ПК? Здесь также будут выкладываться новые результаты по мере их появления.
Спектр получить? Фазу или что? В электретных микрофона с питанием это все уже есть Использовать быстрый дельта-сигма АЦП. BT, USB
Удалось добиться увеличения скорости передачи данных за счёт отказа от буферизации при отправке данных в serial port. Спойлер: Код Код (C++): #include "mbed.h" #include "mbed_mem_trace.h" #include <string> #include <cstdint> #include <cstdio> #include <cstdlib> #include <cstring> #define LEN_SMPL 10000 // Blinking rate in milliseconds #define BLINKING_RATE 5000ms #define micros us_timestamp_t Timer timer; AnalogIn ain0(A0); AnalogIn ain1(A1); AnalogIn ain2(A2); AnalogIn ain3(A3); AnalogIn vrefint(ADC_VREF); AnalogIn tempint(ADC_TEMP); //BufferedSerial pc(USBTX, USBRX,115200); UnbufferedSerial pc(USBTX, USBRX,460800); // 2000000 uint64_t t1 = 0, t2 = 0; uint16_t* sample0 = new uint16_t[LEN_SMPL]; uint16_t* sample1 = new uint16_t[LEN_SMPL]; uint16_t* sample2 = new uint16_t[LEN_SMPL]; uint16_t* sample3 = new uint16_t[LEN_SMPL]; int n = 0, sm = 0; Kernel::Clock::time_point start_time,end_time; int main() { mbed_stats_heap_t heap_stats; CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // DWT->LAR = 0xC5ACCE55; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; printf("Hello, World!\n"); printf("Vref(f): %f, Vref : %u, Temperature : %u\r\n", vrefint.read(), vrefint.read_u16(), tempint.read_u16()); // Initialise the digital pin LED1 as an output DigitalOut led(LED1); while (true) { uint32_t start_cycles = DWT->CYCCNT; start_time = Kernel::Clock::now(); for (n = 0; n < LEN_SMPL; n++) { sample0[n] = ain0.read_u16(); sample1[n] = ain1.read_u16(); sample2[n] = ain2.read_u16(); sample3[n] = ain3.read_u16(); } end_time = Kernel::Clock::now(); uint32_t end_cycles = DWT->CYCCNT; auto elapsed_time = end_time - start_time; ThisThread::sleep_for(100ms); printf("Time read: %lld milliseconds\n", elapsed_time.count()); // Вычисляем время выполнения в тактах uint32_t elapsed_cycles = end_cycles - start_cycles; printf("Time read: %lu CPU cycles\n", elapsed_cycles); // Если нужно перевести такты в микросекунды: uint32_t cpu_frequency = SystemCoreClock; // Частота процессора в Гц float elapsed_time_us = (float)elapsed_cycles / (cpu_frequency / 1000000.0f); printf("Time read: %f microseconds\n", elapsed_time_us); printf("cpu_frequency %lu \n", cpu_frequency); mbed_stats_heap_get(&heap_stats); printf("Heap size: %lu / %lu bytes max %lu \r\n", heap_stats.current_size, heap_stats.reserved_size, heap_stats.max_size); t1 = micros(); led = !led; //ThisThread::sleep_for(BLINKING_RATE); start_time = Kernel::Clock::now(); for (n = 0; n < LEN_SMPL; n++) { printf("%d %d %d %d\n",sample0[n],sample1[n],sample2[n],sample3[n]); } end_time = Kernel::Clock::now(); elapsed_time = end_time - start_time; printf("\nTime write: %lld microseconds\n", elapsed_time.count()); } } Также было обнаружено, что при имеющейся конфигурации ПК Matlab может получать данные без потерь до скорости 460800 бод/с (менее 0,5 Мбит/с). Увеличил количество считываемых каналов до 4. Сравнение сигналов с пьезодатчиков показало наличие разности фаз между ними. Пьезодатчики были заключены в корпуса, как показано на рисунке 1. Спойлер: Рисунок 1 Корпус такой конструкции по задумке должен был уменьшить размеры приёмника давления по сравнению с длиной волны. Для испытаний использовался удалённый на расстояние более 3 длин волн динамик, на котором воспроизводился синусоидальный сигнал постоянной частоты. Обработка сигнала в Matlab показала, что на частоте воспроизводимый динамиком 432 Гц, наблюдалась разность фаз между двумя одинаковыми пьезодатчиками подключёнными во второй и третий канал соответственно. Теоретически разность фаз на возбуждаемый частоте между датчиками должна была быть равна фазовой задержке между измерениями, однако такого не происходит. О фазовой задержке между измерениями Пусть дискретизация АЦП fadc, имеется n каналов, тогда фазовая задержка между i-ым и j-ым каналами dfi (f)=(1/fadc)*(i-j)*2*pi*f При частоте f adc = 82370 Гц, n=4 канала, I=3, j=2, f= 432 Гц: 0,033 радиан. Имеющиеся разность 0,263 радиан. Что может быть не так?
Провёл испытания с трубой длиной 1 м, датчики были присоединены к трубе на одном и том же расстоянии от концов трубы в 0,5м, как показано на рисунке. Динамик располагался на одном из входов трубы. Замеры и обработка показали всё тоже наличие разности фаз между 2 и 3 каналами к которым были подключены датчики: Посторонних шумов меньше в трубе, да и звук от динамика слабее гаснет соотношение сигнал/шум уже радует.
То есть высчитанные разности фаз между ближайшими по очереди каналами ровно в 8 раз больше теоретических. Это вдвое больше чем число каналов. Тогда не морочьте голову, задайте только ДВА канала и мерьте заново.