PDA

View Full Version : Low frequency audio level detector using PIC ADC?



HankMcSpank
- 16th February 2011, 09:26
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?

HankMcSpank
- 16th February 2011, 12:17
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?

cncmachineguy
- 16th February 2011, 12:33
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.

HankMcSpank
- 16th February 2011, 14:54
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!).

cncmachineguy
- 16th February 2011, 15:04
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. :)

HankMcSpank
- 16th February 2011, 22:48
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)


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



'---[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 (http://img135.imageshack.us/i/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 (http://img831.imageshack.us/i/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 (http://img255.imageshack.us/i/74156355.jpg/)


So what am doing wrong wrt getting the timer1 interrupts to interrupt at a 30Khz?!!!

cncmachineguy
- 17th February 2011, 00:30
(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?

HankMcSpank
- 17th February 2011, 00:53
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...



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


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


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 (http://img683.imageshack.us/i/52732853.jpg/)


AaaaaaarrrrrggggghhhhhHHHH!!!!!!!

cncmachineguy
- 17th February 2011, 01:07
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. :)

HankMcSpank
- 17th February 2011, 01:18
Ok, so in the light of your point about taking longer in the interrupt routine, I went back to basics...



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.

HankMcSpank
- 17th February 2011, 10:24
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.

cncmachineguy
- 17th February 2011, 12:20
This gives me an interrupt frequency of 11.44khz (ie 5.72khz on the scope)

Hank, this still bothers me.

Can you try this:


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.

HankMcSpank
- 17th February 2011, 12:30
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.

languer
- 17th February 2011, 20:36
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).

cncmachineguy
- 17th February 2011, 21:35
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.

HankMcSpank
- 17th February 2011, 22:31
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...




@ __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 (http://img253.imageshack.us/i/30853025.jpg/)

cncmachineguy
- 18th February 2011, 01:01
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.



INT_Handler TMR2_INT, _Bert_ISR, PBP, yes

becomes

INT_Handler TMR2_INT, _Bert_ISR, ASM, yes

HankMcSpank
- 18th February 2011, 01:41
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.

cncmachineguy
- 18th February 2011, 01:44
Thanks Hank, I will chew on these numbers tonight.

HankMcSpank
- 18th February 2011, 01:57
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!

cncmachineguy
- 18th February 2011, 03:34
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?

HankMcSpank
- 18th February 2011, 08:29
Excellent.



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)

cncmachineguy
- 18th February 2011, 12:03
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.

HankMcSpank
- 18th February 2011, 13:22
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.

cncmachineguy
- 18th February 2011, 14:05
Have a fun and safe trip Hank :)

HankMcSpank
- 21st February 2011, 18:01
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 (http://img683.imageshack.us/i/95717321.jpg/)

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

http://img691.imageshack.us/img691/6990/59250981.jpg (http://img691.imageshack.us/i/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.

cncmachineguy
- 21st February 2011, 18:40
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.

HankMcSpank
- 21st February 2011, 19:01
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...



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?

cncmachineguy
- 21st February 2011, 19:14
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...



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

HankMcSpank
- 21st February 2011, 20:00
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?

cncmachineguy
- 21st February 2011, 21:00
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.

HankMcSpank
- 21st February 2011, 21:12
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/index.php/Diodes_and_Transistors#Peak_Detector

HankMcSpank
- 22nd February 2011, 00:36
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/en/DeviceDoc/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 ???

cncmachineguy
- 22nd February 2011, 03:13
YES, See how easy it is :)

HankMcSpank
- 22nd February 2011, 19:47
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 :-)

Sneaky-geek
- 22nd February 2011, 22:53
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

HankMcSpank
- 23rd February 2011, 00:37
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..




'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 (http://img41.imageshack.us/i/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 (http://img404.imageshack.us/i/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)

languer
- 23rd February 2011, 06:19
Hats off to you Hank, most excellent project.

mackrackit
- 23rd February 2011, 08:37
Nice job Hank. What about writing this project up for the wiki?
It would be a great addition there.

