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 CCS Technical Support

Timer0 calculation question

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



Joined: 20 Dec 2009
Posts: 23

View user's profile Send private message

Timer0 calculation question
PostPosted: Sat Jan 02, 2010 10:23 am     Reply with quote

Hi folks,

I’m new to CCS C and have a timer0 problem

Inside an ISR I want to generate a 10 uSec square wave with a .1 uSec system clock and a /2 prescaler. I’m using an 18f2320

10 uSec / .1 = 100 cycles
100 cycles /2 = 50 cycles
50 cycles + 2 = 52 cycles (timer set overhead)
65536 - 52 = 65484 cycles

Running the code below gives me a time of 15.1 uSec instead of 10 uSec.

When I delete the Set_timer statements and just let the timer overflow I get 65536 *2 *.1 = 13.1 mSec, exactly what I should get, so I know my oscilloscope is calibrated.

I’ve replaced my entire Main program with ' loop: goto loop; ' And the problem does not change.

I’ve tried using #USE fast_io ( c ), setting the TRIS_c register manually, and using output_pin (C0,1) but I get no output at all. If using #USE fast_io would solve the problem how would I use it correctly?

Any idea what’s wrong? Am I making a really stupid math mistake or is this some kind of compiler or ISR overhead problem? I know this example is unrealistic but It illustrates my problem

If this is an overhead issue is there a workaround? I could simply subtract 5.1 uSec from what ever time I wish to generate and do other things to fine tune the workaround. The error seems to be very constant, even with interrupts going off.


Thanks, Al

==============================================
Code:


#use delay(clock=40MHZ, crystal=10MHZ) .1 //uSec cycle time due to the PLL multiplier

==========

#int_timer0
void Timer0_isr() 
   {
   static int1 i;
   if (++i)   //switch modes each pass
       {
       set_timer0(65484); 
       output_high(PIN_C0);     
       }
   else
      {
      set_timer0(65484);
      output_low(PIN_C0);
      }
  }
 
===========
setup_timer_0(T0_INTERNAL|T0_DIV_2);
Ttelmah
Guest







PostPosted: Sat Jan 02, 2010 11:30 am     Reply with quote

Seriously, do it another way.....
Use the PWM.

