AudioSwitcher — автоматизация того, что казалось бы не нужно автоматизировать +22



Предисловие


У меня дома есть пара комплектов хороших советских акустических систем. Но техника эта довольно старая и просто не может включаться с пульта или автоматически, а постоянно подходить к усилителю звука и включать/выключать его просто лень. Это проблему я и решил. Сначала была куплена ардуино и проект был сделан на ней, но качество работы меня не устроило и проект был переделан под STM32F103C8. В итоге у меня получилось устройство, которое имеет 4 аудио-входа, 1 аудио-выход, вход 220В и выход 220В. При наличии хотя бы одного активного аудио-входа на выходе 220В появляется напряжение, тем самым включая усилитель звука, и активный аудио-канал передается на выход.

Сложности при разработке


Казалось бы все просто: если АЦП получает не 0, тогда считать канал активным. Все почти так, но это работает только если включить источник аудио-сигнала и выключить на нем звук. При выключенном состоянии разные устройства дают разные помехи, тк они не полностью обесточены. Да и у плохих источников звука микроконтроллер мог улавливать помехи при выключенном звуке, причем довольно сильные. И это именно помехи источника, помехи на моей внешней аудиокарте STMка не видит, более того тихий звук с нее — 0.

Как сделать себе такое?


Давайте прежде всего определимся что нам надо. Писать стоимость я не буду, т.к. это сильно зависит от Вашего местоположения.

Что нам понадобится:

  • печатная плата
  • программатор ST-Link v2
  • 1 чип STM32f103C8
  • 4 реле для коммутации входного аудио-канала на выход
  • 1 реле для коммутации 220В для включения усилителя
  • AC-DC понижающий преобразователь 220В — 5В(можно взять со старой зарядки телефона)
  • сетевой провод и разъем для подачи тока нашему устройству и усилителю
  • розетка
  • резисторы, конденсаторы и другая мелочь

Естественно нам понадобятся аудио-провода и minijack штекера с гнездами.

Хотелось бы заострить внимание на выборе реле… Если с выбором 220В реле все предельно понятно: оно должно «уметь» коммутировать 220В переменное напряжение и управляться 3.3В. То с выбором звуковых реле не все так просто. Далеко не каждое реле, даже твердотельное, не будет давать помехи на выходе, а нам это очень важно. Я живу в Минске и не смог ничего найти подходящего и по адекватной цене, поэтому были заказаны с известного китайского магазина 4 реле PVT322A. Возможно в вашем регионе вы сможете найти что-то подешевле.

Схема и разводка


Раз уж начали, то продолжим изучать аппаратные особенности. На схеме, которую вы можете найти в репозитории в папке Eagle нужно подобрать токоограничительные резисторы(R4-7) под ваши реле. В моем случае это 30 Ом. Так же есть катушка L1: выбирайте любой фильтр, сглаживающий высокочастотные помехи.

Заказать печатную плату вы можете на PCBWAY или JLCPCB. Цены у них низкие, я заказывал у JLCPCB и они выставили мне счет всего в 2$. При заказе печатной платы вам нужны будут гербер файлы, вы из сможете найти все в той же папке или сами сгенерировать.

Перейдем к программной части


Рассказывать как подключить программатор к компьютеру, установить среду программирования и драйвер я не буду, т.к. этих инструкций очень много и они предельно доступные. На моей схеме предусмотрены выходы для программаторы. Я использовал Visual Studio 2017 + VisualGDB. Скачав проект из того же репозитория мы сможем открыть проект. Сразу же обратим на файл Settings.cpp.

Settings.cpp
#define DEBUG0 0//init USART and send all measurement values
#define DEBUG1 1//init USART and send information about recognition music or not
#define DEBUG2 0//just init USART
#define MaxEqualToZeroValue 3 //the value which equal or less is equated to zero
#define MaxAvarageForNoise (float)0.4//this is max avarage of measurement values so that the sound is considered noise for NOT active channel
#define MaxAvarageForActiveNoise (float)0.06//this is max avarage of measurement values so that the sound is considered noise for active channel
#define CountOfConsecutiveZeroValueForNoise 250//if count of consecutive zero values bugger it that sound is equated to noise
#define MinCountOfZeroValue 550//it's minimum count of zero values to equate to music(not consecutive)
#define USE_LED 1
#define LED_GPIO_PERIPH RCC_APB2Periph_GPIOC
#define LED_GPIO_GROUP GPIOC
#define LED_GPIO_PIN GPIO_Pin_13
#define USE_AMP 1
#define AMP_GPIO_PERIPH RCC_APB2Periph_GPIOB
#define AMP_GPIO_GROUP GPIOB
#define AMP_GPIO_PIN GPIO_Pin_12


