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.![]()
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.![]()
-Bert
The glass is not half full or half empty, Its twice as big as needed for the job!
http://foamcasualty.com/ - Warbird R/C scratch building with foam!
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)
& 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!)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
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!).
100hz, 2.5Vp signal - extracted 'peak' sample is circa 255....Code:'---[TMR1 - interrupt handler]-------------------------------------------------- @ INT_DISABLE TMR1_INT ; diable Timer 1 interrupts T1CON.0 = 0 ' disables timer1 ADC_Sample: ADCIN 8, Signal_In 'sample the incoming signal IF SIGNAL_IN > 130 THEN 'a level of 128 is the zero crossing point (130 adds a bit of margin for error) zero_cross = 1 'a simple toggle/flag to track whether signal is above or below the 'zero point (ADC val of 128) IF SIGNAL_IN > PREVIOUS_SAMPLE THEN previous_sample = signal_in ; if this latest incoming sample is bigger than the last sample, then it supercedes the previous value GOTO INTERRUPT_END ENDIF if signal_in < 126 and zero_cross = 1 then 'if sample is 128 or less, then we're at the zero cross point, (126 adds some margin for error) therefore we've finished trying to sample peak peak_sample = previous_sample ' our peak sample is therefore whatever value is in the variable 'previous_sample' from above previous_sample = 0 'zero it, ready for when the waveform goes into positive territory again. zero_cross = 0 ' toggle the above zero/below zero flag endif INTERRUPT_END: LOOP_COUNT1 =LOOP_COUNT1+1 'this section just allow me to divide the interrupt rate down for onscreen display (else the serial port would bottleneck & PIC would likely restart) if loop_count1 = 1000 then 'I'm seeing about 1 update onscreen per second, which suggest the interrupt frequency is 1kHz & not the required 30khz! hserout [dec peak_sample,13, 10] loop_count1 = 0 endif TMR1H = $FF ' preset for timer1 MSB register TMR1L = $f0 ' preset for timer1 LSB register T1CON.0 = 1 ' bit 0 enables timer @ INT_ENABLE TMR1_INT ; enable Timer1 interrupts @ INT_RETURN end
100Hz, 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; - 16th February 2011 at 23:22.
I don't see how to set this up for 40Mhz. At any rate, what speed are you running?(if it helps, I'm using a PIC16f1828 ...max internal oscillator of 40Mhz)
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?
-Bert
The glass is not half full or half empty, Its twice as big as needed for the job!
http://foamcasualty.com/ - Warbird R/C scratch building with foam!
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...
Now the timer2 settings...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 interrupt routine...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)
Still just seeing 5khz 'interrupt frequency' on my scope (this is the pin I'm toggling in 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
AaaaaaarrrrrggggghhhhhHHHH!!!!!!!
Last edited by HankMcSpank; - 17th February 2011 at 01:10.
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.![]()
-Bert
The glass is not half full or half empty, Its twice as big as needed for the job!
http://foamcasualty.com/ - Warbird R/C scratch building with foam!
Ok, so in the light of your point about taking longer in the interrupt routine, I went back to basics...
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!)Code:ADC_Sample: @ INT_DISABLE TMR2_INT ; enable Timer 0 interrupts toggle PortC.3 @ INT_ENABLE TMR2_INT ; enable Timer 0 interrupts @ INT_RETURN
It's late here in the UK...need to sleep on this one - many thanks for your help.
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 10:46.
Hank, this still bothers me.
Can you try this:
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.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
Last edited by cncmachineguy; - 17th February 2011 at 12:23. Reason: Fixed frequency for 8M osc
-Bert
The glass is not half full or half empty, Its twice as big as needed for the job!
http://foamcasualty.com/ - Warbird R/C scratch building with foam!
Bookmarks