Interrupts are a lot of work. The compiler has to add a lot of extra code you don't see. It saves all the registers in use, then tests to see which interrupt triggered, then calls the routine. After the routine, it has to restore the saved registers, before finally returning...
There are typically perhaps 60-70 instructions of overhead for an interrupt handler. In your case, you have the extra code, to increment a value (perhaps five instructions), test it (another couple), output a pin (unless you have fast_io selected, three instructions, update the two bytes to the timer register (several more....).
Response to an interrupt, occurs on the following instruction to where the trigger occurs, and itself takes two instruction times, as does the return.

Fast_io, will improve things by two instruction times. Remember that _you_ need to set the pin to be an output, in the main, before this will work.

Now, your timing, actually says that it has taken 51 instruction times to get to the point where you _finish_ setting the timer. Since this includes the three instruction times (one for the advance to the next instruction, two for the call), to respond to the interrupt, and then the 35 or so to do the saving, and the time to increment the counter, test the value etc., his is about right.
You can get rid of 90% of the overhead, by writing your own int_global. This then replaces the compilers saving, but requires that _you_ ensure that you save/restore the registers required, clear the interrupt etc.. Even with this, the code won't give the required time, since you will still have these lesser overheads, plus the time takes to update the timer register. You can improve things a fraction more, by moving the setting to the beginning of the routine, rather than having it after the increment and test....
There is an example with the compiler 'EX_GLINT.C', showing how to use the global interrupt.

However even with the global approach, the chip will be spending perhaps 30%-40% of it's available time, just to do this one job, and it'll need careful tweaking to get the timing right. using any other interrupt, will destroy the timing, and while this pulse is being generated, any code using delays, will give wrong results.

The PWM, will give you an exact time, and involve no processor work at all....
This is why the hardware is there.

Best Wishes
bkamen



Joined: 07 Jan 2004
Posts: 1615
Location: Central Illinois, USA

View user's profile Send private message

PostPosted: Sat Jan 02, 2010 1:12 pm     Reply with quote

If he's only using 1 IRQ, I think Timer0 can be a FAST interrupt which only has like a 3Tcy delay. I'd have to check the Data sheet again for that part.

But there will still be some Jitter (not 50% duty) because of the instructions to check for turn off vs. turn on. (you know - follow the flow and ding every memory location for one instruction past the other!)

BUT, I agree -- Use the PWM, NO overhead!!
_________________
Dazed and confused? I don't think so. Just "plain lost" will do. :D
Ttelmah
Guest







PostPosted: Sat Jan 02, 2010 3:35 pm     Reply with quote

There is no difference on modern compilers, between using 'fast', and using int_global. Fast tells the compiler to use the hardware saving operation for the three main registers, but it does this already for the normal interrupts, if 'high' is not in use. Look at the start of the default handler routine.
Fast is one of the CCS instructions that has become semi-redundant. It only becomes useful, if you want to write your own 'int_global' for the standard interrupts, and use a high priority interrupt as well, with the fast return stack, being used by the standard interrupts. It allows this to be setup, otherwise the compiler will automatically use the fast stack for the high priority interrupts if they are enabled.
I was assuming the fast return stack, would be used if the author went DIY, I'd still expect the total 'time' to be in the order of 30 instructions, by the time the timer update, increment, test, branch, I/O, clearing the interrupt etc., had been done. It would work, but would cripple many other functions on the processor...

Best Wishes
bkamen



Joined: 07 Jan 2004
Posts: 1615
Location: Central Illinois, USA

View user's profile Send private message

PostPosted: Sat Jan 02, 2010 3:49 pm     Reply with quote

Not necessarily.

The ISR (w/fast return stack) can tolerate no context saving as long as the user does something simple without references or what (I think no using the FSR is one example)

i.e. twiddle a port bit or something. (which is what I was doing)

In the meantime, you can STILL have other interrupts running in the same system subject to that much larger ISR delay.

I did a project with several ISR's running but only one of them tied to VectorAddr 0x0008 (high priority) to do some really tightly timed sampling/controlling... while I still had other lower priority ISR's (like INT_RDA) running through VectorAddr 0x0018.

So my code (in compiled form) looking like a single jump at 0x0008h to my fast ISR while at 0x0018 had the normal CCS ISR handler that jumped off to all my other routines.

Worked great.

Maybe I'm thinking of something different.

-Ben

EDIT: I think technically HIGH or LOW priority IRQ's can use the FAST interrupt stack - but only if there's 1 ISR. If there's more than one source for IRQ's is enabled, then code to check which one must exist and to simplify it in my mind, I just say "all bets are off" at that point without diving in deep to DIY an INT_GLOBAL.
_________________
Dazed and confused? I don't think so. Just "plain lost" will do. :D
Ttelmah
Guest







PostPosted: Sun Jan 03, 2010 4:56 am     Reply with quote

Exactly.
Fast effectively behaves exactly like int_global, but for the high priority interrupt. The fast return stack is used by int_global, if no high priority interurpt is selected, so with one interrupt it is 'redundant'. Smile

There is also though a PIC 'caveat', when using 'FAST', which is where (for instance), you setup one high priority interrupt using 'fast', and a number of low priority interrupts. The PIC hardware, always defines the standard 'INT0' interrupt as high priority, if any high priority interrupt is enabled (there is no 'priority' bit for this interrupt), which means that if this interrupt is in use, the 'fast' handler, will have to have the extra code added to test for this interrupt as well.....

Best Wishes
Allan



Joined: 20 Dec 2009
Posts: 23

View user's profile Send private message

PostPosted: Sun Jan 03, 2010 11:33 am     Reply with quote

Ben , Ttelmah, Thanks for your help.

Looks like PWM is the way to go, but I'm going to experiment with the FAST function just learn what kind of improvement I could expect.

Thanks again,

Al
Ttelmah
Guest







PostPosted: Sun Jan 03, 2010 1:54 pm     Reply with quote

Use #int_global instead.
Look at the GLINT example, which shows you how to do this.
FAST gains you nothing, with a single interrupt, and the same jobs have to be done in the global int, or the fast int handler.
Fast adds slight extra complexity, which is not needed here....

Best Wishes
Allan



Joined: 20 Dec 2009
Posts: 23

View user's profile Send private message

PostPosted: Sun Jan 03, 2010 3:53 pm     Reply with quote

Ttelmah,

Sorry about that - I didn't mean to imply that I was ignoring or misunderstood your advice.

I've done enough work in assembly that I should be able to get #int_Global working, and this is my first project with CCP and PWM hardware available so I want to experiment with that also.

Later, I'm going to sit down and carefully review everything in this thread. Lots of new stuff, all of it interesting!

Thanks again, Al
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

Another way to generate square waves with timer0.
PostPosted: Sat Jan 09, 2010 12:11 pm     Reply with quote

With your Timer0 scheme, changing code at the start of the interrupt routine affects timings. Another way to set Timer0 is to add an offset to its current value. This method is immune to such code changes. The offset is calculated from the desired period plus a correction to allow for the time taken to stop Timer0, read it, do the addition and re-load & re-start Timer0. You have to be careful to avoid differences in timing between alternate paths through the code. I have used this technique, there is still some residual jitter due to latency etc, it does work and can be a useful alternative.

I agree PWM is the way to go. PWM also has an extra feature. The duty ratio can be tweaked to within a clock period (i.e. quarter of a machine cycle). So you could, for instance, set Timer2 to divide by and odd number and still get 50% duty ratio.

Mike Walne
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