Jump to content
Sign in to follow this  
ra68gi

What Value Do I Load Onto Tmr0 ?

Recommended Posts

Hi Guys,

I was writing a code for a clock project using timer0. This project is for my thread "programming pic micro using Boostc". The chip I am using is Pic16F72 with 4mhz crystal. It will display seconds minutes & hours. Two switches connected to portc is used for setting the clock. The prescaler is assigned to wdt, so the timer0 increments every clock cycle ie. 1micro second. This subject has already been discussed in previous threads. Yet i need to get more information. Now i need to measure 250micro seconds. Initially i load the timer0 with 6 and start the timer. Then in interrupt service routine my logic says i need to add 6 to the timer zero value to the already elapsed time in the timer0 register ( this elapsed time is due to the latency in the interrupt hander to save all sfr and then enter the service routine). I also realise from reading the data sheet of microchip that every write operation into tmr0 will cause a delay of 2 clock cycles, which in our case is 2micro seconds. So in theory i need to add 2 more into timer, which equals a value of 8.

But when i added 8 into the tmr0 and stepped thro' the code in the simulator i realised that the timer0 register value was much different then i anticipated.

The typical value on tmr0 on entering the ISR was 16. Now inorder to get the exact increment i have used tmr0+=2;. on executing this line of c code on the simulator the tmr0 shows 22. Now i have the following questions.

 

1. Does the compiler automatically correct for the addition into timer0 ?

2. Is the value shown by the tmr0 register in the simulator accurate?

3. Will the time taken (latency time) to enter the ISR be always same in a given code ?

 

My code..

 

/*This code may initially look more convoluted with the function
sec(), min(),hrs(),but you will soon see how advantagious it would
be to call them only when the data gets updated & rest of the time 
we simply display the old value of data.By doing this we get a 
flicker free display. 
*/
#include <system.h>

// Set configuration word 

#pragma CLOCK_FREQ 4000000
#pragma DATA _CONFIG, _XT_OSC & _WDT_OFF & _CP_OFF & _PWRTE_OFF

/*all the below variables are made global for easy access in
our functions.*/

bit updatesec;
bit updatemin;
bit updatehrs;
unsigned char seconds;
unsigned char minutes;
unsigned char hours;
unsigned char unitsec;
unsigned char tensec;
unsigned char unitmin;
unsigned char tenmin;
unsigned char unithrs;
unsigned char tenhrs;

//timer0 interrupt service routine.	
void interrupt( void )
{
unsigned char tick;
unsigned char tick1;
unsigned char seconds;
unsigned char minutes;
unsigned char hours;
   tmr0 +=2;
   tick++;
   if(tick>19)
   {
   tick=0;
   tick1++;
   if(tick1>199)
   {
   tick1=0;
   seconds++;
//a flag to indicate that seconds has been updated    
   updatesec=1;   
   if(seconds>59)
   {
   seconds=0;
   minutes++;
//a flag to indicate that minutes has been updated
   updatemin=1;
   if(minutes>59)
   {
   minutes=0;
   hours++;
//a flag to indicate that hours has been updated 
   updatehrs=1;
   if(hours>23)
  {
   hours=0;
 }
}
  }
 }
}
intcon.2=0;  //clear TMR0 overflow flag
}

//function to get segment values.
unsigned char get_value(unsigned char y)
{
unsigned char segments[10]={0xDE,0x50,0xE6,0xF4,0x78,0xBC,0xBE,0x54,0xFE,0xFC};
return segments[y];
}


//a function to get tens & units place for seconds.
void sec(void)
{
  tensec = get_value(seconds/10);
  unitsec= get_value(seconds%10);
}

//a function to get tens & units place for minutes.
void min(void)
{
  tenmin = get_value(minutes/10);
  unitmin = get_value(minutes%10);
}

//a function to get tens & units place for hours.
void hrs(void)
{
  tenhrs = get_value(hours/10);
  unithrs = get_value(hours%10);
}   