Все настройки в этом файле задокументированы, но мы все равно остановимся на каждой настройке.

#define DEBUG0 0
#define DEBUG1 1
#define DEBUG2 0

Если присвоить дефайну DEBUG0 единицу, то наше устройство перестанет что либо делать, кроме того, что выводить по UARTу значения, которые он получает с аудио-входов в формате, который может «переварить» SerialPortPlotter.

Если же присвоить единицу DEBUG1, то устройство уже будет полностью функционировать, но будет выводить немного информации о работе по UARTу. Это все нужно исключительно для отладки.

Присвоение DEBUG2 даст всего лишь инициализацию UARTа. Если вы не понимаете, зачем это, то и не надо :-)

#define MaxEqualToZeroValue 3

Далее у нас параметр отвечающий значение которого или менее будет считать нулем. Как уже было ранее сказано некоторые источники звука плохого качества и сильно зашумлены.

#define MaxAvarageForNoise (float)0.4

Если аудио-канал сейчас не активен(т.е. канал, который сейчас не коммутируется на выход) и среднее значение измерений за один цикл измерений по этому каналу меньше значения этого параметра, то канал считается без звука.

#define MaxAvarageForActiveNoise (float)0.06

Этот параметр почти тоже самое, что и предыдущий, только для активного на данный момент канала. Дело в том, что когда канал активен и усилитель работает, то происходит падение напряжения аудио-канала. И если пренебречь этой настройкой, то устройство будет считать, что звук есть даже когда провод не подключен ни к какому устройству.

#define CountOfConsecutiveZeroValueForNoise 250

Этот параметр исключительно для оптимизации расхода процессорного времени. Если устройство встречает подряд заданное количество нулей, то оно считает, что это сигнал не звук.

#define MinCountOfZeroValue 550

А это уже важная настройка. Некоторые устройства, когда выключены создают странные помехи, но я выделил один общий фактор среди них: они очень редко опускаются до нулевых значения. Именно поэтому пришлось ввести этот параметр. Если количество нулевых значений за один цикл измерений меньше заданного, то сигнал считается шумом.

#define USE_LED 1
#define LED_GPIO_PERIPH RCC_APB2Periph_GPIOC
#define LED_GPIO_GROUP GPIOC
#define LED_GPIO_PIN GPIO_Pin_13
#define USE_AMP 1
#define AMP_GPIO_PERIPH RCC_APB2Periph_GPIOB
#define AMP_GPIO_GROUP GPIOB
#define AMP_GPIO_PIN GPIO_Pin_12

Данный блок предельно понятен для тех, кто уже программировал микроконтроллеры. Он выбирает пин на котором будет расположен светодиод и выход на реле управления усилителем. Если Вы не будете изменять мою схему, то эти параметры Вам не нужны.

Перейдем к следующим настройкам:

Открыв файл main.cpp, в самом начале функции
int main()
вы найдете определение кучи переменных.

Остановимся на этом поподробней. Там достаточно много параметров, отвечающих за аппаратую настройку микроконтроллера. Их мы затрагивать не будем.

const uint8_t channelsCount = 2;

Это количество входных аудиоканалов, которые будут использоваться.

const uint8_t countOfIterationsForSwitch = 5;

Количество циклов измерения, необходимое для изменения состояния активное/пассивное.

const uint8_t ADCSampleTime = ADC_SampleTime_239Cycles5;

Этот параметр отвечает за качество измерения. Оно установлена максимальное, не рекомендую его менять.

const uint16_t measurementsDuration = 2000;

Это время в ms, в течение которого будет производится один цикл измерений.

const uint32_t measurementFrequencies[] = { 1000, 1000, 1000, 1000 }; 

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

Заключение


Ну вот и все. Все необходимые настройки я описал. Осталось только собрать схему, скомпилировать проект, залить прошивку в микроконтроллер и радоваться.

В заключении хотелось бы сказать, что нельзя просто так оставить входной аудио-провод «не воткнутым» ни во что, нужно его вставить в какое либо устройство или заглушку в виде гнезда minijack, в котором все контакты соединены между собой.


Если у вас источники звука довольно хорошие, то вы можете поставить низкие параметры настроек, но для переключения состояния звука может потребоваться его выключение (не из розетки). Возможно когда нибудь я добавлю ссылку на 3D модель корпуса, но пока у меня нет 3D принтера и корпус на данный момент такой. Но это только пока: 3D принтер уже собирается :-)


Это моя первая статья, буду рад любой аргументированной критике. Понимаю, что это не шедевр, но я старался как мог.

Спасибо за прочтение.

UPD1: Добавлены изображения схемы и разводки в статье.

UPD2: Добавлены в репозиторий изображения схемы и разводки, добавлены новые коментарии в коде.




К сожалению, не доступен сервер mySQL