mark_s
- 23rd February 2011, 15:46
Hank,
Thanks for sharing! You projects always push the limits, so we all learn.

Regards

HankMcSpank
- 23rd February 2011, 17:13
Thanks gents ...but I'm just a guy who has a couple "hmm, I wonder if ....." ideas - & without truly knowing how to arrive at the end destination! (or sometimes how to even start off on the journey!). It's the wealth of knowledge on this forum (& willingness to contribute to my threads) by folks that actually have a clue that makes it all happen.

Re the Wiki - I'd be too mortified/embarrassed that my (sometimes cringeworthy) stuff would actually make it into such a place (like I say, in this instance the program I've knocked up is not that elegant but seems to work...I think!), but to do it justice I'd have to write it up a lot better, do a lot more testing etc ....there's not enough hours in the day blah blah.... but I'm really chuffed though that you thought it was a potential contender though :-)

HankMcSpank
- 23rd February 2011, 22:11
You thought I was done here didn't you?!

Well for this to be 100% dependable, I need to be assured I know what the hell is going 'behind the scenes' with respect to this special event trigger melarkey.specifically whether the sample rate can be easily worked out & dependable.

Firstly...I've accounted for the ADC value 'jitter' - the incoming audio ignal is jittering! (this means it's not a measurement error...but the actual signal itself is varying. This is very heartening as it means this thing has real potential!)

Ok, something troubles me here, this from the datasheet.,..

"The Special Event Trigger output of the CCP occurs immediately upon a match between the TMR1H, TMR1L register pair and the CCPRxH, CCPRxL register pair. The TMR1H, TMR1L"

At 8MHz, therefore a 2MHz instruction clock that presetting 50 for TMR1L to match against would - if I have the maths/theory correct - mean a a 40khz special event trigger rate......but if that were the case, I'd have expected to seen the ADC samples be ing far more erratic at an audio input signal 20khz than what I'm seeing (two samples during the 20Khz waveform would have ADC results all over the place) ...so what's going on?