//main program starts here.	
void main()
{

unsigned char a;
trisb = 0;		//configure port B
portb = 0;		//clear port B
//portc.7 & portc.6 are made as inputs & the rest as outputs.	
   trisc = 0b11000000;  
portc = 0;

   while(1)
   {
//set time	
   delay_s(1);
//if both switches are closed start clock.    
   if(portc.6 == 0 && portc.7 == 0)
   {
    goto clock;
     }
//if switch in portc.6 is pressed increment minutes      
   if(portc.6 == 0)
   {
    minutes++;
    if(minutes>59)
    {
     minutes=0;
      }
    }
//call function min() to get units & tens value for minutes.     
   min();
   for(a=0;a<250;a++)
   {
    portb = unitmin;
    portc.2=1;
    delay_ms(2);
    portc.2=0;
    portb = tenmin;
    portc.3=1;
    delay_ms(2);
    portc.3=0;
     }
//again check for both switches to be closed.
    if(portc.6 == 0 && portc.7 == 0)
    {
     goto clock;
      }
//if switch in portc.7 is pressed increment hours.       
    if(portc.7 == 0)
    {
     hours++;
     if(hours>23)
     {
      hours=0;
       }
     }
//call function min() to get units & tens value for hours.      
    hrs();
    for(a=0;a>250;a++)
    {
     portb = unithrs;
     portc.4=1;
     delay_ms(2);
     portc.4=0;
     portb = tenhrs;
     portc.5=1;
     delay_ms(2);
     portc.5=0;
     }
    if(portc.6 == 0 && portc.7 == 0)
    {
     goto clock;
       }
}

//start clock
clock:

    updatesec=1;
    updatemin=1;
    updatehrs=1;
    
//assign prescaler to wdt & start tmr0.
option_reg = 8;	

   // enable interrupts
intcon.5=1; //enable tmr0 overflow interrupt     
intcon.7=1; //enable global interrupt
//set tmr0 to genterate interrupt at every 250us.	
tmr0=6;    


while( 1 )   //endless loop
{
   if(updatesec==1)  //check for update flag,if set
{
updatesec=0;  //clear the flag & call function sec
sec();        // to get units & tens value 
}             //for seconds.
for(a=0;a<60;a++)
{
portb = unitsec;
portc.0=1;
}
portc.0=0;
for(a=0;a<60;a++)
{
portb = tensec;
portc.1=1;
}
portc.1=0;
if(updatemin==1)
{
updatemin=0;
min();
}
for(a=0;a<60;a++)
{
portb = unitmin;
portc.2=1;
}
portc.2=0;
for(a=0;a<60;a++)
{
portb = tenmin;
portc.3=1;
}
portc.3=0;
if(updatehrs==1)
{
updatehrs=0;
hrs();
}
for(a=0;a<60;a++)
{
portb = unithrs;
portc.4=1;
}
portc.4=0;
for(a=0;a<60;a++)
{
portb = tenhrs;
portc.5=1;
}
portc.5=0;
 } 
}  

 

Regards,

Raghunathan.

Share this post


Link to post
Share on other sites
I also realise from reading the data sheet of microchip that every write operation into tmr0 will cause a delay of 2 clock cycles, which in our case is 2micro seconds. So in theory i need to add 2 more into timer, which equals a value of 8.

 

You mean subtract, an extra delay adds more time than you want so you would end up with 2mS every time you reload the timer.

 

Btw why are you using a goto in beginner code? And why are you not resetting the timer inside the

ISR? Your clock code to me seems like it will desync very quickly due to its organization irrespective

of what the simulator tells you your timing is since you do not reload your timer in your ISR. You could

frequently be 10+mS out between when you exit your ISR and restart your Timer.

Share this post


Link to post
Share on other sites
1. Does the compiler automatically correct for the addition into timer0 ?
No.
2. Is the value shown by the tmr0 register in the simulator accurate?
If you are talking about the SourceBoost simulator then the answer is Yes - best to ultimately check on the hardware :)
3. Will the time taken (latency time) to enter the ISR be always same in a given code ?

No, it depends on when the interrupt occurs and what the code is doing. If you disable interrupts in the code it will increase the latency.

 

The latency is probably much higher that you expect because of the code the compiler adds to do context saving.

 

Regards

Dave

Share this post


Link to post
Share on other sites

The secret is that timer0 is for making general delays. The problem is that there is no way to gaurantee what time you will reload the counter. Use timer2 and set the timer cycle to 250-1. This way the timer will interrupt at regular intervals and you wont have to apply rocket science to the problem.

 

The other thing to note is that crystals tend to not be very accurate so you will find that your clock drifts (maybe a few seconds a day). Most crystals are 20 parts per million accurate so your time base may not be exactly 4 MHz. I wrote some code that has a digital solution to the problem using the variable TickAdjust. Part of the code below implements a clock using a 20MHz crystal so you will need to divide the prescaler value by 5 for 4 MHz (250/5=50). So instead of 250-1 use 50-1.

 

As usual, I cut and pasted this code so the tabs are all messed up. You can grab clock.c and clock.h from CBLib.zip from my web site:

 

http://www.geocities.com/ted_rossin/Electronics/Pic/Pic.html

 

I should be more clear about this. Setting TickAdjust to -1 will add a 1ms delay every 256 seconds to the clock. Setting it to -2 will slow the clock down by 2ms every 256 seconds. Likewise, setting TickAdjust to 3 will speed the clock up by 3ms every 256 seconds. With this control, it is possible to tune the clock up to be very accurate.

 

Make sure you put the 22pF caps on your crystal or you'll have no chance of it working well.

 

