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

Oscillator / timer0 problem (or question)

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



Joined: 11 Sep 2003
Posts: 83
Location: Warmenhuizen - NL

View user's profile Send private message

Oscillator / timer0 problem (or question)
PostPosted: Wed Nov 07, 2012 7:28 am     Reply with quote

Hi,

I ran into a problem I seem to be unable to solve on my own, even with the brilliant information found when searching this forum.

I am developing a widget that connects a I2C sensor and a 115kbps serial Bluetooth connection. I breadboarded the device using the internal oscillator at 16MHz and had no problems. Clocks were running on time. For the prototype PCB I decided to use an external oscillator running on 18.432MHz, taking into account the desired baudrate and the potential problems with the internal oscillator generating that. I recalibrated timer0 for the new clock speed and everything worked again. Until I realized that a couple of timers took longer than before. Suddenly my millisecond and second counters were off by a margin.

I took away all of the program and left only the bare basics in, and I still cannot explain what is happening. I am starting to think that I managed to miss an important bit of information regarding oscillators and timer interrupts and I hope someone can shed some light on this.

PIC: 18F14K22
compiler version : 4.136

Code:

#include <18F14K22.h>

#FUSES NOWDT                             //No Watch Dog Timer
#FUSES WDT128                            //Watch Dog Timer uses 1:128 Postscale
#FUSES ECH                               //External clock ,high power
#FUSES NOPLLEN                           //4X HW PLL disabled, 4X PLL enabled in software
#FUSES NOBROWNOUT                        //No brownout reset
#FUSES NOLVP                             //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
   
#use delay(clock=18432000)

#define RED_LED         PIN_C2         // Red LED output


#INT_TIMER0

void clock_isr(void) {

   // Hier komt het programma elke 0,1 ms
   // Eerst de timer weer ijken
   set_timer0(0xff8c);
   
   output_low(RED_LED);
   delay_us(5);
   output_high(RED_LED);
   
}



void main() {

   //
   // Init
   //
   setup_timer_0(T0_INTERNAL|T0_DIV_4);         // Internal clock, /4 prescaler, 16 bits timer default.
                                                // Clock 18.432 MHz -> timer tick every 868 ns
                                                
                                                // I want a timer0 interrupt every  0.1 ms. So I need to count 0.1ms / 868ns = 115.2 timer ticks
                                                // (0x0073).
   set_timer0(0xff8c);                           // Initialise timer0 at 0xffff - 0x0073 = 0xff8c
   
   enable_interrupts(INT_TIMER0);                       // Enable timer0 interrupt
      
   enable_interrupts(GLOBAL);                     //
   
   
                                                
   while(TRUE)
   {
      
   }

}


My question is, I expected this code to generate a 10kHz pulse on PIN_C2. Instead it generates a 8.940 KHz pulse. The clock is measured on PIN_A5 at 18.42 MHz. The /4 output on PIN_A4 is measured at 4.60 MHz. The code behaves as if there is a 16MHz oscillator at work instead of the 18.432MHz I am using. Where am I going wrong in my thinking?

Thanks and regards,


Paul
Ttelmah



Joined: 11 Mar 2010
Posts: 19466

View user's profile Send private message

PostPosted: Wed Nov 07, 2012 9:38 am     Reply with quote

Start with your timer calculation. The timer interrupts when it wraps to _0_, not 0xFFFF. So your timing is every 0x74, not 0x73 counts. One more than you calculated. 464 instructions.
Then, it takes typically 32 instructions to actually _arrive_ in the interrupt handler, after the timer triggers. One to respond to an interrupt, and probably about 4 to load the timer. When you load the timer, it starts on the _next_ instruction cycle, so I'd think about 503 instruction times or 2012 cycles of the clock. This gives a predicted output frequency of about 9.1KHz. So your output is a fraction slow, but not stupidly so....

Do a search here on the forum.

If you want an _accurate_ tick, use timer2. This uniquely on the standard timers has the ability to reset itself at a particular count. Alternatively, you can improve things significantly by _adding_ the offset to the current timer value. Reduce it by one to allow for the time taken to read/load the timer and you should be very close.

However also 'beware'. If you are doing this in your normal code with other things happening, think about how quick this all is, and how many instructions it takes. 10000Hz, is every 460 instructions. What you have posted, takes about 100 instructions for the interrupt on it's own. If you want other things to happen are you going to have time to do these as well?....

Best Wishes
Mike Walne



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

View user's profile Send private message

PostPosted: Wed Nov 07, 2012 3:57 pm     Reply with quote

I would add that if you insist on using timer0 with t0_div_by4 you might as well run it in 8 bit mode. That way you reduce the overhead of doing the arithmetic which Ttelmah has suggested. The other advantage of doing the adding (or subtracting), compared to forcing in a fixed value, is that you're not going to be so seriously affected by location of the timer0 manipulation in the ISR. However, if you've got timer2 spare then use that, it's so much simpler.

Mike
Woody



Joined: 11 Sep 2003
Posts: 83
Location: Warmenhuizen - NL

View user's profile Send private message

PostPosted: Thu Nov 08, 2012 3:22 am     Reply with quote

Mike, Ttelmah,

Thank you for the wakeup call. I think I was staring too long at my code to be able to draw the right conclusions, if any. Alarming is the fact that I used the 'adding' instead of 'setting' of TMR0 lots of times before. Whyever I decided to use set_timer0 here is not clear to me. Must be my age.

Anyway, I took your suggestions and tried to make timer0 work. That did not get me close enough, so I then decided to use timer2.

The outcome:

Code:

#include <18F14K22.h>

#FUSES NOWDT                             //No Watch Dog Timer
#FUSES WDT128                            //Watch Dog Timer uses 1:128 Postscale
#FUSES ECH                               //External clock ,high power
#FUSES NOPLLEN                           //4X HW PLL disabled, 4X PLL enabled in software
#FUSES NOBROWNOUT                        //No brownout reset
#FUSES NOLVP                             //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
   
#use delay(clock=18432000)

#define RED_LED         PIN_C2         // Red LED output

#INT_TIMER2

void clock_isr(void) {

   // Hier komt het programma elke 1 ms
   output_low(RED_LED);
   delay_us(5);
   output_high(RED_LED);
   
}



void main() {

   //
   // Init
   //
   setup_timer_2(T2_DIV_BY_16, 71, 4);            // Clock 18.432 MHz, prescaler /16 -> timer tick every 3.472us
                                                // Timer 2 counts 71, overflow every 250us, postscaler 4 -> interrupt every 1ms
                                                // There seems to be an extra timer tick somewhere, for the outcome of 250uS / 3.472 us = 72
                                                // but 71 gives a more precise interrupt.
   
   enable_interrupts(INT_TIMER2);               // Enable timer0 interrupt
      
   enable_interrupts(GLOBAL);                     //
   
   
                                                
   while(TRUE)
   {
      
   }

}


This gives me a pulse frequency of 999.6 Hz and a pulse period of 1.000ms (according to my scope). And that is more than close enough to do the timing in my application with some accuracy. Also, although not (yet) a problem, it gives me a lot more instructions between interrupts.

All this shows that timing is probably the most crucial part of setting up a microcontroller project. Part of the problem here was that along the way I decided to get a higher clock frequency and a higher interrupt rate, wihout looking at the overall impact of things. Well, the good thing is that I now finally got to setting up a spreadsheet that gives me the timing for various clock rates and timer settings.

Thanks again!

Paul
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