Low frequency audio level detector using PIC ADC?

1. ## 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?
Last edited by HankMcSpank; - 16th February 2011 at 10:33.

2. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

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?
Last edited by HankMcSpank; - 16th February 2011 at 13:20.

3. ## 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.

4. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

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!).
Last edited by HankMcSpank; - 16th February 2011 at 15:56.

5. ## 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.

6. ## 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
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....

100Hz, 1.25Vp signal - extracted 'peak' sample should be circa 192 (remember here that the signal's zero crossing point is ADC=128)

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...

So what am doing wrong wrt getting the timer1 interrupts to interrupt at a 30Khz?!!!
Last edited by HankMcSpank; - 17th February 2011 at 00:22.

7. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

(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?

8. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

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)...

AaaaaaarrrrrggggghhhhhHHHH!!!!!!!
Last edited by HankMcSpank; - 17th February 2011 at 02:10.

9. ## 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.

10. ## 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.

11. ## 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.
Last edited by HankMcSpank; - 17th February 2011 at 11:46.

12. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

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.
Last edited by cncmachineguy; - 17th February 2011 at 13:23. Reason: Fixed frequency for 8M osc

13. ## 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.

14. ## 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).

15. ## 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.

16. ## 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

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!

Last edited by HankMcSpank; - 18th February 2011 at 00:23.

17. ## 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```

18. ## 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.
Last edited by HankMcSpank; - 18th February 2011 at 02:44.

19. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

Thanks Hank, I will chew on these numbers tonight.

20. ## 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 .....

now I have no clue if that's significant or not, but thought it worth flagging!

21. ## 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?
Last edited by cncmachineguy; - 18th February 2011 at 04:43. Reason: asked wrong question

22. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

Excellent.

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)

23. ## 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.

24. ## 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.

25. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

Have a fun and safe trip Hank

26. ## 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!)

now here it is with a PR value of 66....

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.
Last edited by HankMcSpank; - 21st February 2011 at 19:05.

27. ## 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.

28. ## 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?
Last edited by HankMcSpank; - 21st February 2011 at 20:11.

29. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

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
Last edited by cncmachineguy; - 21st February 2011 at 20:16.

30. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

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?
Last edited by HankMcSpank; - 21st February 2011 at 21:58.

31. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

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?

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.

32. ## 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.

33. ## 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
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 ???
Last edited by HankMcSpank; - 22nd February 2011 at 01:52.

34. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

YES, See how easy it is

35. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

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 :-)
Last edited by HankMcSpank; - 22nd February 2011 at 20:52.

36. ## 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

37. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

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

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```

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....

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!)

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)
Last edited by HankMcSpank; - 23rd February 2011 at 02:23.

38. ## Re: Low frequency audio RMS signal level detector using PIC ADC?

Hats off to you Hank, most excellent project.

39. ## 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.

40. ## 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