void interrupt( void )
{
   // Handle timer2 interrupt
   if(test_bit(pir1,TMR2IF)){ 
       clear_bit(pir1,TMR2IF);
                
       GlobalTick100us++;
       if(GlobalTick100us==10){ 
    ClockCurrentTime.Tickms++;
    GlobalTick100us = 0;
    if(ClockCurrentTime.Tickms==1000){
        ClockCurrentTime.Second++;
       LocalSec++;   // Just a free running 8 bit counter.
        if(TickAdjust<0){
                       if(LocalSec <= -TickAdjust){
                           ClockCurrentTime.Tickms = 0xffff;  // Slow down clock
                       }
                       else{
                           ClockCurrentTime.Tickms = 0;
                       }
        }
        else{
                       if(LocalSec<=TickAdjust){
                           ClockCurrentTime.Tickms = 1;  // Speed up clock
                       }
                       else{
                           ClockCurrentTime.Tickms = 0;
                       }
        }
        if(ClockCurrentTime.Second==60){
            ClockCurrentTime.Second = 0;
            ClockCurrentTime.Minute++;
            if(ClockCurrentTime.Minute==60){
                ClockCurrentTime.Minute = 0;
                ClockCurrentTime.Hour++;
                if(ClockCurrentTime.Hour==24){                    ClockCurrentTime.Hour = 0;
        ClockCurrentTime.Day++;
                }
            }
        }
              }
         }
     }
}

void ClockInitTimer(void)
{
        // Create interrupt every 100us
        
    pr2 = 249;  // Timer2 period register - 1
    t2con = 0x0c;  // Timer on divide by 2

        // Enable timer 2 interrupt
    set_bit(intcon, PEIE);
    set_bit(pie1, TMR2IE);
    set_bit(intcon, GIE);

   GlobalTick100us = 0;
   
   ClockCurrentTime.Day = 0;
   ClockCurrentTime.Hour = 0;
   ClockCurrentTime.Minute = 0;
   ClockCurrentTime.Second = 0;
   ClockCurrentTime.Tickms = 0;
}

Edited by trossin

Share this post


Link to post
Share on other sites

Thanks Trossin & Dave.

I wanted to do make a beginner level project & so started of with the relatively simpler tmr0 & without having to set the PIR & PIE. I see that i can't ask for more accuracy from it. But after doing so much I think i should go a little further and implement timer2 for more accuracy.

 

Emte,

 

You mean subtract, an extra delay adds more time than you want so you would end up with 2mS every time you reload the timer.

 

Yes. The data sheet says if tmr0 is written(add to or subtracted from or just write a new value), the increment of the timer is inhibited for the following two clock cycles. ie. to say it takes 2micro seconds( in our case with 4mhz crystal) for the timer0 to get your value on its register.

 

Btw why are you using a goto in beginner code? And why are you not resetting the timer inside the

ISR?

I confess i am new to c pogramming, but found it very simple to implement it here.

Why sould you reset the timer in the ISR. The ISR should just update the necessary flags. The most important of which is the timer over flow flag(intcon.2).

 

Your clock code to me seems like it will desync very quickly due to its organization irrespective

of what the simulator tells you your timing is since you do not reload your timer in your ISR. You could

frequently be 10+mS out between when you exit your ISR and restart your Timer.

 

I don't stop the timer. It starts running as soon as you set the option register. And it sets the over flow flag when ever the the tmr0 rolls over from 255 to 0. There is no question of resetting your timer. With out any write operation the timer0 will clock 256 microsecons(4mhz crystal) on every interrupt(at every roll over). We just have to see that it gives us an interrut at every 250 microsecond for which we advance the tmr0 by 6micro(256-6) or add 6 to timer0(tmr0+=6;).

I think that explains your doubt. On implimenting i faced with problem & thats explained in my first post.

 

Regards,

Raghunathan.

Share this post


Link to post
Share on other sites

This thread has my complete source file that implements using timer 0 is the way you need:

 

How To Setup A Software Interrupt?

 

You get an exact period interrupt from timer 0.

Depending on interrupt latency it's 250 + or - 3 clocks.

 

The update of the timer 0 count value corrects for the two clock delay before the timer counts after a write.

 

If you can't adapt this to make a clock on a 16F72 you are not half trying.

 

You must do all of the seconds, minutes, hours, days, months, and years calculating in the idle loop.

 

DO NOT DO THAT KIND OF STUFF IN AN INTERRUPT HANDLER!

 

It's a waste of time and it annoys the author.

 

cac.

Share this post


Link to post
Share on other sites

Btw why are you using a goto in beginner code? And why are you not resetting the timer inside the

ISR?

I confess i am new to c pogramming, but found it very simple to implement it here.

Why sould you reset the timer in the ISR. The ISR should just update the necessary flags. The most important of which is the timer over flow flag(intcon.2).

 

