View previous topic :: View next topic |
Author |
Message |
smiles
Joined: 20 Feb 2008 Posts: 33
|
1 second with Timer1 |
Posted: Wed Mar 12, 2008 9:47 am |
|
|
I intend to measure frequency using method: counting the number of low-to-high pulses in 1 second.
Use this code
Code: |
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1); // Start timer 1
// set_timer1(0x0BDB); //this sets timer1 register to 0
setup_ccp1(CCP_CAPTURE_RE); // Configure CCP1 to capture rise
enable_interrupts(INT_CCP1); // Setup interrupt on falling edge
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
|
My crystal is 4MHz so each internal instruction takes 1us, I use T1_DIV_BY_1 then interrupt #int_timer1 happens after 65.536us
I need 1s=1000000us so I need 16 times to do this
1000000=16*(65536-3036)
caused 3036 equivalent to 0x0BDC
so I did this code for interrupt
Code: |
#int_timer1
void timer1_isr()
{
set_timer1(0x0BDC);
i++;
if(i==16)
{
freqRes=count;
count=0;
}
}
#int_ccp1
void CCP1_isr()
{
count++;
}
|
Now test with simulate tool, I embed 50Hz then got 53Hz (100Hz then 106Hz, 150 Hz then 160Hz)
Could you show me where I am wrong ?
Thanks !!! |
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
Re: 1 second with Timer1 |
Posted: Wed Mar 12, 2008 11:11 am |
|
|
smiles wrote: | I intend to measure frequency using method: counting the number of low-to-high pulses in 1 second.
Could you show me where I am wrong ?
Thanks !!! |
Read this thread describing how to measure time with timer1 accurately.
http://www.ccsinfo.com/forum/viewtopic.php?t=26177
You can probably configure timer2 to interrupt an exact number of times per second without loading the timer value. Use timer1 to count falling edges. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
Re: 1 second with Timer1 |
Posted: Wed Mar 12, 2008 11:49 am |
|
|
smiles wrote: |
Code: |
#int_timer1
void timer1_isr()
{
set_timer1(0x0BDC);
i++;
if(i==16)
{
freqRes=count;
count=0;
}
}
|
|
The problem is you did not allow for processing overhead. When you get a Timer 1 interrupt, the Timer 1 has just then wrapped around to 0000. By the time you get to your set_timer1(0x0BDC), it is probably already 12 microseconds later, and maybe more depending on what other interrupts are enabled and whether or not you ever disable interrupts, even for a short time. In general, it is a bad idea to count on precise interrupt overhead, because it can change so easily. It would be better to let Timer 1 run free. After 16 overflows, the time elapsed will be 1.048576 seconds. So whatever count you get, divide it my 1.048576 to get the frequency.
Robert Scott
Real-Time Specialties |
|
|
smiles
Joined: 20 Feb 2008 Posts: 33
|
|
Posted: Thu Mar 13, 2008 1:14 am |
|
|
Thanks Scott, the result is better
Code: |
int i=0;
int32 count=0,freqRes;
char freqChar[10];
float freqNum;
char words[]="THE VALUE OF FREQUENCY: ";
#int_ccp1
void isr()
{
count++;
}
//**********************
#int_timer1
void isr_timer()
{
i++;
if(i==16)
{
setup_timer_1(T1_DISABLED);
freqNum=(float)count/1.048576;
}
}
void main()
{
// TODO: USER CODE!!
// int16 adc_value;
// float volts;
// int16 voltsRes;
// char voltsChar[12];
InitPic();
InitLcd();
Speech(words,0x00);
delay_ms(1000);
do
{
if(freqNum!=0)
{
WrCmd2Lcd(lcd_clr);
delay_ms(100);
freqRes=ceil(freqNum);
itoa(freqRes,10,freqChar);
Speech(freqChar,0x00);
delay_ms(1000);
freqNum=0;
i=0;
count=0;
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1); // Start timer 1
set_timer1(0); //this sets timer1 register to 0
}
}
while(1);
}
void InitPic()
{
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
output_b(0x00);
output_d(0x00);
output_c(0x00);
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_8);
set_adc_channel(0);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1); // Start timer 1
set_timer1(0); //this sets timer1 register to 0
setup_ccp1(CCP_CAPTURE_RE); // Configure CCP1 to capture rise
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_CCP1); // Setup interrupt on falling edge
enable_interrupts(GLOBAL);
}
|
Hi Neutone, I will try it |
|
|
smiles
Joined: 20 Feb 2008 Posts: 33
|
|
Posted: Thu Mar 13, 2008 3:50 am |
|
|
Hi I try 1second from this topic link above
embed 50Hz then result of simulation is 49,52,53... ???
Code: |
//RTC VARIABLES
#define XTAL_FREQUENCY 4000000
#define TIMER1_FREQUENCY (XTAL_FREQUENCY / 4) // 1 clock tick = 1 instr. cycle = crystal frequency / 4
int32 Ticker;
int8 seconds=0;
// GUIDED WORDS AND VARIABLES
int32 freqCount=0,freqStore;
char freqChar[10];
char words[]="THE VALUE OF FREQUENCY: ";
//************************
void InitCCP1(void)
{
setup_ccp1(CCP_CAPTURE_RE); // Configure CCP1 to capture rise
enable_interrupts(INT_CCP1); // Setup interrupt on falling edge
}
//************************
#int_TIMER1
void TIMER1_isr()
{
Ticker -= 65536; // Decrement ticker by clocks per interrupt
if ( Ticker < 65536 ) // If second has expired
{
Ticker += TIMER1_FREQUENCY; // Increment ticker by clocks per second
seconds++; // Increment number of seconds
}
}
//*************************
#int_CCP1
void CCP1_isr()
{
freqCount++;
}
//*************************
void main()
{
// TODO: USER CODE!!
int8 prevSeconds;
InitLcd();
InitRTC();
InitCCP1();
Speech(words,0x00);
delay_ms(1000);
enable_interrupts(GLOBAL);
while(1)
{
if (seconds != prevSeconds)
{
prevSeconds = seconds;
freqStore=freqCount; //I think there are problem with my freqCount value, should I let it increase freely like Timer1
freqCount=0;
itoa(freqStore,10,freqChar);
WrCmd2Lcd(lcd_clr);
delay_ms(100);
Speech(freqChar,0x00);
}
}
}
|
|
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Mar 13, 2008 11:34 am |
|
|
smiles wrote: |
Hi I try 1second from this topic link above
embed 50Hz then result of simulation is 49,52,53... ???
| The algorithm from the linked thread is very accurate in the long term but it is not very accurate for timing a single second interval (in your implementation at 4MHz about 2% deviation).
In your original program you had: Code: | #int_timer1
void timer1_isr()
{
set_timer1(0x0BDC);
i++;
if(i==16)
{
freqRes=count;
count=0;
}
} | As Robert Scott already pointed setting the timer this way is not very accurate because you never know how much time has passed when your function is entered. Instead of the floating point calculation there is another method with much less overhead: Code: | #int_timer1
void timer1_isr()
{
set_timer1( get_timer1() + 0x0BDC);
i++;
if(i==16)
{
freqRes=count;
count=0;
}
} | A small inaccuracy in this code is introduced by the fact that Timer1 will continue counting in between the instructions for reading the timer and setting it again. This can be improved by slowing down timer1 (use DIVIDE_BY_16 instead of dividing by 1) or by adjusting the offset value.
A more elegant solution was suggested by Neutone; use Timer2 instead of Timer1. The beauty of Timer2 is that it has a programmable hardware compare register so it won't reset at the overflow from 0xFF to 0x00 but at any value you specify. This means you don't have to load the offset value again on every interrupt, resulting in a higher accuracy with less code.
Code: | // Assuming 4MHz crystal.
// 4Mhz = 1 million instruction ticks
// In order to detect an exact 1 second interval we set the prescaler to 16,
// the comparator to 250 and postscaler to 10. This way we get an interrupt
// exact every 0.04 sec. Counting 25 of these is 1 second. (25 * 16 * 250 * 10 = 1 million)
// Note: other value combinations are possible to achieve the same result, just choose a combination you like.
setup_timer_2( T2_DIV_BY_16, 250, 10);
.
.
.
#int_timer2
void timer2_isr()
{
i++;
if (i==25)
{
freqRes=count;
count=0;
}
} |
|
|
|
smiles
Joined: 20 Feb 2008 Posts: 33
|
|
Posted: Fri Mar 14, 2008 1:48 am |
|
|
Very thanks !!! I will continue to try it |
|
|
|