Arduino + микрофон

В данной статье рассматривается частный случай подключения микрофона к Arduino. Всё написанное также верно и для клонов Arduino, например Freeduino.

В статье встречаются технические термины, для понимания которых могут быть не бесполезны статьи Использование прерываний Arduino, Таймеры-счётчики Arduino и Как подключить к Arduino...

Подключение микрофона к Arduino.

Надо заметить, что подключить к Arduino конденсаторный (электретный) микрофон - это задача, которая требует навыка работы с операционными усилителями, поскольку сам по себе выходной сигнал от микрофона довольно слабый, и поэтому прежде, чем подать сигнал с микрофона на АЦП (аналого-цифровой преобразователь), его желательно предварительно усилить, и заодно немножко почистить от шума в цепях питания.

Дело в том, что в схеме Arduino питание цифровой и аналоговой цепей микроконтроллера производится по упрощённой схеме, без LC-фильтра между ними. И соответственно, всё, что происходит в цифровой части схемы, попадает в аналоговый сигнал, оцифровываемый с помощью АЦП.

Поэтому, подключая микрофон, я включил в цепь питания микрофона и операционного усилителя дроссель L1, что позволило заметно снизить количество шумов в сигнале.

В качестве схемы для подключения микрофона я взял за основу схему подключения микрофона из Application Note AVR335: Digital Sound Recorder with AVR and DataFlash. Микрофон подключал так называемый компьютерный, то есть со стерео-джеком 3.5-мм.

Саму схему я путём экспериментов полностью изменил, поскольку микросхема усилителя TL071, приводимая в Application Note, у меня от +5 Вольт работать не захотела, а в даташите я так и не смог найти минимальное напряжение, которое ей требуется. Поэтому использовал в качестве усилителя тот низковольтовый операционник, который имелся в запасе, а им оказался KA2209, который меня в своё время привлёк своей ценой и тем, что он требует однополярного напряжения. Но вообще-то это усилитель мощности и даже не аудио. Хотя с питанием от дросселя он меня даже удивил тем, что не шумит (без дросселя шумел довольно заметно). Так что я счёл его использование в данной схеме вполне приемлемым.

Для того, чтобы откалибровать схему, я подал на вход вместо сигнала от микрофона напряжение +5 Вольт, используя для отображения простой скетч, отображающий состояние АЦП на текстовом терминале, совместимом с VT100:

void setup()

{

Serial.begin(230400);

Serial.print("\x1B[?25l"); // Выключение курсора

}

void loop()

{

unsigned char input = (analogRead(A0) >> 6); // Чтение состояния входа Analog In 0 со сдвигом значения в диапазон [0..15]

Serial.print("\x1B[0;0H|"); // Курсор в позицию 0, 0

for (unsigned char n = 0; n < 16; ++n) { // Изображение шкалы

Serial.print((n == input) ? '+' : '-');

}

Serial.print('|');

}

Скачать скетч.

В linux после успешной компиляции и загрузки скетча в память микроконтроллера, можно вызвать эмулятор VT100-терминала командой

screen /dev/ttyUSB0 230400

где /dev/ttyUSB0 - это путь к USB-устройству Arduino. Выход из screen производится нажатием Ctrl+A, затем Ctrl+\ и ответом y на запрос подтверждения выхода. Либо отключением Arduino от USB.

Калибровку удобно производить, снимая сигнал с ползунка потенциометра. После этого можно подать на вход схемы сигнал от микрофона и посмотреть, насколько результат совпадает с ожидаемым.

В схеме присутствуют два потенциометра, подключенные к входам Analog In 1 и 2. В скетче они используются для регулировки фильтра и уровня реверберации соответственно.

Фильтрация сигнала требуется по той причине, что регулировка уровня реверберации приводит к тому, что при достаточно больших уровнях громкости усилителя и реверберации происходит самовозбуждение системы. Без регулировки (операции умножения 8-битного текущего значения в буфере на 7-битное значение уровня реверберации) этого не происходит. С фильтром же у меня микрофон не "заводится" даже, если поднести его вплотную к динамику.

Скетч захватывает сигнал с входа АЦП, затем сигнал обрабатывается обрезным фильтром ВЧ четвёртого порядка, на него накладывается эффект реверберации, затем сигнал выводится на вывод широтно-импульсного модулятора (ШИМ) Digital 11, к которому подключен 8-Омный динамик с усилителем класса D. Текст скетча с комментариями приводится ниже:

#define MICINPUT A0 // Микрофон подключен к выводу Analog In 0

#define FILTERINPUT A1 // Регулятор частоты среза фильтра - вывод Analog In 1

#define ECHOINPUT A2 // Регулятор уровня реверберации - вывод Analog In 2

#define PWMOUTPIN 11 // Вывод ШИМ - Digital 11


volatile bool waitForInterrupt = true; // Флаг ожидания прерывания

unsigned int out = 0; // Виртуальный звуковой выход

unsigned char filterInput = 0; // Состояние регулятора частоты среза фильтра

unsigned char echoInput = 0; // Состояние регулятора уровня реверберации


#define BUFSIZE 1096 // Размер буфера ревербератора

unsigned char buffer[BUFSIZE]; // Буфер ревербератора

int nbuf = 0; // Текущий индекс буфера ревербератора


void setup() // Инициализация микроконтроллера

{

noInterrupts(); // Выключить прерывания

clearBuffer(); // Инициализация буфера ревербератора

pinMode(PWMOUTPIN, OUTPUT); // Контакт вывода ШИМ в режим вывода

setupPWMOutput(); // Настройка ШИМ

interrupts(); // Включение прерываний

}


void loop() // Основной цикл программы

{

out = (analogRead(MICINPUT) << 6); // Чтение состояния микрофонного входа

// со сдвигом значения в диапазон [0..65536]

filter(); // Обработка сигнала фильтром

mixReverb(); // Наложение эффекта ревербератора

while(waitForInterrupt); // Ожидание прерывания

OCR2A = (out >> 8) & 0xFF; // Вывод сигнала на выход ШИМ

waitForInterrupt = true; // Установка флага ожидания прерывания

++nbuf; // Увеличение индекса буфера на 1

if (nbuf >= BUFSIZE) { // Если индекс вышел за пределы размеров буфера

nbuf = 0; // Обнуление индекса

filterInput = (analogRead(FILTERINPUT) >> 4); // Чтение состояния регулятора частоты среза фильтра

echoInput = (analogRead(ECHOINPUT) >> 3); // Чтение состояния регулятора уровня реверберации

}

}


void setupPWMOutput() // Настройка ШИМ

{

TCCR2A = 0;

TCCR2A |= _BV(WGM21) | _BV(WGM20); // Режим быстрого ШИМ (TOP = 0xFF)

TCCR2A |= _BV(COM2A1); // Сброс OC2A при совпадении, установка OC2A в нижней точке,

// (не-инверсный режим).

TCCR2B = 0;

TCCR2B |= _BV(CS20); // Тактовый сигнал таймера 2 - от предделителя /1

TIMSK2 |= _BV(OCIE2A); // Разрешить прерывание TIMER2_COMPA_vect

}


ISR(TIMER2_COMPA_vect) // Обработка прерывания TIMER2_COMPA_vect

{

waitForInterrupt = false; // Сброс флага ожидания прерывания

}


void clearBuffer() // Инициализация буфера

{

for (int n = 0; n < BUFSIZE; ++n) { // Заполнение буфера нулями

buffer[n] = 0;

}

}


void filter() // Фильтр ВЧ четвёртого порядка

{

static unsigned int prevx[4] = { 0, 0, 0, 0 };

unsigned int fout = out - (prevx[0] >> 7) * (filterInput >> 1)

- (prevx[1] >> 7) * (filterInput >> 2)

- (prevx[2] >> 7) * (filterInput >> 3);

- (prevx[3] >> 7) * (filterInput >> 4);

prevx[3] = prevx[2];

prevx[2] = prevx[1];

prevx[1] = prevx[0];

prevx[0] = out;

out = fout;

}


void mixReverb() // Наложение реверберации на сигнал

{

unsigned int rev = buffer[nbuf] * echoInput ;

out = (out >> 1) + rev;

buffer[nbuf] = ((out >> 9) & 0x7F) + ((rev >> 9) & 0x7F);

}

Скачать скетч.

Скачать схему в формате PDF.

Прослушать демо можно, кликнув по ссылке.

Коррективы:

Rev. A. Функция clearBuffer(): исправлено значение верхней границы цикла (было BUFSIZE-1, стало BUFSIZE).

Автор: Андрей Шаройко <vanyamboe@gmail.com>