Hmm this differs slightly from what the datasheet says and what you do...

TMR0IF must be cleared before reenabling the Timer0 Interrupt, nothing about it

automatically happening. Looking at your code i expect that the timer will run once

and then stop. If the datasheet is right then as soon as you enter the TMR0 interrupt

you should clear the flag and reenable it so that it runs again right away with the

instruction losses for the ISR calculated in.

 

Used to using Timer1 myself on chips that need a counter reload everytime so this

is a normal process to me :)

Edited by emte

Share this post


Link to post
Share on other sites

Btw why are you using a goto in beginner code? And why are you not resetting the timer inside the

ISR?

I confess i am new to c pogramming, but found it very simple to implement it here.

Why sould you reset the timer in the ISR. The ISR should just update the necessary flags. The most important of which is the timer over flow flag(intcon.2).

 

Hmm this differs slightly from what the datasheet says and what you do...

TMR0IF must be cleared before reenabling the Timer0 Interrupt, nothing about it

automatically happening. Looking at your code i expect that the timer will run once

and then stop. If the datasheet is right then as soon as you enter the TMR0 interrupt

you should clear the flag and reenable it so that it runs again right away with the

instruction losses for the ISR calculated in.

 

Used to using Timer1 myself on chips that need a counter reload everytime so this

is a normal process to me :)

 

Hi emte,

My code works beautifully on the simulator.

TMR0IF is the the same as intcon.2, we also call it as timer0 over flow flag( also called timer0 interrupt flag). I have cleared this flag at the end of the code in the ISR(interrupt service routine) void interrupt(void).

 

 

You get an exact period interrupt from timer 0.

Depending on interrupt latency it's 250 + or - 3 clocks.

 

The update of the timer 0 count value corrects for the two clock delay before the timer counts after a write.

 

If you can't adapt this to make a clock on a 16F72 you are not half trying.

 

You must do all of the seconds, minutes, hours, days, months, and years calculating in the idle loop.

 

DO NOT DO THAT KIND OF STUFF IN AN INTERRUPT HANDLER!

 

It's a waste of time and it annoys the author.

 

cac.

 

Hi cac001,

If your talking of + or - 3 clock pulses, i think my code will do the job or may be better. If the source boost simulator gives an accurate value of the latency in tmr0 upon entering the ISR & if it also gives the right value upon execution of the c code tmr0 += x; Then my clock should be accurate.

I think i have one more question for Dave.

Dave, you told me that the compiler does not compensate for a write operation on the timer0 but does the simulator compensate for it?

I think it compensates for the write operation & also the extra lines of asm code that it would generate for the c instruction tmr0+=x;.

 

cac001, the incrementing of seconds, minutes, hours are generally done in the ISR itself. I am not unique. See Trossin's code.

 

Regards

Raghunathan.

Share this post


Link to post
Share on other sites

ra68gi,

Dave, you told me that the compiler does not compensate for a write operation on the timer0 but does the simulator compensate for it?

I think it compensates for the write operation & also the extra lines of asm code that it would  generate for the c instruction tmr0+=x;.

There is no compenastion for any code execution delays. You have todo that in your code (maybe you even need to use inline assembler to gurantee that the code will never change as could happen with some new optimisation).

 

The simulation of TMR0 works like the hardware :)

 

Regards

Dave

Share this post


Link to post
Share on other sites
ra68gi,
Dave, you told me that the compiler does not compensate for a write operation on the timer0 but does the simulator compensate for it?

I think it compensates for the write operation & also the extra lines of asm code that it would  generate for the c instruction tmr0+=x;.

There is no compenastion for any code execution delays. You have todo that in your code (maybe you even need to use inline assembler to gurantee that the code will never change as could happen with some new optimisation).

 

The simulation of TMR0 works like the hardware :)

 

Regards

Dave

Dave, you say that the simulator works just like the hardware. Than if the hardware takes 2 clock cycles for a write operation onto tmr0, your simulator too must take 2 clock cycles. Am I right?

Raghunathan.

Share this post


Link to post
Share on other sites
Dave, you say that the simulator works just like the hardware. Than if the hardware takes 2 clock cycles for a write operation onto tmr0, your simulator too must take 2 clock cycles. Am I right?

Yep.

 

Regards

Dave

Share this post


Link to post
Share on other sites
Hi cac001,

 

If your talking of + or - 3 clock pulses, i think my code will do the job or may be better. If the source boost simulator gives an accurate value of the latency in tmr0 upon entering the ISR & if it also gives the right value upon execution of the c code tmr0 += x; Then my clock should be accurate.

 

Regards

Raghunathan.

 

Your code seems to be using the wrong value to reload the TMR0 period register.

 

//timer0 interrupt service routine.

void interrupt( void )

