guy
Joined: 21 Oct 2005 Posts: 297
|
Frequency detector in analog signal |
Posted: Tue Sep 20, 2016 9:48 pm |
|
|
It's hard to define this piece of code. I wrote it to detect repetitive pulses from a light sensor (detect a flashing light) under different ambient light conditions. It assumes the pulse in the analog signal is easily detectable and without other significant frequencies or noise but it doesn't require specific voltages or amplitude. It could be used to detect recurring pulses, measure their frequency, and it also contains an optimized IIR low pass filter which is useful.
Theory of operation:
1. A sample is taken by taking 4 consecutive ADC readings and averaging them.
2. Each sample is fed into an IIR Low Pass Filter in order to establish a baseline, average signal level.
3. Rising and falling edges of pulses are detected by comparing a sample to the average baseline. For example when looking for a rising edge of the pulse, if a sample is 5% higher than the average it is detected.
4. The time between rising and falling edges is measured, allowing for 10% error in timing. After several repetitive pulses are measured the program goes to Detected mode and gives 3 beeps every 30 seconds.
Code: | #include <16F1829.h>
#device ADC=10
#fuses INTRC_IO,NOLVP,PUT,BROWNOUT,BORV25,NOCLKOUT,NOWDT
#use delay(CLOCK=8000000) // 8MHz
#define PUSHBUTTON PIN_B6 // pulled up, low=ON
//#define BZR PIN_C3..C7, low=ON
#DEFINE THRES 50 // 50/1024 = rise/fall detection threshold:
// 5% of full scale above/below average
#DEFINE NUM_PULSES_DETECT 3
#define PULSE_ACCURACY 0.1 // ס10% pulse accuracy
#define LOOP_TIME 50 // *1ms
void main() {
int16 smpl;
int16 lpf;
int32 tmp;
int16 riseTmr=0,fallTmr=0;
int16 prevRiseTmr=0,prevFallTmr=0;
int16 riseCntr=0,fallCntr=0;
byte i;
short startup;
short roundd;
short waitRising=1;
setup_adc(ADC_CLOCK_DIV_16); // 8MHz
SETUP_ADC_PORTS(sAN2); // PIN 17, PIN_A2
SET_ADC_CHANNEL(2);
delay_ms(1);
read_adc(); // dummy read - ignore result
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
PORT_B_PULLUPS(0x40); // B6 pushbutton pulled up
output_c(0); // BZR on
delay_ms(50);
output_c(0xFF); // BZR off
startup=1;
set_timer1(0);
while(1) {
smpl=0;
for(i=0;i<4;i++) {
delay_ms(1);
smpl+=read_adc(); // take new sample
}
smpl>>=2; // average 4 samples, then process:
if(startup) {
startup=0;
lpf=smpl; // initial lpf value for IIR filter
}
// calc LPF (IIR)
tmp=lpf;
tmp<<=5; // weight of new sample 1/32
tmp-=lpf;
tmp+=smpl;
roundd=bit_test(tmp,4);
tmp>>=5;
if(roundd) tmp++;
if(lpf==tmp) { // make sure to reach dest. at some point
if(lpf<smpl) lpf++;
else if(lpf>smpl) lpf--;
}
else lpf=tmp;
// compare new sample to average :
// test for rising edge:
if(waitRising) {
if(smpl>lpf+THRES) {
// rising edge:
waitRising=0;
// compare to last rise-to-rise time:
if((riseTmr>=prevRiseTmr*(1-PULSE_ACCURACY))&&(riseTmr<=prevRiseTmr*(1+PULSE_ACCURACY))) {
// similar rise-to-rise time:
if(riseCntr<0xFFFF) riseCntr++;
}
else {
// different rise-to-rise time:
riseCntr=0;
fallCntr=0;
}
// save timer for next round:
prevRiseTmr=riseTmr;
riseTmr=0;
}
}
else {
// test for falling edge:
if((lpf>=THRES)&&(smpl<lpf-THRES)) {
// falling edge:
waitRising=1;
// compare to last fall-to-fall time:
if((fallTmr>=prevFallTmr*(1-PULSE_ACCURACY))&&(fallTmr<=prevFallTmr*(1+PULSE_ACCURACY))) {
// similar fall-to-fall time:
if(fallCntr<0xFFFF) fallCntr++;
}
else {
// different fall-to-fall time:
riseCntr=0;
fallCntr=0;
}
// save timer for next round:
prevFallTmr=fallTmr;
fallTmr=0;
}
}
// test if enough pulses detected:
if((riseCntr>=NUM_PULSES_DETECT)&&(fallCntr>=NUM_PULSES_DETECT)) {
// !!!! CELEBRATE !!!!
riseCntr=0;
fallCntr=0;
goto detected;
}
// advance timers:
riseTmr++; // allow overflow - it will prevent false similar pulses detected
fallTmr++; // allow overflow - it will prevent false similar pulses detected
while(get_timer1()<(LOOP_TIME*250));
set_timer1(get_timer1()-(LOOP_TIME*250-3)); // -3 is finetune
}
/////////////
detected:
smpl=0;
while(input(PUSHBUTTON)) {
if(smpl==30000) smpl=0;
if((smpl==0)||(smpl==200)||(smpl==400)) output_c(0); // BZR on
if((smpl==100)||(smpl==300)||(smpl==500)) output_c(0xFF);// BZR off
smpl++;
delay_ms(1);
}
reset_cpu();
} |
|
|