CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Timer1 not keeping time as expected

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Freddie



Joined: 06 Sep 2003
Posts: 49

View user's profile Send private message

Timer1 not keeping time as expected
PostPosted: Tue Sep 23, 2003 3:47 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Sep 23, 2003 4:21 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Sep 23, 2003 4:23 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Sep 23, 2003 5:48 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Sep 23, 2003 6:55 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 24, 2003 10:03 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 24, 2003 1:26 pm     Reply with quote

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

View user's profile Send private message Send e-mail

PostPosted: Wed Sep 24, 2003 1:51 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 24, 2003 2:01 pm     Reply with quote

Ah yes, now it works perfectly!

Thanks to all that responded.
Hans Wedemeyer



Joined: 15 Sep 2003
Posts: 226

View user's profile Send private message

PostPosted: Wed Sep 24, 2003 3:14 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 24, 2003 5:27 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 24, 2003 8:32 pm     Reply with quote

what a waste of resources...
About as good as a sledge hammer for cracking.

One simple TIMER1 ISR fixes all the problem...
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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