{

unsigned char tick;

unsigned char tick1;

unsigned char seconds;

unsigned char minutes;

unsigned char hours;

tmr0 +=2;

tick++;

 

At the point where you write to TMR0 you need to set the count register to count 250 instruction cycle clocks minus the number of instruction cycle executed plus two for the write to TMR0 count delay.

 

You are using +2. This is the wrong value.

 

The way you have configured the TMR0 clock source TMR0 will count instruction cycles. This means that TMR0 will count the instructions after the TMR0IF flag is set until your interrupt handler can update TMR0 to the period you want.

 

So now we need to add a value to TMR0 so that 250 clock later the TMR0IF flag is set again.

 

The first thing a TMR0 interrupt handler must do is clear the TMR0IF flag. Your code does this last. It's a bad thing.

 

The next thing is to reload the TMR0 period to interrupt exactly 250 clocks after the write.

 

The value added to the TMR0 register must be 2-249, or 9.

 

The reason the value is -249 rather than -250 is that TMR0 needs 250 clocks to count from -249 to zero. The 2 is there for the 2 cycle count delay after a write to TMR0.

cac001, the incrementing of seconds, minutes, hours are generally done in the ISR itself. I am not unique. See Trossin's code.

Just because many do it does not make it a good thing.

 

When using TMR0 to interrupt every 250 clocks it is a particularly bad thing.

 

Every instruction that the TMR0 ISR must execute on one less instruction that the idle loop or any other ISR can use.

 

Assuming that TMR0 is the only interrupt source and the TMR0 ISR uses 50 clocks then there are 200 available for your application or about 80 percent of the execution resource.

 

If the TMR0 ISR uses 200 clocks then there are 50 available for your application or about 25 percent of the execution resource.

 

If the idle loop really does have nothing better to do then having the ISR do the basic time keeping is tolerable.

 

On the other hand if your application needs to scan analog inputs, log changes to a serial EEPROM and do serial I/O than maybe you want a very different interrupt handling method.

Edited by cac001

Share this post


Link to post
Share on other sites

This thread is interesting but out of control in that the point is for an easy beginner example. Using TMR0 is not a good choice for a beginner. It is just there for compatability with older PICs (like the 16F84). Timer 1 and 2 are superior and should be used for the example. The end result will be code that is easier to understand and will not be littered with magic adjustments and explanations. Also, the end result will be a clock that won't gain or lose a few minutes a day.

 

I built a clock using TMR0 on an 16F84 and found that the only way to use it was to never load the count and just let it interrupt every 256 ticks. I then applied lots of ugly math to convert the tick rate to seconds. It required a dithering technique as the result had a fractional component to it. This clock loses about a second a day which is due to the crystal freq not my math.

 

There are 60*60*24 = 86400 seconds in a day. With a 20 ppm (parts/million) crystal that is a 86400*((1000000+20)/1000000)=86401.728 seconds/day or it could blow it by about 2 seconds per day.

 

One thing that I have noticed is that the error seems to be pretty constant for a given crystal at a given temperature so the error can be trimmed away which is what my code for timer 2 does above.

 

Help me! I'm typing and I can't stop.

Share this post


Link to post
Share on other sites
This thread is interesting but out of control in that the point is for an easy beginner example.  Using TMR0 is not a good choice for a beginner. 

Yes using TMR0 correctly is a real challenge and not something anyone new to the PIC architecture will find easy to understand.

 

If TMR1 is available it is almost always a better choice for keeping track of real time.

 

My example does a good job with TMR0 and gives the application access to accurate delays in the one milliseconds range that are mostly insensitive to interrupt activity. But it will leave the new kids wondering, "What the heck was he thinking?"

Share this post


Link to post
Share on other sites

Yes, after all this dicussion i have a better picture. I was wrong in loading tmr0+=2;

I took this wrong decision based on the tmr0 register value on the simulator.

As Dave says, if the simulator & hardware behaves in a similar way then i sould load the value as tmr0+=8; and not be botherd about the value shown on the simulator. tmr0+=8;( 6 clock cycles to get 250 count & 2 more for delay in write operation).

Finally as Trossin says, the proof of the pudding is in its eating. Let me load this value and run my clock for a day and compare it with some other standard clock(do we have one?).

 

At the point where you write to TMR0 you need to set the count register to count 250 instruction cycle clocks minus the number of instruction cycle executed plus two for the write to TMR0 count delay.

 

cac001, you can see that i am setting the tmr0 as soon as i enter the ISR. Thats the first c instruction. Instead of saying "you need to set the count register to count 250 instruction " you could say you need to count 250 clock cycle. Again tmr0 is in the habbit of counting upto 256 ( to be precise it counts upto 255 & when it rolls over to 0(ie.256 count) it sets the interrupt) before it sets the interrupt flag. So we need to make it count only 250 before it sets the interrupt flag and this we do by making it not count 6 clock cycle. In an ideal condition where upon entering the ISR if your tmr0 shows 0 we simply we would like to start tmr0 from counter value 6. which we would write in c as tmr0=6;. From the data sheet we know we need to add 2 more since the micro halts for 2 clock cycle before it registers this value so we should write tmr0=8;

But we do not have an ideal world and by the time we enter the ISR the tmr0 would have clocked a few ticks (to save the various registers). Let us say its 20 clock cycles, so tmr0 will show 20.But you can be sure its defnitely not going to take more than 50 clock cycle to save all this file. This being the case we simply give tmr0 an addition advantage or handicap of 8 clock cycle for it to generate the next interrupt. For this we simply add 8 to the alredy existing counter value. So we write in C as tmr0+=8;. Irrespective of what the tmr0 counter is upon entering the ISR we simply add 8 to it. Time taken for save operation can vary but that will not effect our accuracy, because we simply add 8 to get tmr0 to count 250.

Next the code for incrementing and setting the flag of the seconds minutes and hours is done. We sould make sure this whole process does not take more than 150 clock cycles(thats a lot of time and we can confortably finish the whole thing in less than 25 clock cycles). Finally we clear the interrupt Flag so that we are ready to receive the next interrupt.

In the main program we just check the flag and spend most of the time firing the 7-segment displays. Thats all to it.

 

Finally as Trossin says the accuracy & effect of drift in the crystal will decide how accurate your clock will be.

Edited by ra68gi

Share this post


Link to post
Share on other sites
tmr0+=8;( 6 clock cycles to get 250 count & 2 more for delay in write operation).

You still have a problem. You have it fixed in your head that the number TMR0 needs to count from is 250.

 

This is incorrect. The correct number is 249 because this number will cause TMR0 to go through 250 count states and then set the TMR0IF flag.

 

Using tmr0+=8; to set the period of TMR0 will cause it to use 251 count states.

 

Think of the TMR0 as if it's an index of an array in C. If the array is 250 elements then the index of the first element is 0 and the last is 249.

Share this post


Link to post
Share on other sites

If you need an accurate clock to compare time against use one that is connected to the AC line voltage. Back in the old days when synchronous motors were used for clocks this AC line frequency was the standard. The power companies are very careful to produce the correct beats/day. Today, most digital clocks that plug into a wall also use the AC line frequency as a standard.

 

Another more way to go is with the "atomic" clocks that use a radio to get the time from a radio station that is using the atomic clock standard. I picked up a travel alarm clock at a Target store here in Colorado for 7 bucks that dials into the radio standard.

Share this post


Link to post
Share on other sites

Short term the AC line frequency varies with load.

 

Over a 24 hour period in North America we see 5,184,000 cycles.

 

This too does drift but the grid operators try to maintain a very accurate phase lock over the long term. If any part of the grid drifts too far then there are large power flows back and forth from one point to another.

 

The ultimate in accuracy is a GPS receiver that provides a time sync output pulse, usually at a one second interval. With a satellite lock this is accurate to within 1 or 2 PPM.

Share this post


Link to post
Share on other sites
tmr0+=8;( 6 clock cycles to get 250 count & 2 more for delay in write operation).

You still have a problem. You have it fixed in your head that the number TMR0 needs to count from is 250.

 

This is incorrect. The correct number is 249 because this number will cause TMR0 to go through 250 count states and then set the TMR0IF flag.

 

Using tmr0+=8; to set the period of TMR0 will cause it to use 251 count states.

 

Think of the TMR0 as if it's an index of an array in C. If the array is 250 elements then the index of the first element is 0 and the last is 249.

 

Hi ca001,

i think, you have missed a few lines of my explaination. We all know that tmr0 will show values from 0-255 only. If you count from 0-255 you will get only 255 counts and not 256. Only when the counter counts back from 255-0, would we count 256. Check it out. A small Quick check. Let's have a counter upto 5.

0-1..........1 count

1-2...........2

2-3............3

3-4.............4

4-5...............5

now

5-0.................6

So if you count upto 249, you make 249 counts or ticks o.k. But you can not make tmr0 register to count upto 249 and then jump to 0. Because tmr0 will generate interrupt only when the counter changes from 255 to 0. So what we do, we make it to start counting from 6. AS you say 255-6=249. So you have your magical no.249.

And now when the counter jumps to 0 from 255, eventually you have counted 250.

Again we know that the counter sleeps for 2 ticks upon writing 6 to tmr0. but that does not mean that the real time isn't changing. This elapsed 2 clock cycles of real time is also added to 6 and so becomes 8.

I hope this explaination satisfies you.

Regards

Raghunathan.

Share this post


Link to post
Share on other sites
If you need an accurate clock to compare time against use one that is connected to the AC line voltage.  Back in the old days when synchronous motors were used for clocks this AC line frequency was the standard.  The power companies are very careful to produce the correct beats/day.  Today, most digital clocks that plug into a wall also use the AC line frequency as a standard.

 

Trossin, I live in India which is seeing tremendous industrial & economic growth and with that comes a huge energy demand. Unable to meet with the peak demand many a times the frequency dips far below the normal( 50 Hz) in our place. Line frequency as clock is out of question. But your second option looks great. I should check out if such a clock is available here.

Regards

Raghunathan.

Share this post


Link to post
Share on other sites

You could also choose a PIC with an internal RTC, or use an external RTC if it is

important to keep acurate time.

Share this post


Link to post
Share on other sites
You could also choose a PIC with an internal RTC, or use an external RTC if it is

important to keep acurate time.

But that will be a all together different project.

Regards,

Raghunathan.

Share this post


Link to post
Share on other sites
This elapsed 2 clock cycles of real time is also added to 6 and so becomes 8.

I hope this explaination satisfies you.

ra68gi,

 

I have run your code using the MPLAB simulator.

 

After changing line 38 from "tmr0 +=2;" to "tmr0 +=8;"

 

I set a break point at line 38.

 

Executed the code until the break point is reached.

 

Opened the Stopwatch dialog and zeroed the time.

 

Then I press F9 to run until the break point is reached four times.

 

The expected cycle count should be 1000. It is in fact 1004.

 

If you keep pressing F9 the cycle count gap between the expected and actual keeps getting larger. So this is not an interrupt latency issue. The adjustment to the TMR0 period is not correct.

 

One of us must be wrong. I think it may be you.

 

cac.

Share this post


Link to post
Share on other sites
This elapsed 2 clock cycles of real time is also added to 6 and so becomes 8.

I hope this explaination satisfies you.

ra68gi,

 

I have run your code using the MPLAB simulator.

 

After changing line 38 from "tmr0 +=2;" to "tmr0 +=8;"

 

I set a break point at line 38.

 

Executed the code until the break point is reached.

 

Opened the Stopwatch dialog and zeroed the time.

 

Then I press F9 to run until the break point is reached four times.

 

The expected cycle count should be 1000. It is in fact 1004.

 

If you keep pressing F9 the cycle count gap between the expected and actual keeps getting larger. So this is not an interrupt latency issue. The adjustment to the TMR0 period is not correct.

 

One of us must be wrong. I think it may be you.

 

cac.

 

Special thanks to you cac. You have been so patient & helpful in sorting out the tmr0 issue. I too tested it on the simulator and found the same error. When i changed it to tmr0+=9; it gave perfect result. Occasionally it shows one micosecond less or more but it gets corrected in the next run.

some of the values i got are

250,500,750,1001,1250,1500......4501,4750....,6749,7000....

Looks like its self adjusting.

But how am i going to justify tmr0+=9;. It defies my logic.

cac, trossin, dave, any one who can give an explaination?

 

note- 1 count error in one interrupt cycle will results in 4000 count per second & around 5.76 minutes per day.

Share this post


Link to post
Share on other sites

Guys,

Let me type whats written on to the data sheet again. It says, If tmr0 register is written, the increment is inhibited for the following two instruction cycles. The user can work around this by writing an adjusted value to the tmr0 register.

 

For the 1st clock cycle the write operation takes place. For the next two clock cycles the timer is inhibited. So in total the counter is inactive for three clock cycles.

So looks like we need to add 3 to 6 and write it as tmr0+=9;.

Some reverse engineering i guess. Please comment.

Share this post


Link to post
Share on other sites

ra68gi,

note- 1 count error in one interrupt cycle will results in 4000 count per second & around 5.76 minutes per day.

You get back to what trossin said, to have an accurate clock you need to leave TMR0 value untouched and just let free run. Then compensate when counting ticks for the fact they don't occur at the correct rate.

 

Regards

Dave

Share this post


Link to post
Share on other sites
But how am i going to justify tmr0+=9;. It defies my logic.

cac, trossin, dave, any one who can give an explaination?

For the 1st clock cycle the write operation takes place.

 

For the next two clock cycles the timer is inhibited.

 

So in total the counter is inactive for three clock cycles.

 

So looks like we need to add 3 to 6 and write it as tmr0+=9;.

You arrived at the correct number but for the wrong reason.

 

This has to do with the way we count with integers.

 

Try this exercise:

 

Using a 8-bit HEX representation for numbers count backwards until you have written five numbers.

 

Take that fifth number and make a 2's complement of it.

 

What is that number?

 

0x00 number 1

0xFF number 2

0xFE number 3

0xFD number 4

0xFC number 5

 

(0xFC ^ 0xFF) + 1 = 4

 

--------------------------

 

Try thinking of TMR0 as counting from 1 to 256.

 

Then ask what number (n) needs to be in TMR0 such that there are 250 numbers from (n) to 256 including both (n) and 256?

 

Answer: n = 256 - 250 + 1, or 7

 

Each one of the 250 numbers represents a count state of TMR0.

 

To correct for the 2-clock delay after a write to TMR0 we add 2 to n.

 

This give us a final working value of n = 9.

 

--------------------------

 

It is useful to remember that this only works when the prescaler is not used with TMR0.

 

When then prescaler is used any write to TMR0 clears the prescaler.

 

This means that when using TMR0 with the prescaler for keeping track for real time you can never write to TMR0. Doing so will cause a loss of time counted in the prescaler.

 

cac.

Edited by cac001

Share this post


Link to post
Share on other sites
But how am i going to justify tmr0+=9;. It defies my logic.

cac, trossin, dave, any one who can give an explaination?

For the 1st clock cycle the write operation takes place.

 

For the next two clock cycles the timer is inhibited.

 

So in total the counter is inactive for three clock cycles.

 

So looks like we need to add 3 to 6 and write it as tmr0+=9;.

You arrived at the correct number but for the wrong reason.

 

This has to do with the way we count with integers.

 

Try this exercise:

 

Using a 8-bit HEX representation for numbers count backwards until you have written five numbers.

 

Take that fifth number and make a 2's complement of it.

 

What is that number?

 

0x00 number 1

0xFF number 2

0xFE number 3

0xFD number 4

0xFC number 5

 

(0xFC ^ 0xFF) + 1 = 4

 

--------------------------

 

Try thinking of TMR0 as counting from 1 to 256.

 

Then ask what number (n) needs to be in TMR0 such that there are 250 numbers from (n) to 256 including both (n) and 256?

 

Answer: n = 256 - 250 + 1, or 7

 

Each one of the 250 numbers represents a count state of TMR0.

 

To correct for the 2-clock delay after a write to TMR0 we add 2 to n.

 

This give us a final working value of n = 9.

cac.

So your equation boils down to n=(256-x) +1;

Let's see if your equation is consistent.

If x=256 then

n=(256-256) + 1;

n=1;

So if we leave the tmr0 to count upto 256 we need to make n= 1 & thats wrong.

A formula should be consistent for all value 0<x <=256.

Regards

Raghunathan.

Edited by ra68gi

Share this post


Link to post
Share on other sites
So your equation boils down to n=(256-x) +1;

Let's see if your equation is consistent.

If x=256 then

n=(256-256) + 1;

n=1;

So if we leave the tmr0 to count upto 256 we need to make n= 1 & thats wrong.

A formula should be consistent for all value 0<x <=256.

Well it seems correct to me that n should equal 1 for a count sequence of 256 states.

 

Consider this, TMR0 has just counted from 0xFF to 0x00, the TMR0IF flag is set.

 

Is TMR0 at the terminal state or at the initial state?

 

You only get to pick one.

 

If it's at the terminal state then logically the next state (0x01) would be the initial state for a count cycle of 256 clocks.

 

My reasoning seems correct, but I could be wrong.

 

Do you have a better way to explain how TMR0 works?

 

cac.

Edited by cac001

Share this post


Link to post
Share on other sites
So your equation boils down to n=(256-x) +1;

Let's see if your equation is consistent.

If x=256 then

n=(256-256) + 1;

n=1;

So if we leave the tmr0 to count upto 256 we need to make n= 1 & thats wrong.

A formula should be consistent for all value 0<x <=256.

Well it seems correct to me that n should equal 1 for a count sequence of 256 states.

 

Consider this, TMR0 has just counted from 0xFF to 0x00, the TMR0IF flag is set.

 

Is TMR0 at the terminal state or at the initial state?

 

You only get to pick one.

 

If it's at the terminal state then logically the next state (0x01) would be the initial state for a count cycle of 256 clocks.

 

I don't know but my reasoning seems correct, but I could be wrong.

 

Do you have a better way to explain the TMR0 works?

 

cac.

 

Hi cac,

I thought you would have solved the problem and infact i was very anxiously waiting for your reponse.

My reasoning turned out to be right. ie. it take 3 clock cycles for the timer0 to start running. 1st clock cycle to write onto tmr0 and another 2 clock cycles of inhibition after the write operation.

I changed the code to tmr0+=0; and tried it on the mplab sim with a stop watch.

But when i set the break point & made it run, it did not stop. I didn't know why.

So i made another variable by name check initialized to 0 & added to it. ie.

tmr0+=check; & checked it up. I got 259,518,1036,1295,1554... some times i got +- one cycle but got corrected the next run.

 

I found one more thing, writing tmr0 value to tmr0 itself causes 4 cycles dealy.ie.

tmr0=tmr0;. I got 260,520,780,1041,1300,1559,1820....

 

Raghunathan.

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...