-
Low frequency audio level detector using PIC ADC?
I'm very tight on board space, so I'm looking at how I can win back some board real estate..
My circuit needs to detect the signal level of an incoming audio stream - at the moment, I use the well trodden path....full wave rectifiying then smooting into a PIC ADC.
But since I have a lot of unused pins on my PIC...and it's not being pushed hard, then why not use some of its latent power to detect signal level? If nothing else, this will help me educate myself about sampling.
The maximuum audio frequency is about 1.5khz...the lowest is about 50Hz, the signal is quite sinusoidal, so to my questions.....
1. To try and catch peak (or close to it), what would be a good sampling frequency? (if it helps, I'm using a PIC16f1828 ...max internal oscillator of 40Mhz) - I'm thinking 10khz?
2. Is this methodology correct....
setup a time to interrupt at a rate of 10khz
in the interrupt routine take an ADC sample into a variable, compare it to the last if it's bigger keep it, if it's smaller ignore it.
run a bit of maths to convert peak voltage to RMS (ie multipy by .0707 using some FP workaround)
Have another timer setup to reset the ADC sample variable every 20ms (ie 50hz the lowest frequency)
Anything I'm missing - or any comments?
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Quote:
Originally Posted by
HankMcSpank
2. Is this methodology correct....
setup a time to interrupt at a rate of 10khz
in the interrupt routine take an ADC sample into a variable, compare it to the last if it's bigger keep it, if it's smaller ignore it.
run a bit of maths to convert peak voltage to RMS (ie multipy by .0707 using some FP workaround)
Have another timer setup to reset the ADC sample variable every 20ms (ie 50hz the lowest frequency)
Actually I think my revised method below will be better....
(VCC is 5V, AC signal will sit on 2.5V, therefore positive half of cycle samples @10 bits will be between 512 & 1024, negative parts of the cycle will be between 0 & 512)
1. Setup a timer to interrupt at a rate of 20khz
2. In the timer interrupt routine take an ADC sample into a variable
3. Compare the ADC sample to the last sample - if it's higher keep it, if it's smaller ignore it.
4. If the sample reads below 512 (half VCC - the signal's zero cross), then the highest sample from step 3 is signal 'peak', now zero the variable ready for the next positive cycle (ie samples above 512)
5. Run a bit of maths to convert peak voltage sample to RMS (ie multipy peak sample by .707 using some FP workaround)
Anything else?
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Hank, assuming you have time to spare, why not just sample on each iteration of main? I assume it is running much faster then 20K, And if you can stand the short time it takes to aquire the sample, you will have the best sample rate without going to interupts.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Quote:
Originally Posted by
cncmachineguy
Hank, assuming you have time to spare, why not just sample on each iteration of main? I assume it is running much faster then 20K, And if you can stand the short time it takes to aquire the sample, you will have the best sample rate without going to interupts.
Hiya Bert ...thanks for the input.
The reason for wanting to use interrupts, is that I can sub-contract out the ADC sampling (precisely) & let it maintain itself without having to integrate the sampling into my main loop, where there's other stuff happening (and a whole heap of pauses within!).
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Well in that case, I would make the rate some easy timer rate. like a 8 bit timer with some prescale, running as fast as you can stand to be interupted. :)
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Well the concept seems to work, but would you believe the thing that's hampering me is getting the timer1 interrupt frequency set right....I'm chasing an interrupt (ADC Sample) frequency of about 30khz.
I seem to be getting an interrupt frequency of just 1Khz, which means only low frequency sine waves that I'm sampling are stable from an ADC perspective, what am I doing wrong....
here's the top bit of my code (setting up the timer1)
Code:
' Timer1 Registers:
' Prescaler=1:1; TMR1 Preset=65503; Freq=30,303.0303Hz; Period=33,000 ns
T1CON.5 = 0 ' bits 5-4 Prescaler Rate Select bits
T1CON.4 = 0
T1CON.3 = 1 ' bit 3 Timer1 Oscillator Enable Control: bit 1=on
T1CON.2 = 1 ' bit 2 Timer1 External Clock Input Synchronization Control bit: 1=Do not synchronize external clock input
T1CON.1 = 0 ' bit 1 Timer1 Clock Source Select bit: 0=Internal clock (FOSC/4) / 1 = External clock from pin T1CKI (on the rising edge)
T1CON.0 = 1 ' bit 0 enables timer
TMR1H = $FF ' preset for timer1 MSB register
TMR1L = $DF ' preset for timer1 LSB register
& below is my (DT) interrupt handler (feel free to suggest better way of writing this ...I'm a kludger at heart & I've not really a clue how to code well!)
zero_cross is just a variable to track whether the signal wave/cycle is above zero (the signal zero point = 128....ie 1/2 my supply of 5V)
signal_in is a variable to store the sample (previous_sample + peak_sample are too!).
Code:
'---[TMR1 - interrupt handler]--------------------------------------------------
@ INT_DISABLE TMR1_INT ; diable Timer 1 interrupts
T1CON.0 = 0 ' disables timer1
ADC_Sample:
ADCIN 8, Signal_In 'sample the incoming signal
IF SIGNAL_IN > 130 THEN 'a level of 128 is the zero crossing point (130 adds a bit of margin for error)
zero_cross = 1 'a simple toggle/flag to track whether signal is above or below the 'zero point (ADC val of 128)
IF SIGNAL_IN > PREVIOUS_SAMPLE THEN previous_sample = signal_in ; if this latest incoming sample is bigger than the last sample, then it supercedes the previous value
GOTO INTERRUPT_END
ENDIF
if signal_in < 126 and zero_cross = 1 then 'if sample is 128 or less, then we're at the zero cross point, (126 adds some margin for error) therefore we've finished trying to sample peak
peak_sample = previous_sample ' our peak sample is therefore whatever value is in the variable 'previous_sample' from above
previous_sample = 0 'zero it, ready for when the waveform goes into positive territory again.
zero_cross = 0 ' toggle the above zero/below zero flag
endif
INTERRUPT_END:
LOOP_COUNT1 =LOOP_COUNT1+1 'this section just allow me to divide the interrupt rate down for onscreen display (else the serial port would bottleneck & PIC would likely restart)
if loop_count1 = 1000 then 'I'm seeing about 1 update onscreen per second, which suggest the interrupt frequency is 1kHz & not the required 30khz!
hserout [dec peak_sample,13, 10]
loop_count1 = 0
endif
TMR1H = $FF ' preset for timer1 MSB register
TMR1L = $f0 ' preset for timer1 LSB register
T1CON.0 = 1 ' bit 0 enables timer
@ INT_ENABLE TMR1_INT ; enable Timer1 interrupts
@ INT_RETURN
end
100hz, 2.5Vp signal - extracted 'peak' sample is circa 255....
http://img135.imageshack.us/img135/8818/34443469.jpg
100Hz, 1.25Vp signal - extracted 'peak' sample should be circa 192 (remember here that the signal's zero crossing point is ADC=128)
http://img831.imageshack.us/img831/5980/43862501.jpg
Ok, now you start seeing the problem...up to 300Hz, the ADC readings are fairly stable at +/- 1 sample, but if I now take the audio frequency up to 400Hz, the ADC 'samples' start getting choppy, which suggests the interrupt rate isn't high enough...
http://img255.imageshack.us/img255/7186/74156355.jpg
So what am doing wrong wrt getting the timer1 interrupts to interrupt at a 30Khz?!!!
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Quote:
(if it helps, I'm using a PIC16f1828 ...max internal oscillator of 40Mhz)
I don't see how to set this up for 40Mhz. At any rate, what speed are you running?
Do you have a free pin to toggle each interupt to see exactly what the frequency is? While I agree the seeming like 1K is a far cry from 10K, but for me I just like to know for sure to fine tune it.
Also as a suggestion, DT pointed me to TMR2 and the use of the PR register. The advantage is no reloading of the preload. :) it also has pre and post scaling to turn your 8 bit counter into a slower counter.
Now for more confusion, your preload will give you 32 instructions before overflowing. I think you have that in the int handler itself. not that it will matter, but if you are running at 32 Mhz, 4uSec timing, or 250KHz. So maybe you are flooding HSEROUT?
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Quote:
Originally Posted by
cncmachineguy
I don't see how to set this up for 40Mhz. At any rate, what speed are you running?
Do you have a free pin to toggle each interupt to see exactly what the frequency is? While I agree the seeming like 1K is a far cry from 10K, but for me I just like to know for sure to fine tune it.
Also as a suggestion, DT pointed me to TMR2 and the use of the PR register. The advantage is no reloading of the preload. :) it also has pre and post scaling to turn your 8 bit counter into a slower counter.
Now for more confusion, your preload will give you 32 instructions before overflowing. I think you have that in the int handler itself. not that it will matter, but if you are running at 32 Mhz, 4uSec timing, or 250KHz. So maybe you are flooding HSEROUT?
Hi Bert.
Yeah, I got carried away with the max Osc value this pic can run at - it's 32Mhz.
that said, I'm only running it at 8Mhz (there an errata sheet out that says early versions of this PIC had probs with ADC above 8Mhz clock - so I'm staying on the safe side)
Good call about the toggle - I've put a toggle pin line in the interrupt handler, my scope measures the square wave at 2.66Khz, but I'm assuming the interrupt rate is double that? (half, the time low, half the time high)...therefore I've an interrupt frequency of about 5Khz
I'll have a a go with Timer2, but the PICcalc timers that abound are doing my head in! :-)
Edit:
Just tried timer2...
Code:
@ __CONFIG _CONFIG1, _FCMEN_OFF & _FOSC_INTOSC & _WDTE_OFF & _MCLRE_OFF & _CP_OFF & _IESO_OFF & _BOREN_OFF & _PWRTE_OFF & _LVP_OFF
@ __CONFIG _CONFIG2, _LVP_OFF
INCLUDE "DT_INTS-14.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
Osccon = %01110010 'Osc 8Mhz
DEFINE OSC 8
Now the timer2 settings...
Code:
'Timer2 Registers:
'Prescaler=1:1; TMR2 PostScaler=1:1; PR2=66 - Freq = 30,303.0303Hz - Period = 33,000 ns
T2CON.6 = 0 ' bits 6-3 Post scaler 1:1 thru 1:16
T2CON.5 = 0
T2CON.4 = 0
T2CON.3 = 0
T2CON.2 = 1 ' Timer2 on bit: 1=Timer2 is on;
T2CON.1 = 0 ' bits 1-0 Prescaler Rate Select bits
T2CON.0 = 0
T2CON = %00000100
PR2 = 66 ' PR2 (Timer2 Match value)
Now the interrupt routine...
Code:
ADC_Sample:
@ INT_DISABLE TMR2_INT ; enable Timer 0 interrupts
toggle PortC.3
ADCIN 8, Signal_In 'sample the incoming signal
IF SIGNAL_IN > 130 THEN 'a level of 128 is the zero crossing point (130 adds a bit of margin for error)
zero_cross = 1 'a simple toggle flag to track whether we're above zero or below
IF SIGNAL_IN > PREVIOUS_SAMPLE THEN previous_sample = signal_in ; if this latest incoming sample is bigger than the last then it supercedes the prvious value
GOTO INTERRUPT_END
ENDIF
if signal_in < 126 and zero_cross = 1 then 'if sample is 128 or less, then we're at the zero cross point, (126 adds some margin for error) therefore we've finished trying to sample peak
peak_sample = previous_sample ' our peak sample is therefore whatever value is in the variable 'previous_sample' from above
previous_sample = 0 'zero it, ready for when the waveform goes into positive territory again.
zero_cross = 0 ' toggle the above zero/below zero flag
endif
INTERRUPT_END:
LOOP_COUNT1 =LOOP_COUNT1+1
if loop_count1 = 100 then
hserout [dec signal_in,9,dec peak_sample,13, 10]
loop_count1 = 0
endif
@ INT_ENABLE TMR2_INT ; enable Timer 0 interrupts
@ INT_RETURN
Still just seeing 5khz 'interrupt frequency' on my scope (this is the pin I'm toggling in the interrupt routine)...
http://img683.imageshack.us/img683/6763/52732853.jpg
AaaaaaarrrrrggggghhhhhHHHH!!!!!!!
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Hank WAIT!!! (drama over)
Before you change anything, play with your preload. you still only have 32 instructions between interupts. Do you understand why?
Timers are this: they always count up and flag int when they rollover to 0. when set up to clock from fosc/4, this means 1 count per ASM instruction. so you are at 2Mips, so (1/2,000,000)*count is your int frequency. in your case count = 32 which = 16uSec. or 62.5K.
So the question is why is yours 5.32K? I believe it is because you are actaally taking longer to get there AND longer in the int, so your numbers are skewed.
Play with the preload until you get numbers that make sense. Then work from there. :)
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Ok, so in the light of your point about taking longer in the interrupt routine, I went back to basics...
Code:
ADC_Sample:
@ INT_DISABLE TMR2_INT ; enable Timer 0 interrupts
toggle PortC.3
@ INT_ENABLE TMR2_INT ; enable Timer 0 interrupts
@ INT_RETURN
This gives me an interrupt frequency of 11.44khz (ie 5.72khz on the scope)...so my interrupt routine is severely skewing my interrupt! (told you I sucked!)
It's late here in the UK...need to sleep on this one - many thanks for your help.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Having slept on this....think I'm gonna have to give this one much more thought.
Not the interrupt frequency problem (btw - Bert, I think you were right...put the peak detect code in the main loop!), but I noticed late last night that for the above code 'trapped' the peak of a nice sigen sine wave very well, a real life guitar signal caused problems. Basically, a guitar signal is full of harmonics...so you've not just got a fundamental going on, you've got strong second harmonic immediately after a string has been plucked - these harmonics are lower in signal magnitude & worse still, force the signal to cross zero too - which plays havoc with the larger fundamental peak detect bit.
I guess what I really need is a slick bit of code that acts like a diode with a smoothing cap following on - there's probably a role to play with DTs averaging routines here!
Something along these lines...
1. Is present ADC sample larger than last ...if so then store it (that'll be the diode bit)
2. When cycle crosses zero, then largest sample from the logic above is the peak.
3. Have a rolling average for all the peaks (that'll be the capactor/LPF bit)
One to mull.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Quote:
Originally Posted by
HankMcSpank
This gives me an interrupt frequency of 11.44khz (ie 5.72khz on the scope)
Hank, this still bothers me.
Can you try this:
Code:
BMAIN: 'make this your new temporary main for this test
LATC = LATC ^ %00001000 ' this assumes you can use portc.4 as an output.
GOTO BMAIN
'new ISR
LATC = LATC ^ %00000100 ' this will toggle portc.3
the point here is main will just toggle portc.4 and the ISR will toggle portc.3. When you scope these, we will see the speed at which you are running. We expect main to be ~4 instructions, maybe 6. so that would give C.4 a frequency of .5M (.25M) and the ISR should interupt every 7 or 8th time through main.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Hi Bert,
Sure, I will certainly try that (it troubles me & I'd like to understand why I'm not getting the interrupt frequency that I should), but it won't be until tonight after the kids have been put to bed! I'm in the UK, so it'll probably be about 5.00pm EST when I have some results.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
I am no fan of doing the RMS thing on the PIC itself, but in any case you probably be better served by having the CCP module trigger the ADC conversion. That way you get a very precise sampling rate (look at section 15.2.5 SPECIAL EVENT TRIGGER of the datasheet).
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Oh Thats a pretty sneaky way to do it. (BTW, section 16.2.5 not 15.)
@Hank, are you by any chance using this in the same applaction as the phase do-hicky you were working on a little while back? Seems if so, you may already have the parts in place for the trigger.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
languer - tks, I'd actually seen that previously in the datasheet, but couldn't immediately work out how to apply it, then I forgot about it.
Bert - phase project has been parked up.
Meanwhile, this ongoing problem is going to turn out so easy that I'll never be able to show my face on here again.
Ok, the latc didn't work, so I'm using a basic toggle, here the stripped back code...
Code:
@ __CONFIG _CONFIG1, _FCMEN_OFF & _FOSC_INTOSC & _WDTE_OFF & _MCLRE_OFF & _CP_OFF & _IESO_OFF & _BOREN_OFF & _PWRTE_OFF & _LVP_OFF & _CLKOUTEN_OFF
@ __CONFIG _CONFIG2, _LVP_OFF & _PLLEN_OFF
INCLUDE "DT_INTS-14.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
Osccon = %01110010 'Osc 8Mhz
DEFINE OSC 8
DEFINE NO_CLRWDT 1 ' PBP doesn't clear WDT automatically
'16F1828 Pin Connections*********************************************
TRISC.4 = 0 '6 RC4 Main Output
'**********************************************************************
CM1CON0 = 0 ' COMPARATORS OFF
CM2CON0 = 0 ' COMPARATORS OFF
ADCON0 = 0
ADCON1 = 0
ANSELA = 0 ; All Port A ports to be digital
ANSELB = 0 ; All Port B ports to be digital
ANSELC = 0 ; All Port B ports to be digital
txsta = %10100100 'needed to turn on pin 10 as valid HSerout pin.
WPUA = 0 'weak pullups disabled.
WPUB = 0 'weak pullups disabled.
WPUC = 0 'weak pullups disabled.
'Timer2 Registers:
'Prescaler=1:1; TMR2 PostScaler=1:1; PR2=66 - Freq = 30,303.0303Hz - Period = 33,000 ns
T2CON.6 = 0 ' bits 6-3 Post scaler 1:1 thru 1:16
T2CON.5 = 0
T2CON.4 = 0
T2CON.3 = 0
T2CON.2 = 1 ' Timer2 on bit: 1=Timer2 is on;
T2CON.1 = 0 ' bits 1-0 Prescaler Rate Select bits
T2CON.0 = 0
PR2 = 66 ' PR2 (Timer2 Match value)
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR2_INT, _Bert_ISR, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM
@ INT_ENABLE TMR2_INT ; enable Timer 0 interrupts
;----[Main Program Loop]------------------------------------------------------------------
BMAIN: 'make this your new temporary main for this test
toggle portc.4 'pin 6 (blue scope trace)
'LATC = LATC ^ %00001000 ' this assumes you can use portc.4 as an output.
GOTO BMAIN
'---[TMR2 - interrupt handler]--------------------------------------------------
Bert_ISR:
@ INT_DISABLE TMR2_INT ; enable Timer 0 interrupts
toggle PortC.3 'pin 7 (yellow scope trace)
'LATC = LATC ^ %00000100 ' this will toggle portc.3
@ INT_ENABLE TMR2_INT ; enable Timer 0 interrupts
@ INT_RETURN
end
& here's the result....
green trace is the main loop toggle (port C.4) with a frequency of just 1kHz(!!!) & the yellow trace is the ISR toggle (port C.3)with a frequency that's higher at 12Khz(!!!) , I have not got one clue what is going on here - it feels like 'week one - intro to PICs' all over again!
http://img253.imageshack.us/img253/1829/30853025.jpg
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Hank, 2 things to try. First turn off use PBP in the int setup. check the output. then change pr=200. no reason for the value, just much larger. You don't need to post the Scope unless you want. I am happy with just the numbers right now.
Code:
INT_Handler TMR2_INT, _Bert_ISR, PBP, yes
becomes
INT_Handler TMR2_INT, _Bert_ISR, ASM, yes
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Hi Bert,
You're onto something!
Firstly, this doesn't stack up, but when I disable interrupts my Main loop sees a frequency of 142Khz on my scope (therefore double that at 284khz?!)
Ok, now when I enable interrupts with the 'pbp' entry ....much baddo, my main loop toggle shows just 498Hz on my scope (therefore about 1khz)
change the bit you outlined to it so asm, I once again get the main running 142khz (albeit choppy - I guess the interrupts are stopping a nice clean toggle in the main) & the interrupt toggle showing about 15khz on my scope (30khz)
changing PR=200, changes the interrupt toggle rate to about 5khz on my scope (therefore 10khz)
it's after 1.30am here in London.....and so to bed, g'night.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Thanks Hank, I will chew on these numbers tonight.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Just one last afterthought - I'm using a PIC16F1828 for which the BAS & INC files haven't been offically released yet....but Darrel kindly pasted up the files for someone else .....
http://www.picbasic.co.uk/forum/showthread.php?t=13977
now I have no clue if that's significant or not, but thought it worth flagging!
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Well MAYbe that is why LATC didn't work, but the numbers seem pretty right on. main loop speed free running seems to show there are 7 instructions being executed. heres the math:
8Mhz clock / 4=
2Mips
1/2000000 = .0000005 sec per instruction
.5uSec * 7 = 3.5uSec
1/.0000035 = 285,714.xx Hertz ( close to 284K as posted)
:)
Using PBP instead of ASM, show clearly the Interupt is firing WAY faster then the time it takes to get in and out. If you are game to it, I think we can come to a definate minimun.
With PBP and main loop at 1K, what was ISR frequency?
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Excellent.
Quote:
Originally Posted by
cncmachineguy
With PBP and main loop at 1K, what was ISR frequency?
On my scope, with main loop @1Khz, the ISR frequency shows as 5.98Khz (therefore 12Khz)
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Ok, here's what we know. Type=ASM, we get the timing we expect. Now if you lower PR until the main loop frequency starts to fall off, we will know the max rate for a clean interrupt. Well actually you can tell by the "choppy" main signal. Is each cycle choppy, every other 1, every 3rd? If the main loop takes 7 instructions, then we would expect it to occur 66/7 times before interruption. Well minus the time the ISR takes. Using PR with TMR2, the clock keeps ticking. So you get a more stable interrupt as the ISR also adds to the count. Unless it is turned off and on.
Thats enough for now, I may be rambling.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Hi Bert, no you're not rambling...this is a good stuff for those of us, that aren't used to getting deep down 'n dirty with instructions/clock timings (that'll be me)....I'll have a tweak with the PR.
That said, I'm travelling to visit relatives in the North of England tonight..so no more updates until I can get back & fire the PIC up (Monday night) - but many thanks for your input thus far.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Have a fun and safe trip Hank :)
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Ok, here's what I see with a PR value of 200....
(top green trace is main loop 'toggle', the lower yellow trace is interrupt 'toggle ....oh btw, ignore the spikes sitting on the square waves!)
http://img683.imageshack.us/img683/1208/95717321.jpg
now here it is with a PR value of 66....
http://img691.imageshack.us/img691/6990/59250981.jpg
Now, whilst I can understand the need to get a clean main loop (& establish what PR2 value yields that), I'm wondering here what's going on with the PR2 value ....does higher mean less interrupts? (ie should I be taking the PR2 value higher or lower)...cos whichever way I quickly experimented with I got an uneven main toggle trace!
oh & btw, how did you determine the port toggle took 7 instruction cycles a couple of posts ago?!!
I've pondered the end goal a little bit - an anlogue peak detect circuit emulator.
What's going on with a peak detector is that a cap charges up to reflect the value an AC signal....a diode stops the cap charge decrementing when the waveform traverses back towards zero - meanwhile there's another resitor in parallel across the cap that disharges the cap (at a t rate chosen by the circuit designer).
So I guess I need some code like this...
1. Take an ADC Sample (via a timer ...or special event trigger etc)
2. Is the present signal sample bigger than the last? ......if yes, then update a variable with the latest value, if not then do nothing.
Have another timer decrement the same variable (this is the discharge time)
Ought to be simple enough (once I get a grip on this interrupt frequency melarkey!).....and would stop the strong second harmonic presence messing up the overall results as seen when I put a guitar into the circuit.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Ok hank, since you choose to swallow the red pill, here goes:
PR works opposite from the timer's. Timers ALWAYS count up and generate interrupt (if enabled) on overflow, So for a given count, we preload the timer to start somewhere other then zero. PR on the other hand, is a match for the timer. So if PR=200, the timer will start counting (from zero). When it gets to 200 (TMR = PR), timer is reset and starts again. If there is no PR postscaler, interrupt will be set.
So a higher PR will yeild less interrupts. I would like to take back my statement about a "clean" main. You can NEVER acheive that in the literial sense if using interrupts by definiation it will be interrupted. On the other hand, lets say the main is 7 instructions, you would never expect main toggle to be less then 1/7th of its uninterupted rate. Do you see why?
Make sense?
How did I know 7 insctructions? Its just math. you are running a 8 Mhz clock. that becomes 2MIPS. (2 million insctructions per second). 1 instruction takes .0000005 sec * 7 = .0000035 seconds. 1/.0000035 = ~285KHz. Ok really this is the proof, but go backwards to get the number of instructions.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Ok, yes, I'm getting there (thanks), but I come back to how did you know the main took 7 instructions, once again, here's the main...
Code:
BMAIN: 'make this your new temporary main for this test
toggle portc.4 'pin 6 (green scope trace)
GOTO BMAIN
So, to my eyes, there are two instructiions in there ...the toggle & the goto ...do these add upto to the aforementioned 7?
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Quote:
Originally Posted by
HankMcSpank
Ok, yes, I'm getting there (thanks), but I come back to how did you know the main took 7 instructions, once again, here's the main...
Code:
BMAIN: 'make this your new temporary main for this test
toggle portc.4 'pin 6 (green scope trace)
GOTO BMAIN
So, to my eyes, there are two instructiions there ...the toggle & the goto ...do these add upto to the 7?
The confusion comes from my exclusion. When I am talking about number of instructions, I mean to say ASM instructions. This just shows the beauty of PBP. I prolly should use the term fosc/4, but I have always just thought about it as instructions. BTW, some take 1(fosc/4) and some take 2. GOTO (in ASM) takes 2 while BCF (clears a bit) takes 1
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Quote:
Originally Posted by
cncmachineguy
The confusion comes from my exclusion. When I am talking about number of instructions, I mean to say ASM instructions. This just shows the beauty of PBP. I prolly should use the term fosc/4, but I have always just thought about it as instructions. BTW, some take 1(fosc/4) and some take 2. GOTO (in ASM) takes 2 while BCF (clears a bit) takes 1
thanks bert...ok, so taking PR2 = 8 yields an interrupt toggle of 22khz, therefor 44khz (I took PR2 down to 6, but the interrupt frequency on my scope stayed the same?!)
I'm thinking about putting the 'capacitor discharge' emulation into the same interrupt, so something high level like this...
(I will use a variable called previous_sample to store highest ADC sample)
1. does present incoming ADC sample equal (or is greater than) previous_sample.
i)if so previous_sample = present incoming sample .....& retrun
ii) if not, decrement previous_sample by '1' & return (the 1 here can be altered depending on how fast you want the cap emulation to dischare)
with an ADC sample of 8 bits, the maximum signal sample is 255.....the maximum pseudo discharge time will be the interrupt rate 22.27uS (44khz) * 255, therefore about 6ms (which is nicely tied in with the lowest frequency 'half cycle' on a guitar @82Hz)....sound about right?
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Quote:
Originally Posted by
HankMcSpank
thanks bert...ok, so taking PR2 = 8 yields an interrupt toggle of 22khz (I took PR2 down to 6, but the interrupt frequency on my scope stayed the same so it looks like I'll have to stick with that?!)
This doesn't sound to good. lets see if it makes sense: PR2=8 means every time TMR2 counts to 8, an interrupt should fire and TMR2 gets reset. with PR2=66, you had ~30K. So remember the main thats approx 7 clock ticks(clock ticks = instructions, so as not to continue confusing everyone). The math remains the same, for TMR to count to 8 thats 4uSec! 1/.000004 = 250K, not 22K. So my guess is you are going too fast. what was mains frequency? start back at PR2=66 and increase it. see if the INT gets slower, until it is 20K. At PR2=200, I think you were at 15K? so just guessing, maybe a value of ~130?
Now about that ADC time. I haven't read the errata, but did it say not to run faster then 8mHz or not to run the ADC faster? I ask because I saw something about the speed of the ADC implying there is a prescaler for that so maybe you could run at 32mHz?
Quote:
I'm thinking about putting the 'capacitor discharge' emulation into the same interrupt, so something high level like this...
(I will use a variable called previous_sample to store highest ADC sample)
1. does present incoming ADC sample equal (or is greater than) previous_sample.
i)if so previous_sample = present incoming sample .....& retrun
ii) if not, decrement previous_sample by '1' & return
with an ADC sample of 8 bits, the maximum signal sample is 255.....the maximum pseudo discharge time will be the interrupt rate (22khz) * 255, therefore about 12ms (which is nicely tied in with the lowest frequency 'cycle' on a guitar @82Hz)....sound about right?
I don't know much about this, I will have to think on it tonight when I get home from work.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
thanks bert...slight oversight in my previous (noe edited)...the toggle frequency on my scope is HALF the interrupt frequency!
Re the pseudo cap - a peak detect cicuit (the end goal here)....it's basically a diode followed by a cap...when the incoming ac signal is higher in magnitude in voltage than that across the cap the diode allows it through...and the cap's voltage increases......when the incoming signal is lower the diode switches off....the cap holds the voltage, but for this circuit to be useful, we actually add in a resistor to discharge it at the designer's choice.....hence wanting to decrement the ADCsample variable when the incoming signal is lower.
http://hades.mech.northwestern.edu/i...#Peak_Detector
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
ok, despite far too loong trying, i can't get into the interrupt, do my stuff & get out fast enough (for the sample rate I need)....so i'm officially parking the interrupt method of taking ADC samples at a high frequency.
So back to what languer recommended to me earlier - using the special event trigger to do ADC in background hw.
this from the data sheet (http://ww1.microchip.com/downloads/e...Doc/41419A.pdf page 216 23.2.4 SPECIAL EVENT TRIGGER )...
"When Special Event Trigger mode is chosen(CCPxM<3:0> = 1011), the CCPx module does the following:
• Resets Timer1
• Starts an ADC conversion if ADC is enabled
The CCPx module does not assert control of the CCPxpin in this mode.
The Special Event Trigger output of the CCP occursimmediately upon a match between the TMR1H,TMR1L register pair and the CCPRxH, CCPRxL register pair. The TMR1H, TMR1L register pair is not reset until the next rising edge of the Timer1 clock. The Special
Event Trigger output starts an A/D conversion (if the A/D module is enabled). This allows the CCPRxH, CCPRxL register pair to effectively provide a 16-bit programmable
period register for Timer1."
After an hour of dicking about - I've got it working but would like confirmation how to set the sampling frequency - So I'm back to maths again!
Ok, with an 8Mhz clock, I now know that we get 2MIPs.....therefore will the Timer1 increment with each instruction clock cycle? so - 1/2,000000? ...that's every 500ns?). If so, to get a sampling frequency of say 40khz, this would mean an ADC sample needed every 25uS, therefore every (25us/500ns) 50 instruction cycles. is it just a matter then of setting CCPR4H = 0 and CCPR4H = 50 for the Timer1H & Timer1L registers to match ???
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
YES, See how easy it is :)
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Quote:
Originally Posted by
cncmachineguy
YES, See how easy it is :)
thanks for bearing with me Bert ...it seems to be working a treat - ie Im feeding an AC signal of 5V peak to peak into an analogue pin...and the PIC is extracting the signal's peak level in real time.....just like a hardware peak detector (but better as the 'charge time' is essentially instant, and the 'discharge time' linear & changeable on the fly).
FWIW, I'm also reversing the ADC reading from the negative half of the incoming AC signal (ie readings below 128) to give a smoother overall reading (essentially full wave rectification in software)
the special event trigger was absolutely key in getting this to work without using interrupts ....a hearty thanks to languer too :-)
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Hi Hank,
That is great that you got it to work! Is there a chance that you could post a working version in the code examples section for the rest of us? It is still a little foggy to me.......
Thank you,
Sneaky-geek
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Quote:
Originally Posted by
Sneaky-geek
Hi Hank,
That is great that you got it to work! Is there a chance that you could post a working version in the code examples section for the rest of us? It is still a little foggy to me.......
Thank you,
Sneaky-geek
Sure, at the risk of being ridiculed for my elementary coding (I'll always be a n00b!), here you go..
Code:
'16F1828 Pin Connections*********************************************
' PIN# NAME USE & CONNECTION
'1 Vdd +5VDC power supply
TrisC.3 = 1 '7 RC3 AN7 ADC iN
TrisB.7 = 0 '10 RB7 HSEROUT pin
'20 Vss Ground
''*****************************************************************
@ __CONFIG _CONFIG1, _FCMEN_OFF & _FOSC_INTOSC & _WDTE_OFF & _MCLRE_OFF & _CP_OFF & _IESO_OFF & _BOREN_OFF & _PWRTE_OFF & _LVP_OFF
@ __CONFIG _CONFIG2, _LVP_OFF
Osccon = %01110010 'Osc 8Mhz
DEFINE OSC 8
DEFINE NO_CLRWDT 1 ' PBP doesn't clear WDT automatically
' HW Serial port setup - 115200 @ 8Mhz***********************************
DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1
DEFINE HSER_CLROERR 1 ' Clear overflow automatically
DEFINE HSER_SPBRG 16 ' 115200 Baud @ 8MHz, 2.12%
SPBRGH = 0
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
'********************************************************************
CM1CON0 = 0 ' COMPARATORS OFF
CM2CON0 = 0 ' COMPARATORS OFF
ADCON0 = %00011101 'SET AN7 as selected ADC input + ON
ADCON1 = 0
ANSELA = 0 ; All Port A ports digital
ANSELB = 0 ; All Port B ports digital
ANSELC = %00001000 'Analogue pin on RC3 (AN7)
CCP4CON = %0001011 'set CCP4 into special event timer mode
TMR1H = 0 ' set timer1 counter registers to zero
TMR1L = 0
CCPR4H = 0 ' preset for timer1 match (MSB register) when timer1 TMR1H & TMR1L match these two regsiters, an ADC sample occurs in hardware
CCPR4L = 50 ' preset for timer1 match (LSB register) lower value = higher sampling rate (40Khz here - I think!)
T1CON.0 = 1 ' set timer1 on
count1 var byte
rolling_peak var BYTE
present_sample var BYTE
rolling_peak = 0
present_sample= 0
count1 = 0
'**********ok that's all the config out the way, let's start***************************************
main:
count1 = count1 +1
present_sample = ADRESH 'copy the ADC value as taken in the background (by the special event trigger) into a variable
IF present_sample < 128 then 'if it's less than 128, then this the waveform has entered the negative part of its cycle.
present_sample = (127 - present_sample) 'therefore let's make it positive! (just like a full wave rectifier)
else
present_sample = present_sample -128 ' if it' not in the negative cycle (as checked above), then we're in the positrive part of the cycle, so let's rebase everyting down to 0
endif
IF present_sample >= rolling_peak THEN 'if the present sample is equal or bigger than the last sample - keep it
rolling_peak = present_sample
else 'if the present sample is not equal or bigger than the last, start decrementing (discharging)
if count1 = 10 then ' this count will set the decrement ('discharge') rate (lower is faster)
rolling_peak = rolling_peak -1
count1 = 0
endif
endif
HSEROUT [dec rolling_peak, 13, 10] 'lets make sure everything looks ok on screen
goto main
The above sets a 16f1828 up to take ADC samples using the special event trigger ...if using the exact code above, your AC input signal goes in on pin 7 (max 'peak to peak' amplitude being your VCC level - best to amplify your signal for presenting into the pin so that 'max peak to peak' of your incoming signal is as near your VCC voltage as possible - you'll achieve max resolution then) ....your AC signal should be sitting an a DC level of half your PIC's VCC (ie positive cycle go from ADC values of 128->255. negative cycles go from 127->0 - the zero crossing point is an ADC value of about 128)
I should emphasise that I've not used it in anger yet....there may well be some bugs to flush out - & I'm sure it can be improved (feel free anyone to outline how I can make it slicker anyone!), but it works approx ±1 jitter @7 bits...seems to be reasonably ok upto 20khz...which suggests my sampling frequency in the code is higher than I think it is (btw you lose 1 bit of ADC resolution by flipping the negative cycle up into positive territory...this is a sacrifice worth making as you'll get less 'psuedo ripple' when otherwise the value of the ensuing decrements during the negative cycles would kick in - you can always use 10 bits for your ADC, if so, then you'll lose you one bit, therefore a max 512 values up for grabs) ...
5V peak to peak @300hz....
http://img41.imageshack.us/img41/4354/53140692.jpg
Here's approx 2.5V peak to peak @20khz (a tad more jitter), it should read 64, but I reckon the discrepancy can be accounted for with the DC not being exactly 2.5V that the AC signal sits on (&/or my Chinese USB scope might not be that accurate!)
http://img404.imageshack.us/img404/3926/64419063.jpg
One last point - despite the thread title, this detects 'peak' not RMS (& peak is what I need in the end...if you need RMS multiply the result by .707 ....or rather use a workaround for the lack of floating point)
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Hats off to you Hank, most excellent project.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Nice job Hank. What about writing this project up for the wiki?
It would be a great addition there.
-
Re: Low frequency audio RMS signal level detector using PIC ADC?
Hank,
Thanks for sharing! You projects always push the limits, so we all learn.
Regards