A situation in place was the next – my half done amplifier was working already 3 years just proving the statement “there last longing device is a temporary one”. It was kind of fine – but it consumes an energy. In a fact a solid chunk of the energy – it is class A amplifier. At some point in time I started to think – why to not switch on/off the amplifier in dependence on the signal presence.
So, our future device should have tow main functions:
- To disable the amplifier when there is no signal at the input
- To turn ON the amplifier when the signal appeared at the input
Whole thing can be represented with a next diagram:
The inverting amp amplifies the weak signal to larger amplitudes and then the signal delivered to the comparator, which provides the logic output for the microcontroler.
I used TL062 operational amplifier IC (just because I hat it in my stashes) for the first amplifier and compartor+uC is pic12f629, I thought it is way too much to use 628a here.
All my thoughts materialized in the next schematic:
A relay is moved outside of this schematic, since it will be mounted separately from the pcb. Some resistors are just pcb placeholders just in case (for instance R5).
To check if a device is alive I coded such thing:
#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
The sound is here – the led is blinking, no sound – the led is not changing the state.
Thus, we made sure that the device can preform the task it created for. Now we can switch to the functions realizations, the first bullet is turning on, lets check what do we have at the comparator output when the sound is disappearing (the same code is still in the microcontroller)
We’ve been lucky in this particular case – no any single artifact after a sound source is silent, however I have seen some peaks before, so we would need some averaging technique.
Let’s say we have the next situation:
In the beginning we have a bunch of pulses – clearly something is happening, perhaps the music is on, then we have a couple of singular pulses.
For me the most logical way was to save the timer value at each trigger., i.e. on each interrupt from the comparator, we:
- Saving the timer value to the variable
- Clearing the timer and restarting it
Now, when we should to cut off the power from the amp? When the time periods of singular pulses are big enough:
tcur+tprevious >=tstop
This condition will work more or less, but today I would change it to something more reliable which will analyze more single pulses and average them out.
Anyway I did not do it then, so this what I had created:
#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
With quartz frequency equal to 4MHz, the maximum checking window is about 500ms, i.e. in my code my total averaging period is 40s, but I’m going to increase this number in future (maybe I watch the movie and there is a long awkward silence).
The result of the work:
On the left we still have the sound signal, then it goes off and, finally, after ~40s the relay getting the switch off signal.
All right, the switching off is done, how about turning thing ON?
Drawing a picture…
Shortly: after comparator start to see pulses we start to count them, if we have more than n pulses it is time to turn thing on.
#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
In the code above, I am not increasing the counter of comparator triggers, till we dont have 20 second passed. Only then counting for 10s how many of pulses we have there and if more than 6 – it’s a time to rise the gate of the N transistor to high level.
Testing:
A bit expanded test from turning OFF to turning ON:
It works! Theoretically, I could have left everything as is and live my life, but the thing with uC – it can freeze, it will freeze and we need a way back out from this situation – the watchdog module serves for this purpose.
What we need to know about the watchdog:
- it has independent RC oscillator with period in 18ms
- the time can be increased 128x times with help of the prescaler of TMR0
With statements above we are getting the maximum possible period in 2.3 seconds, not bad I think. To set the maximum period of the watchdog one need to:
PSA = 1; //Watchdog setup PS2 = 1; PS1 = 1; PS0 = 1;
Then, to not get our uC restarted we need to scatter the CLRWDT() instruction along the code.
Now I need to just increase a time interval after which I am OK to switch off the power from the amplifier – I chosen it to be 2 minutes for now:
#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