|
|
View previous topic :: View next topic |
Author |
Message |
zio_pecos
Joined: 24 Oct 2004 Posts: 21 Location: italy
|
How Do i Increase Resolution with CCP Capture? |
Posted: Sun Nov 14, 2004 2:29 am |
|
|
hello
and excuse me for the insistence but i'm a beginner and i don't understand very well all about timers...(i'm working with pics only since september)
i am using Pic16C745 capture mode to measure a frequency with varies from 1Khz to 15Khz.
initially i have used this code
/--------------------------------------------------------------------------
// CCP1 Data
//
int16 CCP1Value;
int16 CCP1OldValue;
BOOLEAN CCP1Captured;
//--------------------------------------------------------------------------
#int_CCP1
CCP1_isr()
{
CCP1Value = CCP_1 - CCP1OldValue;
CCP1OldValue = CCP_1;
CCP1Captured = TRUE;
}
//--------------------------------------------------------------------------
void main() {
long periodo,per;
int h1,h2;
int8 out_data[USB_EP1_TX_SIZE];//array outgoing data
setup_counters(RTCC_INTERNAL,RTCC_DIV_256);
setup_ccp1(CCP_CAPTURE_RE);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_4);//era 8
enable_interrupts(INT_CCP1);
enable_interrupts(global);
CCP1Value = 0;
CCP1OldValue = 0;
CCP1Captured = TRUE;
usb_init();//inizializza la trasmissione tramite porta usb
usb_wait_for_enumeration();
if(usb_enumerated())
while(1){
if (CCP1Captured) {
//---
// F = 1/T
// Timer1 prescaler DIV_BY_8
// Pic16C745 at 24Mz -> 1.66666e-7* 8=1.33333e-6
//---
periodo = ((float)CCP1Value*1.333e-6)*550000;
per=periodo;
CCP1Captured = FALSE;
}
//break in 2 pieces to send through usb
h1= make8(per, 0);//low
h2= make8(per, 1);///high
//h3= make8(per, 2);
//h4= make8(per, 3);
//----------------------------------------------------------------------------------
out_data[0]=0;
out_data[1]=0;
out_data[2]=0;
out_data[3]=h1;//contiene la lettura di frequenza
out_data[4]=h2;
out_data[5]=0;
out_data[6]=0;
out_data[7]=0;
usb_put_packet(1, out_data, USB_EP1_TX_SIZE, TOGGLE);
delay_ms(500);
}}
but With a 24MHz clock and a Timer1 divide by 8 the CCP count increments every 1.33333us
1/((24000000/4)/8) = 1.3333333333e-6.
So, say the CCP value is 68. 68*1.33us = 90.66us or 11.028KHz
So, say the CCP value is 69. 69*1.33us = 91.99us or 10.869KHz
So, say the CCP value is 70. 70*1.33us = 93.33us or 10.714KHz
The resolution is not high enough for timer1's increment rate.
then i've found this post:
http://www.ccsinfo.com/forum/viewtopic.php?t=906
and i used the code written inside...
but the compiler gives me many errors!! why?
can someone help me in writing the code??
after i copied this
#define BytePtr(var, offset) (char *)(&var + offset)
// The Timer1 interrupt increments an 8-bit variable which
// extends the timer to 24 bits. We need this so we can
// avoid having to switch the Timer pre-scaler between "low"
// and "high" rpm ranges.
char gc_timer1_extension;
BOOLEAN gc_capture_flag;
#int_TIMER1
void timer1_isr(void)
{
gc_timer1_extension++;
}
#int_ccp1
void ccp1_isr(void)
{
char timer_ext_copy;
int32 current_ccp;
static int32 old_ccp = 0;
// Set flag to indicate that we did a capture.
gc_capture_flag = TRUE;
current_ccp = (int32)CCP_1; // Read the current CCP
// Get local copy of the timer ext.
timer_ext_copy = gc_timer1_extension;
// Check if a Timer1 interrupt is pending. If so, check if
// the CCP capture occurred before or after the Timer rolled
// over. We can tell if it occurred after it rolled over, if
// the CCP's MSB is zero. ie., if the CCP is somewhere between
// 0x0000 and 0x00FF.
// We know that we can just check if the MSB = 0x00, because
// it takes about 30 us to get into this ISR. (Using a 4 Mhz
// crystal on a 16F628). The timer increments at 1 us per
// count, so 0xFF = 255 us.
// Actually, to be safer, I'll give it 2 MSB counts, which is
// 511 us. That way, if I lengthen any of the other ISR's,
// we'll still be able to detect the roll-over OK.
// If the timer did roll over after we got a CCP interrupt,
// then we need to increment the timer extension byte, that we
// save. We have to do that because the CCP interrupt has
// priority, and so it executes before the Timer isr can
// execute and increment the extension.
// (Designing the code with the priority switched doesn't help.
// You still have the same type of problem. With CCP first,
// the fix is easier).
//
// Was CCP captured after Timer1 wrapped ?
// If so, increment the copy of the timer ext.
if(TMR1IF_BIT)
{
if(*BytePtr(current_ccp, 1) < 2)
timer_ext_copy++;
// Since we know a timer interrupt is pending, let's just
// handle it here and now. That saves a little load off
// the processor.
gc_timer1_extension++; // Increment the real timer extension
TMR1IF_BIT = 0; // Then clear the Timer1 interrupt
}
// Insert the timer extension into the proper place in the
// 32-bit CCP value.
// ie., Insert it into location "EE" as follows: 0x00EEnnnn
// (nnnn = the CCP).
*BytePtr(current_ccp, 2) = timer_ext_copy;
// Because we're using unsigned math, we don't have to worry
// if the current value is less than the old. The result is
// always the absolute value of the difference. The only way
// there could be a problem is if the new CCP value had rolled
// over twice. But with a 24-bit value, and a Timer
// pre-scalar of 1, that's 16.7 seconds. That's way beyond any
// practical value.
// Edited on Jan. 2, 2004: There was a bug in this routine,
// because I was promoting a 24-bit value to a 32-bit data type,
// but the upper byte was always left = 0. This caused a problem
// with the 32-bit subtraction when the 24-bit value rolled over past 0.
// Kenny spotted this error and provided a fix in a PM to me.
// I have commented out the original line, and inserted his fix, below.
//g32_ccp_delta = current_ccp - old_ccp;
g32_ccp_delta = (current_ccp > old_ccp) ? current_ccp - old_ccp : current_ccp + (0x1000000 - old_ccp);
// Save the current ccp value for next time.
old_ccp = current_ccp;
}
what i must do ???
thanks |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sun Nov 14, 2004 7:50 am |
|
|
First a general remark: When posting code use the 'Code' button! This ensures the formatting of your code is kept and will make reading much easier to us.
The errors you get are from two missing variable declarations.
Sorry, but I get the feeling that you have no clue as to what you are doing. You are browsing this forum for code that might do what you want it to do, but please do inderstand there are about a dozen different solutions for measuring period times. It all depends on the frequency of the input signal, required accuracy and update period.
In order to save everybody a lot of time, please answer the following questions:
What is your lowest input frequency?
What is your highest input frequency?
How accurate must the measurement be?
Your update period is 0,5 seonds, isn't it? |
|
|
zio_pecos
Joined: 24 Oct 2004 Posts: 21 Location: italy
|
|
Posted: Sun Nov 14, 2004 11:11 am |
|
|
lower frequency : 1k Hz
higher frequency: 15k Hz
accuracy: 1Hz
i think that in this condition my update period is 0.5 seconds...i don't know which setting can change the update period
excuse me a lot for all this but it's hard to me understand...
i must do this work at all cost
excuse me again...
try to help me |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Sun Nov 14, 2004 5:14 pm |
|
|
As I said in your other post. Using a divide by 1 will give you eight times more resolution than you currently have. Still, thats not going to give you your 1Hz accuracy. You could extend the accuracy a bit further by measuring several rising edges. Any fractional timer ticks would get added together instead of getting lost. |
|
|
Kenny
Joined: 07 Sep 2003 Posts: 173 Location: Australia
|
|
Posted: Sun Nov 14, 2004 9:24 pm |
|
|
As Mark said, counting over multiple edges will give a better result, but the measurement accuracy will still be limited to a few Hz at the 15000Hz end of the range.
Even with the maximum ccp prescaler of 16
setup_ccp1(CCP_CAPTURE_DIV_16);
the problem remains. Successive readings could be fed into a moving average filter to further improve things a bit.
Actually with these requirements it would probably be better to setup timer 1 as a counter and connect the input to RC0, the input for timer 1.
To get the 1Hz accuracy it would be necessary to gate the counter for at least one second and this will limit the update rate.
In the example below the gate time is done with with a delay - another timer could be used to generate the gate time.
Code: |
#bit timer1_on = 0x10.0 // T1CON bit 0
void main()
{
int16 freq;
int16 new_count;
int16 old_count;
setup_timer_1(T1_EXTERNAL);
while(1)
{
timer1_on = TRUE;
delay_ms(1000);
timer1_on = FALSE;
new_count = get_timer1();
freq = new_count - old_count;
old_count = new_count;
}
}
|
|
|
|
zio_pecos
Joined: 24 Oct 2004 Posts: 21 Location: italy
|
|
Posted: Tue Nov 16, 2004 3:53 am |
|
|
ok now that's all right
with your help i've founded a solution!!!
thank you very much
but
i want to ask you another question:
i want to capture 2 signal from ccp1 and ccp2 pins
this code is right?
Code: |
//--------------------------------------------------------------------------
// CCP1 Data
//
int16 CCP1Value;
int16 CCP1OldValue;
BOOLEAN CCP1Captured;
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// CCP2 Data
//
int16 CCP2Value;
int16 CCP2OldValue;
BOOLEAN CCP2Captured;
//--------------------------------------------------------------------------
#int_CCP1
CCP1_isr()
{
CCP1Value = CCP_1 - CCP1OldValue;
CCP1OldValue = CCP_1;
CCP1Captured = TRUE;
}
//--------------------------------------------------------------------------
#int_CCP2
CCP2_isr()
{
CCP2Value = CCP_2 - CCP2OldValue;
CCP2OldValue = CCP_2;
CCP2Captured = TRUE;
}
//--------------------------------------------------------------------------
void main() {
int16 periodo,per;
int16 periodo1,per1;
setup_counters(RTCC_INTERNAL,RTCC_DIV_256);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
setup_ccp1(CCP_CAPTURE_RE|CCP_CAPTURE_DIV_16);
setup_ccp2(CCP_CAPTURE_RE|CCP_CAPTURE_DIV_16);
enable_interrupts(INT_CCP1);
enable_interrupts(INT_CCP2);
enable_interrupts(global);
CCP1Value = 0;
CCP1OldValue = 0;
CCP1Captured = TRUE;
CCP2Value = 0;
CCP2OldValue = 0;
CCP2Captured = TRUE;
if (CCP1Captured) {
//---
// F = 1/T
// Timer1 prescaler DIV_BY_1
// Pic16C745 at 24Mz -> 4/24M= 1.66666e-7;ans*(timeprescaler/ccpprescaler)
//
periodo=((float)CCP1Value*4/(24e6)/16)*100000000;
per=periodo;
CCP1Captured = FALSE;
}
if (CCP2Captured) {
//---
// F = 1/T
// Timer1 prescaler DIV_BY_1
// Pic16C745 at 24Mz -> 4/24M= 1.66666e-7;ans*(timeprescaler/ccpprescaler)
//
periodo1=((float)CCP2Value*4/(24e6)/16)*100000000;
per1=periodo1;
CCP2Captured = FALSE;
}
| [/code]
thank you |
|
|
Kenny
Joined: 07 Sep 2003 Posts: 173 Location: Australia
|
|
Posted: Tue Nov 16, 2004 4:46 pm |
|
|
As stated before, the CCP capture approach will not be accurate at the 15kHz end of the range.
Personally I would use int32 instead of float for the scaling calculations.
Float generates a lot of code and the rounding error may be a problem.
With the CCP prescaler of 16, at 1kHz the count would be 96,000 and at 15kHz it would be 6400.
96,000 exceeds the 16 bit boundary so it is necessary to use PCM Programmer's timer 1 extension code. It works brilliantly well.
The code calculates the change in value.
Code: |
int32 freq;
while(!gc_capture_flag);
gc_capture_flag = 0;
// Limit count to within maximum for 24 bit
if (g32_ccp_delta > 15000000) g32_ccp_delta = 15000000;
// Calculate frequency from captured count
// Apply rounding by adding half denominator.
freq = ((15000 * 6400) + (g32_ccp_delta/2))/ g32_ccp_delta;
|
For the second CCP it would be necessary to duplicate t1_extend.h for it. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Wed Nov 17, 2004 6:48 am |
|
|
Well if you only have to update every 0.5 seconds, then just count the number of pulses in 0.5 seconds and multiply by 2. That will give you accuracy of 2 Hz and would be much simplier for a beginner. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|