I looked at the datasheet, and apparently it's possible to get the special event trigger to toggle a pin (as opposed to take an ADC sample) ...at least in that way, I could the put a scope on to the physical pin confirm the observed frequency vs the number preloaded into the CCPRxH, CCPRxL pair. I'll revisit this over the coming evenings, but if anyone has had prior dealings with the special even trigger & specifically getting it to toggle a pin - all top tips greatfully received! Here's what I'm seeing on the CCP4 pin (I've set the pin as an output)....

http://img841.imageshack.us/img841/5582/10641603.jpg (http://img841.imageshack.us/i/10641603.jpg/)

(thats doesn't look like a good square wave at all!)

& here's the register I'm setting....



CCP4CON = %0000010




Also, can someone clear up this ADC uncertainty for me.....Fosc/8 & tads...there's a table on page 149 of the datasheet here http://ww1.microchip.com/downloads/en/DeviceDoc/41419A.pdf but it's not clear to me how many tads an 8 bit ADC sample will take (10 bits would appear to be 11.5...therefore 12us?!)....basically I want to know how long an ADC sample takes...as obviously this will dictate the maximum sample rate I can get the special event timer to run at.

cncmachineguy
- 24th February 2011, 01:44
At 8MHz, therefore a 2MHz instruction clock that presetting 50 for TMR1L to match against would - if I have the maths/theory correct - mean a a 40khz special event trigger rate......but if that were the case, I'd have expected to seen the ADC samples be ing far more erratic at an audio input signal 20khz than what I'm seeing (two samples during the 20Khz waveform would have ADC results all over the place) ...so what's going on?

I believe you are correct in the math, maybe you are off by a count of 1 for reload or something. The more perplexing part to me is the scope showing 30 Hz!! That really confuses me.

Is the incoming signal comming from a signal generator? if so it is prolly a really nice clean 20K? So if this is the case, it stands to reason the samples are being taken at the same spot maybe? since 20k is half the sample frequency.

What if the signal is 19.367K? (random number)

mackrackit
- 24th February 2011, 02:30
Re the Wiki - I'd be too mortified/embarrassed that my (sometimes cringeworthy) stuff would actually make it into such a place (like I say, in this instance the program I've knocked up is not that elegant but seems to work...I think!), but to do it justice I'd have to write it up a lot better, do a lot more testing etc ....there's not enough hours in the day blah blah.... but I'm really chuffed though that you thought it was a potential contender though :-)

You do not give yourself enough credit. Who cares if the code is "elegant" as long as it works. I do understand about time though...
Keep up the good work!

HankMcSpank
- 24th February 2011, 09:38
I believe you are correct in the math, maybe you are off by a count of 1 for reload or something. The more perplexing part to me is the scope showing 30 Hz!! That really confuses me.

Is the incoming signal comming from a signal generator? if so it is prolly a really nice clean 20K? So if this is the case, it stands to reason the samples are being taken at the same spot maybe? since 20k is half the sample frequency.

What if the signal is 19.367K? (random number)

That's a good call (as ever) Bert - I shall try it tonight.

Since I can't get to the bottom of ensuring the sample rate *is* what I *hope* it is....last night, I tried the good old 'tried & tested' ...send the max audio frequency I'm interested in (1.5khz) then tweak the TMR 'match against' CCPRxH, CCPRxL registers (sample rate) until I saw static ADC readings. From recollection it was about CCPR4l = 100 (which I think works out at 20khz sampling)...which is 13 samples throughout the waveform ....albeit this was with a slower decrement (pseudo capacaitor discharge rate). As I type this, that doesn't seem enough samples in the light of the stable readings I was seeing. Hmmm.

I really can't figure out what's going on with the toggling CCP4 output pin (the whacky square wave)...30Hz?!!!

Can anyone think of an alternative method of confirming the special event trigger frequency?!!

I've now got a fresh challenge-ette with this method ...the dependency (stability) of it very much depends on the time it takes for my main loop....obviously the ADC register is getting updated at the sample rate (in the background), but if I don't copy the ADRESH register into my my main loop & then process it 'in between' subsequent samples then I'm in trouble - therefore I need to establish a way of seeing exactly how much time my main loop is taking (ie so I know when I've gone past the time allowed between samples) .....how best to approach this? (my immediate thought is a timer count between loops & convert the timer value to human time that I can see onscreen?!!)

Dave
- 24th February 2011, 12:20
HankMcSpank , If you have an extra pin available or one you can (borrow) for the test, just set it at the begining and then clear it at the end of the portion of the program you are trying to time. Use a scope or a timer/counter to get the actual timing value.... It works for me... The plus side is the instructions don't add much to offset the timing. (portb.4 = 1).......(portb.4 = 0)

Dave Purola,
N8NTA

tony45
- 24th February 2011, 12:25
[QUOTE=HankMcSpank;99190]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)


i agree :)

cncmachineguy
- 24th February 2011, 13:07
Hank, I think something is wrong with the settings. The toggle rate seems to represent a word timing out. For instance timer1 is 16 bit, full count is 65535. 65535*.0000005 = 32.7675mSec. that sure looks like your toggle rate!

So I would guess TMR0 is not clearing on a match!

HankMcSpank
- 24th February 2011, 13:21
[QUOTE=HankMcSpank;99190]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)


i agree :)


