View previous topic :: View next topic |
Author |
Message |
Freddie
Joined: 06 Sep 2003 Posts: 49
|
Timer1 not keeping time as expected |
Posted: Tue Sep 23, 2003 3:47 pm |
|
|
I'm outputting a simple pulse, using Timer1 to keep track of time. In the code below, I would expect the pulse width to be 480 microseconds. When the code executes the pulse is actually 498 microseconds (as measured on scope). To me that is a significant error and will not work for the project I'm working on.
Is there flaw in my algorithm? Is there a more accurate way yo do this with CCS? Thanks.
#include <16f877.h>
#use delay(clock=20000000)
#fuses HS,NOWDT,PUT,NOPROTECT,NOLVP
#zero_ram
#case
int flag; //global variable
void main(void)
{
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
while(1)
{
output_high(PIN_D0);
set_timer1(65535-(480*5)); //480microsecond pulse.
flag = 0;
while(flag == 0);
{
}
output_low(PIN_D0);
delay_ms(10); //pause between pulses.
}
} //end main
#INT_TIMER1
void my_timer1_intRoutine(void)
{
flag = flag + 1;
if(flag >=250) //for safety, as to not overflow
{
flag = 1;
}
} |
|
|
TSchultz
Joined: 08 Sep 2003 Posts: 66 Location: Toronto, Canada
|
|
Posted: Tue Sep 23, 2003 4:21 pm |
|
|
I think the extra time is coming from the interrupt service routine and your interrupts code. You set the output, then start the timer, when the timer expires you increment the flag, then after the interrupt is handled you clear the output.
What is most important for your application, the timing of the pulse, or the delay between pulses?
In your example you fire one pulse, then wait 10mS. Is this what you want to do? If you can afford to waste the time you may want to simply do this;
output_high( PIN_D0 );
delay_us( 480 );
output_low( PIN_D0 );
delay_ms( 10 );
There are a few ways to accomplish the timing, each with their pros and cons. Can you describe your needs a bit further so someone can suggest alternatives for you. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Sep 23, 2003 4:23 pm |
|
|
You start the timer, then wait in a tight loop and poll the flag.
But when an interrupt occurs, the CCS interrupt dispatcher code
has to be executed. Then it jumps to your ISR, and finally it jumps
back to the exit code within the interrupt dispatcher. This is quite
a few instructions and will account for most of the delay that you're
seeing.
One quickie fix would be to get rid of the ISR and the interrupts,
and just poll the hardware interrupt flag for Timer1 instead of the
software flag. |
|
|
Freddie
Joined: 06 Sep 2003 Posts: 49
|
|
Posted: Tue Sep 23, 2003 5:48 pm |
|
|
Thanks PCMprogrammer and TSchultz. I can see how the ISR is adding the extra time. My critical parameter is the pulse, not the delay between pulses.
PCMprogrammer suggested polling the "hardware interrupt flag" for Timer1. What register address should I poll?
TSchultz suggested:
output_high( PIN_D0 );
delay_us( 480 );
output_low( PIN_D0 );
delay_ms( 10 );
The problem I had with this is that my time delay is a variable (I did not make this clear in my original post). Per the CCS manual, if passing a variable to delay_us and delay_ms must they must be an 8-bit integer (1-255). So I could not use this approach since I need to create variable pulse widths up to 1100us. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Sep 23, 2003 6:55 pm |
|
|
PCMprogrammer suggested polling the "hardware interrupt
flag" for Timer1. What register address should I poll?
The Timer1 interrupt flag is bit 0 of the PIR1 register.
For the 16F877, you could define it like this:
#bit TMR1IF = 0x0C.0
Then get rid of this code:
flag = 0;
while(flag == 0);
{
}
and replace it with this:
TMR1IF = 0;
while(TMR1IF == 0); // Wait here until interrupt flag goes high |
|
|
Hans Wedemeyer
Joined: 15 Sep 2003 Posts: 226
|
|
Posted: Wed Sep 24, 2003 10:03 am |
|
|
As you are running at 20MHz and the time period is as long as 480uS it should be possible to do your work in the ISR.
I did something similar 1mS and 19mS and it wrkds just fine.
Try this... it is untedted, however I think it's reasonable.
Hans W
#include <16f877.h>
#use delay(clock=20000000)
#fuses HS,NOWDT,PUT,NOPROTECT,NOLVP
#zero_ram
#case
//global variables
long T480Value;
long T10msValue;
int flag;
/////////////////////////////////////////////////////////////////
// ____ ____
// PIN_D0 ____| |_________________________| |_________
// 480uS Aprox. 10mS
//
#INT_TIMER1
void my_timer1_intRoutine(void)
{
if ( flag == 0 )
{
set_timer1( T480Value ); // high
output_high(PIN_D0);
flag++;
}
else
{
output_low(PIN_D0); // low end of 480 microsecond pulse.
flag++ // flag is now 2
set_timer1( T10msValue ); //reload the timer for 500 uSec
if ( flag >= ) // 10mS / 500uS = 20
{
flag = 0
}
}
}
//////////////////////////////////////////////////
//
void main(void)
{
T480Value = 65536 - (0.00048/(4/20000000)); // the 480 uS value
T10msValue = 65536 - (0.0005 /(4/20000000); // the 500 uS value
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
enable_interrupts(INT_TIMER1);
set_timer1(T480Value); //480microsecond pulse.
enable_interrupts(GLOBAL);
output_low(PIN_D0); // start with pin low
flag = 0;
while(1)
{
}
} //end main |
|
|
Freddie
Joined: 06 Sep 2003 Posts: 49
|
|
Posted: Wed Sep 24, 2003 1:26 pm |
|
|
Thanks PCMprogrammer,
I like the idea of polling the Timer1 Interrupt Flag. Using the code below on an actual PIC and simulating it in MPLAB, TMR1IF never goes to 1. Timer1 is counting and rolling over though. Any ideas why?
#include <16f877.h>
#use delay(clock=20000000)
#fuses HS,NOWDT,PUT,NOPROTECT,NOLVP
#zero_ram
#case
int flag; //global variable
void main(void)
{
#bit TMR1IF = 0x0C.0
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
while(1)
{
output_high(PIN_D0);
set_timer1(65535-(480*5)); //480microsecond pulse.
TMR1IF = 0;
while(TMR1IF == 0); // Wait here until interrupt flag goes high
output_low(PIN_D0);
delay_ms(10); //pause between pulses.
}
} //end main
#INT_TIMER1
void my_timer1_intRoutine(void)
{
} |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Wed Sep 24, 2003 1:51 pm |
|
|
Yeah, because you still have the interrupt handler there!
Code: |
#include <16f877.h>
#use delay(clock=20000000)
#fuses HS,NOWDT,PUT,NOPROTECT,NOLVP
#zero_ram
#case
#bit TMR1IF = 0x0C.0
void main(void)
{
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
while(1)
{
output_high(PIN_D0);
set_timer1(65535-(480*5)); //480microsecond pulse.
TMR1IF = 0;
while(TMR1IF == 0); // Wait here until interrupt flag goes high
output_low(PIN_D0);
delay_ms(10); //pause between pulses.
}
} //end main
|
Regards,
Mark |
|
|
Freddie
Joined: 06 Sep 2003 Posts: 49
|
|
Posted: Wed Sep 24, 2003 2:01 pm |
|
|
Ah yes, now it works perfectly!
Thanks to all that responded. |
|
|
Hans Wedemeyer
Joined: 15 Sep 2003 Posts: 226
|
|
Posted: Wed Sep 24, 2003 3:14 pm |
|
|
If all the chip has to do is generate a pulse this code may be fine,
The main problem with this type of code is, the moment you need to do something else, the 480uS goes to hell...
With the solution I provided the while(1) (;) in main leaves your program open to handling other tasks, and the the pulse train will continue as expected.
hansw
#include <16f877.h>
#use delay(clock=20000000)
#fuses HS,NOWDT,PUT,NOPROTECT,NOLVP
#zero_ram
#case
int flag; //global variable
void main(void)
{
#bit TMR1IF = 0x0C.0
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
while(1)
{
output_high(PIN_D0);
set_timer1(65535-(480*5)); //480microsecond pulse.
TMR1IF = 0;
while(TMR1IF == 0); // Try to do somethinghere and the 480uS goes to hell... this is not a good idea... !
output_low(PIN_D0);
delay_ms(10); //pause between pulses.
}
} //end main |
|
|
Kenny
Joined: 07 Sep 2003 Posts: 173 Location: Australia
|
|
Posted: Wed Sep 24, 2003 5:27 pm |
|
|
I like the hardware approach using the compare features of the CCP modules.
Just tested the code below, pulse was 479uS, time between pulses 10.0017mS.
#include <16F877.h>
#use delay(clock=20000000)
#fuses HS,NOWDT,PUT,NOLVP
#define PULSE PIN_D0
#int_CCP1
CCP1_isr() {
output_bit(PULSE,0);
}
#int_CCP2
CCP2_isr() {
output_bit(PULSE,1);
}
void main(void)
{
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); // 0.2uS increments
setup_ccp1(CCP_COMPARE_INT);
setup_ccp2(CCP_COMPARE_RESET_TIMER);
enable_interrupts(INT_CCP1);
enable_interrupts(INT_CCP2);
enable_interrupts(global);
set_timer1(0);
output_bit(PULSE,1);
CCP_1 = 480*5;
CCP_2 = (10000 + 480)*5;
while (1);
} |
|
|
Hans Wedemeyer
Joined: 15 Sep 2003 Posts: 226
|
|
Posted: Wed Sep 24, 2003 8:32 pm |
|
|
what a waste of resources...
About as good as a sledge hammer for cracking.
One simple TIMER1 ISR fixes all the problem... |
|
|
|