Ситуация сложилась так, что мой временный усилок добросовестно впахивает уже третий год, подтверждая высказывание “ничто так долго не работает, как временный костыль”.
И задумался я о том, что он частенько зря пожирает электроэнергию, по той простой причине, что мы часто забываем его выключить.
Итак у нашего девайса есть две основные функции как можно догадаться:
- Отключение усилителя при отсутствии сигнала
- Включение усилителя при появлении сигнала
Это все дело можно условно изобразить в виде следующей структурной диаграмки:
Инвертирующий усилитель раскачивает сигнал минимальной громкости до нужных величин, после чего этот сигнал подается на компаратор, сигналы которого в свою очередь обрабатываются микроконтроллером.
В качестве ОУ для первого усилка я выбрал TL062 (просто потому что он у меня был), компаратор + мк = pic12f629, 628а мне здесь показался избыточным.
Все мои фантазии вылились в такую схему:
Реле вынесено за пределы схемы, т.к. оно будет закрепляться отдельно. Резистор R5 на плате не установлен, я просто предусмотрел дополнительные площадки на всякий пожарный.
Схема собрана, надо бы проверить как там все дышит, для этого я набросал такой код:
#include <htc.h> #define _XTAL_FREQ 4000000 // ??????? 4 ??? #define LED GPIO4 // Config: ext reset, no code protect, no watchdog __CONFIG(MCLRDIS & UNPROTECT & WDTDIS & INTIO); volatile bit flag=0; unsigned char i; void main() { CMCON=0b00000010; TRISIO = 0x00; GPIO2 = 0; LED = 0; GIE = 1; // глобальные прерывания PEIE = 1; // прерывания периферии CMIE = 1; for (;;) { } } void interrupt isr() { if (CMIF) { __delay_ms(2); //что то вроде антидребезга if (!COUT) { flag = 1; } else { flag = 0; } LED = flag; CMIF = 0; }//cmif }//isr
Есть звук светодиод мигает, нет – остается в старом положении. Ниже видео с небольшим тестом(звук лучше сделать погромче):
Таким образом, мы убедились в работоспособности нашего девайса, можно приступать непосредственно к делу. Первым пунктом у нас идет выключение, давайте посмотрим что творится на выходе микроконтроллера при выключении звука (в него зашит код, написанный выше):
Здесь нам повезло и после выключения звука не проскакивает ни одного артефакта, но судя по статистике моих наблюдений время от времени это происходит.
Предположим у нас будет ситуация типа этой:
Вначале идет пачка пульсов – явная работа компаратора на звук, потом особняком идет пульс артефакт, которого хорошо бы было уже вообще не засекать. Для меня наиболее логичным путем показалось сохранение значения времени предыдущего промежутка, на каждом новом шаге. То есть, по прерыванию компаратора мы:
- Сохраняем текущее значение времени в переменную
- Обнуляем таймер и запускаем его заново
Осталось дописать условие останова, которое бы не задевало наш импульс артефакт и тогда по идее наше общее условие будет выглядеть примерно так:
tcur+tprevious >=tstop
Такое условие нам позволит не видеть один левый импульс, если их будет несколько, естественно оно не сработает, но как показывает практика, у меня они появляются по одному и крайне редко.
Все вышесказанное можно засунуть в вот такой код:
#include <htc.h> #define _XTAL_FREQ 4000000 // #define LED GPIO4 // Config: ext reset, no code protect, no watchdog __CONFIG(MCLRDIS & UNPROTECT & WDTDIS & INTIO); volatile bit CompFlag=0; volatile bit flag2=0; volatile bit currentState = 1; volatile unsigned char CompCount = 0; volatile unsigned int timeCount = 0; volatile unsigned int oldTimeCount = 0; volatile unsigned int OFFtimeCount = 0; unsigned char i; void main() { for (i=0; i<20; i++) __delay_ms(200); CMCON=0b00000010; TRISIO = 0x00; GPIO2 = 0; LED = 0; T1CKPS1 = 1; T1CKPS0 = 1; T1OSCEN = 0; TMR1CS = 0; GIE = 1; PEIE = 1; TMR1IE = 1; TMR1ON = 1; CMIE = 1; for (;;) { LED = currentState; if ((currentState)&&( (timeCount+oldTimeCount >= 80))) //From ON to OFF { currentState = 0; CompCount = 0; timeCount = 0; } } } void interrupt isr() { if (CMIF) { TMR1IE = 0; if (COUT) { if (currentState) { oldTimeCount = timeCount; timeCount = 0; TMR1L = 0x00; TMR1H = 0x00; } }//if(cOUT) GPIO5 = COUT; TMR1IE = 1; CMIF = 0; }//cmif if (TMR1IF) { CMIE = 0; TMR1L = 0x00; TMR1H = 0x00; if (currentState) { timeCount++; if (timeCount >= 65500) timeCount = 65500; } TMR1IF = 0; CMIE = 1; } }//isr
При частоте кварца 4 МГц, максимум что мы можем получить это ~500мс, то есть здесь я настроился на ожидание в 40 секунд, естественно эта цифра в дальнейшем будет увеличена, мало ли вдруг там в фильме все молчат долго 🙂
А вот и результат работы:
Артефакты упорно не появляются 🙂 Ну да ладно, здесь видна пачка с выхода компаратора (шел звук) потом я нажал на паузу, и примерно через 40 секунд наша линия пошла вверх.
Ну с выключением вроде разобрались, что теперь делать с включением?
Снова нам поможет картинка 🙂
Вкратце: В течение определенного времени, после срабатывания компаратора считаем импульсы, если их больше чем n, то на входе есть сигнал и пора включаться.
Подробнее: Чтобы адекватно и без ошибок проводить такую обработку необходимо некоторое время игнорировать все срабатывания компаратора, например, при первом включении питания девайса можно наблюдать красивую стартовую пачку импульсов, которые к звуку отношения никакого не имеют, а значит мы их должны фильтровать. А такая фильтрация замечательно производиться с помощью программной задержки. Ну в общем хватит слов, пора к коду!
#include <htc.h> #define _XTAL_FREQ 4000000 // #define LED GPIO4 // Config: ext reset, no code protect, no watchdog __CONFIG(MCLRDIS & UNPROTECT & WDTDIS & INTIO); volatile bit CompFlag=0; volatile bit flag2=0; volatile bit currentState = 0; volatile unsigned char CompCount = 0; volatile unsigned int timeCount = 0; volatile unsigned int oldTimeCount = 0; volatile unsigned int OFFtimeCount = 0; unsigned char i; void main() { for (i=0; i<20; i++) __delay_ms(200); CMCON=0b00000010; TRISIO = 0x00; GPIO2 = 0; LED = 0; T1CKPS1 = 1; T1CKPS0 = 1; T1OSCEN = 0; TMR1CS = 0; // Fosc/4 GIE = 1; PEIE = 1; TMR1IE = 1; TMR1ON = 1; CMIE = 1; for (;;) { LED = currentState; if ((currentState)&&( (timeCount+oldTimeCount >= 80))) //From ON to OFF { currentState = 0; CompCount = 0; timeCount = 0; } if ((!currentState)&&(OFFtimeCount>=60)) //From OFF to ON { CMIE = 0; if (CompCount>6) currentState = 1; CompCount = 0; OFFtimeCount = 0; CMIE = 1; } } } void interrupt isr() { if (CMIF) { TMR1IE = 0; if (COUT) { if (currentState) { oldTimeCount = timeCount; timeCount = 0; TMR1L = 0x00; TMR1H = 0x00; } else { CompCount++; } }//if(cOUT) GPIO5 = COUT; TMR1IE = 1; CMIF = 0; }//cmif if (TMR1IF) { CMIE = 0; TMR1L = 0x00; TMR1H = 0x00; if (currentState) { timeCount++; if (timeCount >= 65500) timeCount = 65500; } else { if (CompCount>0) { if (OFFtimeCount <= 40) { OFFtimeCount++; CompCount = 1; } else OFFtimeCount++; } } TMR1IF = 0; CMIE = 1; } }//isr
Здесь я не увеличиваю счетчик срабатываний компаратора, до тех пор пока таймер не натикает 20 секунд, а дальше уже считаю в течении 10 секунд сколько раз пощелкает и если там будет больше 6 срабатываний, врубаю логическую единицу на затворе н транзистора.
А вот и тест нашей системы для включения:
А теперь попробуем вывести такую диаграмму от подачи питания, включения девайса и до пропадания звука:
Работает! Теоретически можно было бы все оставить как есть и заняться внедрением в действующий усилок, но захотелось таки еще защиты от зависаний, что нам поможет сделать сторожевой таймер (Watchdog).
О нем нам нужно знать следующее:
- Независимый RC осциллятор с периодом 18 мс
- Время может быть увеличено в 128 раз с помощью предделителя таймера TMR0
C учетом этого приходим к максимально возможному времени в 2.3 секунды, при нашем периоде работы в 500 мс это вполне себе нормально. Чтобы настроиться на этот самый максимальный период, нужно проделать следующее:
PSA = 1; //Watchdog setup PS2 = 1; PS1 = 1; PS0 = 1;
Дальше, чтобы не происходило преждевременного сброса просто нужно растыкать по коду инструкцию CLRWDT(), по временам меньшим чем 2.3 секунды от прошлой инструкции.
Теперь осталось расширить время отключения, для начала я выбрал 2 минуты, а потом посмотрим надо ли будет его увеличивать/уменьшать:
#include <htc.h> #define _XTAL_FREQ 4000000 // #define LED GPIO4 // Config: ext reset, no code protect, no watchdog __CONFIG(MCLRDIS & UNPROTECT & WDTEN & INTIO); volatile bit CompFlag=0; volatile bit flag2=0; volatile bit currentState = 0; volatile unsigned char CompCount = 0; volatile unsigned int timeCount = 0; volatile unsigned int oldTimeCount = 0; volatile unsigned int OFFtimeCount = 0; unsigned char i; void main() { PSA = 1; //Watchdog setup PS2 = 1; PS1 = 1; PS0 = 1; CLRWDT(); for (i=0; i<20; i++) { __delay_ms(200); CLRWDT(); } CMCON=0b00000010; TRISIO = 0x00; GPIO2 = 0; LED = 0; T1CKPS1 = 1; T1CKPS0 = 1; T1OSCEN = 0; TMR1CS = 0; // Fosc/4 GIE = 1; PEIE = 1; TMR1IE = 1; TMR1ON = 1; CMIE = 1; CLRWDT(); for (;;) { LED = currentState; if ((currentState)&&( (timeCount+oldTimeCount >= 240))) //From ON to OFF { CLRWDT(); currentState = 0; CompCount = 0; timeCount = 0; } if ((!currentState)&&(OFFtimeCount>=60)) //From OFF to ON { CMIE = 0; CLRWDT(); if (CompCount>15) currentState = 1; CompCount = 0; OFFtimeCount = 0; CMIE = 1; } } } void interrupt isr() { if (CMIF) { TMR1IE = 0; CLRWDT(); if (COUT) { if (currentState) { oldTimeCount = timeCount; timeCount = 0; TMR1L = 0x00; TMR1H = 0x00; } else { CompCount++; } }//if(cOUT) GPIO5 = COUT; TMR1IE = 1; CMIF = 0; }//cmif if (TMR1IF) { CMIE = 0; CLRWDT(); TMR1L = 0x00; TMR1H = 0x00; if (currentState) { timeCount++; if (timeCount >= 65500) timeCount = 65500; } else { if (CompCount>0) { if (OFFtimeCount <= 40) { OFFtimeCount++; CompCount = 1; } else OFFtimeCount++; } } TMR1IF = 0; CMIE = 1; } }//isr
Это финальная версия кода. На данный момент код тестируется в реальном режиме, возможно в будущем выложу проапдейченную версию с актуальными таймингами.
Но остались некоторые манипуляции с железяками, нам необходимо:
- Взять питание +5В
- Взять питание +12В для реле (ну или найти другую релюшку)
- Взять сигнальный провод и землю
- Установить реле и плату нашего свитчера в действующий усилок
Первые пункты все происходят вокруг материнской платы компьютера и предполагают, что мы потянем как минимум 2 кабеля от материнки (+12 и +5 вольт), а лучше еще и землю еще одну затянуть для перестраховки.
Ну и как обычно, исходники здесь.