Tony, Bert - The problem with step 4, is that in the absence of an equal or higher sample arriving (or, as you're proposing, while below zero point) my running peak variable is decremented (remember this code is emulating an analogue peak detect circuit - in such a circuit, the diode only lets through voltage that is higher.....and in the absence of a higher voltage the didoe is reverse biased & the cap starts discharging via a resistor - my running peak variable content decrements in this case).

This will utlimately mean that during the negative portions of the cycle, the running peak I've stored will decrement a fair bit, before the next positive part of the cycle returns & sets the peak high again - this is essentially like half wave rectification vs full wave - the former gives much more ripple, ie half wave ripple....

http://www.m.case.btinternet.co.uk/assets/images/DC_Half_Wave_Smoothed.gif

...so what I end up with is pseudo ripple (a variable decrementing then being topped up) This is why when the ADC reading goes below zero (ie below 128 on an 8 bit sample), I want to flip the negative cycle up - to be a positive cycle. This greatly reduces any decrementing (pseudo cap disharging)

This diagrams illustrate the end goal in analogue waveform terms...

http://www.eleinmec.com/figures/018_04.gif

The 0V (zero crossing point) in my case is 2.5V (an ADC reading of 128)...I want to flip the negative part of the wave up. It not only reduces ripple...but halfs the amount of time to detect peak etc, leading to less ripple (peak detect fluctuations)

http://images-mediawiki-sites.thefullwiki.org/04/3/7/7/74728043847055696.png

I realise 10 bits would be better, but just wanted to keep it all simple while I'm ironing out all the issues.

N8NTA - tks for the tip about measuring the main loop time, I'll do that.


Hank, I think something is wrong with the settings. The toggle rate seems to represent a word timing out. For instance timer1 is 16 bit, full count is 65535. 65535*.0000005 = 32.7675mSec. that sure looks like your toggle rate!

So I would guess TMR0 is not clearing on a match!

Wow Bert - that's a great spot! This from the datasheet...

"The Special Event Trigger output of the CCP occurs immediately upon a match between the TMR1H, TMR1L register pair and the CCPRxH, CCPRxL registerpair. The TMR1H, TMR1L register pair is not reset
until the next rising edge of the Timer1 clock."

As far as I can gather. other than to set the CCP register (to select pin toggle vs special event ADC triggering) ...there's nothing else to be done - not sure how this one can be tackled?!!

cncmachineguy
- 24th February 2011, 16:18
Ok, after some careful reading of the datasheet, I find I am no longer any smarter then I was before I started. I can't find anywhere in the datasheet to say in compare mode timer is reset on a match. It DOES say for the Special event trigger the timer is reset. So I suggest this:

Go back to SET mode (Special Event Trigger).
Enable interrupt on A/D conversion done.
Have the ISR do this:
toggle a pin (for debuging speed)
move A/D results to a variable for later use
Return from INT

With that done, several things are solved for you.

Ioannis
- 24th February 2011, 19:16
Hank,

1. why don't you make a full rectifier of your analog signal?

2. is the signal always of the same amplitude?

3. are you after a peak detector or an rms calculator? I am little confused by this thread...

Ioannis

HankMcSpank
- 24th February 2011, 19:32
Hank,

1. why don't you make a full rectifier of your analog signal?

2. is the signal always of the same amplitude?

3. are you after a peak detector or an rms calculator? I am little confused by this thread...

Ioannis

Hi Ioannis,

Ok, I came into this thinking I needed an RMS detector - I don't (despite the title ....which I'm unable to update/edit)....I really want to track 'peak' of a guitar signal. The incoming signal is of very wildly differing amplitude.....but fortunately it only goes up to 1.5khz max (the max fundamental on a guitar...I don't care about the harmonic content here - I'm only wanting to extract magnitude info, not accurately replicate the waveform digitally)

So why not use conventional analogue components? Well I'm *very* pushed for PCB real estate ...the intended PCB will already have a PIC, but the PIC isn't doing much (basic switching, LED indicators etc) ...so that got me thinking, why not extract peak from the incoming analogue signal using the PIC - just like an analogue full wave rectifier into a peak detector (which is what my code above is doing ....'full wave rectifying a signal' but via code inside the PIC) ...if nothing else, my intention was to learn a little more about sampling an AC signal rapidly .....& like most journeys - you discover other stuff enroute. And that has proved to be the case - the special event trigger - it's been a revelation to me at least.

Not only do I save components (PCB space)...I also avoid the forward voltage drop loss that an analogue peak detector suffers from (this is a relatively small signal I'm monitoring)...but it struck me I can also have on the fly varying 'discharge' rates to suit as my peak detector is now working in the digital domain.

The nut has actually been cracked ....all that's happening here now is a bit of a wash up...trying to get to the bottom of a couple of quirks I'm hitting (specifically the calculations wrt sample rate, etc)


Bert,

Again...great ideas...I'll implement this later to get a handle on the sampling frequency (I concur that the datasheet is a bit scant with the 'meat' here!)

Ioannis
- 24th February 2011, 20:21
The first that comes to mind is to use a DSP or dsPIC for the job as there seems to be a lot of maths.

But then since the max frequency is just 1,5KHz then maybe the PIC used is good enough.

Of course it has the speed to convert the input sample, but do you have time to proceess afterwards?

The other matter is the form of the input signal. You mentioned that it is greatly varying. Are you feeding it directly to the ADC? Any pre-conditioning I thing is necessary (filtering, dc-level bias etc.).

Ioannis

P.S. I changed the thread title hoping you do not have any objection.

languer
- 24th February 2011, 20:34
Hank, take a look at the Tips & tricks doc (http://ww1.microchip.com/downloads/en/DeviceDoc/01146B.pdf). Pay special attention to chapter3 (CCP and ECCP); tips #7 and #11.

HankMcSpank
- 24th February 2011, 20:37
The first that comes to mind is to use a DSP or dsPIC for the job as there seems to be a lot of maths.

But then since the max frequency is just 1,5KHz then maybe the PIC used is good enough.

Of course it has the speed to convert the input sample, but do you have time to proceess afterwards?

The other matter is the form of the input signal. You mentioned that it is greatly varying. Are you feeding it directly to the ADC? Any pre-conditioning I thing is necessary (filtering, dc-level bias etc.).

Ioannis

P.S. I changed the thread title hoping you do not have any objection.


Actually, that's the beauty here - there's almost no complicated maths....ie just et the special event trigger grabbing ADC samples at a reasonably high sampling rate (& this is all done in the background without any recourse to interrupts etc), then in the main (fast) loop, just compare the present ADC sample to the last sample...if it is the same or bigger, keep it as the 'present peak'....if it's not the same or bigger. start decrementing the variable linearly.

This approach wouldn't be right, if the PIC was already running intensively....but like I say, in my application the PIC isn't being too stressed.

The signal is preconditioned in that it goes through a buffer.....this puts the AC signal on a DC level of half VCC (necessary to full wave rectify the signal inside the PIC).

At the end of the day, I'm really just using the latent (otherwise wasted) power of the in-situ modest PIC to save on a few components (not for cost, but space) - .I don't want to start involving specific specialised ICs for what is after all a simple enough requirement.

oh, btw I've no problem with the title being changed!


Hank, take a look at the Tips & tricks doc (http://ww1.microchip.com/downloads/en/DeviceDoc/01146B.pdf). Pay special attention to chapter3 (CCP and ECCP); tips #7 and #11.

wow...a great doc.....need to digest - thanks!

HankMcSpank
- 24th February 2011, 21:25
Eeek, even a little bit of code like this....


main:
toggle PortC.4 'pin 6
'count1 = count1 +1
present_sample = ADRESH
IF present_sample < 128 then
present_sample = (127 - present_sample)
else
present_sample = present_sample -128
endif

IF present_sample >= rolling_peak THEN
rolling_peak = present_sample
else
if count1 = 50 and rolling_peak !=0 then
rolling_peak = rolling_peak -1
count1 = 0
endif
endif
goto main

is only toggling at 16khz - meaning that the main loop is only running at 30khz ...gulp (that's not leaving much room for anything else) bearing in mind I only have a 20khz sample rate going on...I wish the 16f1828 didn't have the ADC iissue about 8Mhz internal clock, becuase the obvious way of winning back por main loop time is to crank the internal clock up.

cncmachineguy
- 24th February 2011, 21:48
what is your device ID and REV?

HankMcSpank
- 24th February 2011, 21:53
what is your device ID and REV?

I don't know...and it's possible they've fixed the problem for the ones I have (purchased just a couple of weeks ago)

I use a Pickit2 programmer & can't seem to find anything that tells me such device info?

In the light of needing more 'main loop time', I've actually just cranked the Oscillator up to 16Mhz...and the ADC aspect still seems to be working ok.

cncmachineguy
- 24th February 2011, 21:59
Per the errata, they have fixed it for all current stuff. So unless your supplier has old stock, you are prolly good. I didn't think to look at the date on the errata, that may shed some light on if it will work. It sounded like it will either work or not. It didn't sound like it could be interminent.

And remember to change your ccpr values to reflect the faster clock. - unless you want to double the samples :)

HankMcSpank
- 24th February 2011, 23:16
Per the errata, they have fixed it for all current stuff. So unless your supplier has old stock, you are prolly good. I didn't think to look at the date on the errata, that may shed some light on if it will work. It sounded like it will either work or not. It didn't sound like it could be interminent.

And remember to change your ccpr values to reflect the faster clock. - unless you want to double the samples :)

Thanks Bert - Actually, I think we're done here would you believe! (Bert sighs in relief!)

I've aborted any attempt to confirm the sampling rate & have reasonable faith in our calculations! I've got the PIC Internal Oscillator running at 16 Mhz, and a special event trigger frequency of of 16Khz (ADC sample rate ), ie Timer1 matching against this....

CCPR4H = 0
CCPR4L = 250

(16Mhz clock, 4Mhz Instruction = 0.00000025sec period, therefore 250 x 0.00000025 = 0.0000625 sec .....so 1/0.0000625 sec= 16Khz)

....I've integrated the 'peak detect' code above into the final destination main loop - I've scoped a pin toggling within my my main loop @50khz which means it's looping at 100khz (so plenty of time to do other stuff in the main loop if I so desire). I don't use any interrupts except for switch presses,...and when a switch is pressed, I'm not worried about losing ADC samples.

...just connected it up to my guitar (vs the sig gen I've been testing with)....it's working as expected :)

A *very* happy Hank...thanks for all your input.

cncmachineguy
- 25th February 2011, 00:38
Thanks Bert - Actually, I think we're done here would you believe! (Bert sighs in relief!)

No sigh at all Hank, I really enjoy these kinds of threads. Lots of new things learned by all. I don't seem to have the imagination or desire to make such projects, but I LOVE thinking about them.

So Thank YOU and I look forward to your next one. :)

BTW, I agree you should consider making a WIKI of this. Your coding is fine, as Dave said you underestimate yourself. I do get the time aspect of it.

Ioannis
- 25th February 2011, 08:08
Thumbs up from me too.

Good idea with just a few components and clever programming. To the Wiki absolutely!

Ioannis

ronsimpson
- 26th February 2011, 23:14
Back at post #42 there is a picture of a square wave, (not so square wave) and a comment about how bad it looks. The wave looks like this because it has just passed through a capacitor. (A high pass filter) (DC blocking capacitor)

What are you trying to do? Measure the output of your electric guitar in peak response mode?

For long before I was born, audio has been measured in two modes.

Average mode: Full wave rectify. Then use a 2hz low pass filter. The response time is set to about the spoken syllable rate. (half wave produces little error)

Peak mode: Full wave rectify. Use a filter with an attack time of 1.7mS and a decay time of 660mS. You need the 1.7mS attack time or any very small peak will peg you meter but you ear can not hear that small peak.

Do you want RMS response?

HankMcSpank
- 27th February 2011, 10:19
Hi Ron,

Post #52 above explains what I was trying to achieve.....thankfully, it's al sorted now & seems to work ok.

ronsimpson
- 28th February 2011, 16:28
If you search for VU meter you will find examples of what you are working on.
With out a PIC the two attached pictures show an analog way of getting average and peak readings.
The rectifier is:
if MSB is 1 then shift left to remove that bit.
if MSB is 0 then compliment number +1 then shift left.
The peak detector (analog version):
The meter moves up at a rate set by R18 & C5. (fast)
The meter moves down at a rate set by R17+R18 & C5. (slow)
In software:
If the output of the rectifier > than the meter reading then (meter-rectifier)/x add to meter.
If rectifier is < meter then meter=meter-y.
Where y is a small number that will move the meter from full scale to 0 in about 600mS.
Where x sets the response time. (moving up)

There are several companies (THAT CORP and National see LM3915/16.) that have applications notes on the response of of VU meters. A typical test is to inject a 5khz tone for 10mS then stop. The meter should reach near full scale in 1.7mS and decay in 660mS.

My first try at a VU meter is attached. R2,3 set the input at 1/2 supply.
C1 blocks DC. 1/(2*2.14*50k*C1)=20hz set lowest frequency of intrest.
R1 & C2 makes a low pass filter to block information above 20khz.
R1 is to limit the current going into the PIC if the signal gets above 5 volts or below 0 volts. Could be 1k.

There is several better ways of setting the DC input of the ADC to 2.5 volts. More later.

HankMcSpank
- 2nd March 2011, 18:44
In software:
If the output of the rectifier > than the meter reading then (meter-rectifier)/x add to meter.
If rectifier is < meter then meter=meter-y.


Hi Ron

If you glance at the final code I knocked together - that's essentially what I'm doing ...... 'x' being the AC signal coming in on a PIC pin, 'y' being my pseudo (sw) cap discharge rate (albeit I'm not driving VU meters & I require different attack/release times).

ronsimpson
- 2nd March 2011, 21:07
Audio has a large dynamic range. I wanted to get as much range as possible. I needed to “zero” at 512 (10 bit). Resistors at 1% and even 0.1% did not always get me to zero. I also tried resistors to get within 1% of zero and then used the PWM output to move the ˝ voltage point +/- a little. I know in software I could have move the zero point to correct for this error. I wanted to do all the math slightly faster than the ADC could work and there was no time.

HankMcSpank
- 2nd March 2011, 21:47
Audio has a large dynamic range. I wanted to get as much range as possible. I needed to “zero” at 512 (10 bit). Resistors at 1% and even 0.1% did not always get me to zero. I also tried resistors to get within 1% of zero and then used the PWM output to move the ˝ voltage point +/- a little. I know in software I could have move the zero point to correct for this error. I wanted to do all the math slightly faster than the ADC could work and there was no time.

I can live with the 1% (even 2%)....quiescently, I was seeing jitter of just over 1%.(ie readings of 127,127,126,127,17,126, etc)

The accuracy of your ADC 'zero' point is obviously going to be down to how you approach DC biasing your AC signal feeding into the ADC pin - as a late suggestion, have you tried a DC blocking cap between your AC signal input & your chosen ADC pin? Obviously you then have to rebias the ADC pin - but 2 x 1% tolerance resistors between your PIC VCC & Gnd should ...with the the resistor junction connected to your ADC pin, should get you incredibly close to a reading of 512.

ronsimpson
- 3rd March 2011, 01:05
127,127,126,127,127,126, etc) APPROX. = 126.67
If you have a voltage =126.66 in 8 bit mode you will likely get 127 and 126 readings.

If I use 1% resistors then the error from resistors could be as great as 2%. Typically under 0.5%

The ADC is not perfect. point 128 may be off a little. Typically it is close but it could be several counts off. I wanted to correct for both resistor and ADC worst case errors.

Yes I have a DC blocking cap.

It appears you read in data in 8 bit mode. After rectificaton you have 7 bit data.
I chose to read in data in 10 bit mode. Rectify to 9 bit data. Loose the LSB to get 8 bit data.
The math is done in 8 bit mode to keep the speed high.

In RMS mode I square the 8 bit number to get a 16 bit number that is averaged.