Pic18f45k20 Timer0 RTC

Thread Starter

alexm1992

Joined Dec 19, 2013
7
I am making progress using an external 32Khz crystal to count number of seconds. It appears to work but the timing is well off.

Please see code:

Rich (BB code):
/** C O N F I G U R A T I O N   B I T S ******************************/

#pragma config FOSC = LP, FCMEN = OFF, IESO = OFF                      
#pragma config PWRT = OFF, BOREN = SBORDIS, BORV = 30
#pragma config WDTEN = OFF, WDTPS = 32768 
#pragma config MCLRE = OFF, LPT1OSC = OFF, PBADEN = ON, CCP2MX = PORTC      
#pragma config STVREN = ON, LVP = OFF, XINST = OFF     
#pragma config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF   
#pragma config CPB = OFF, CPD = OFF    
#pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF               
#pragma config WRTB = OFF, WRTC = OFF, WRTD = OFF                           
#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF           
#pragma config EBTRB = OFF   


/** I N C L U D E S **************************************************/
#include "p18f45k20.h"
#include "delay.h"
#include "delays.h"
#include "switch header.h"

/** D E C L A R A T I O N S ******************************************/






void main (void)
{

int Day =0; // number of days
int Counter =0; // counter
int Sec = 0; // seconds passed


PORTA = 0x03;            //Preset RA1,RA0 to 1    
TRISA = 0x00;            
   
     

INTCON2bits.RBPU = 0;		// enable PORTB internal pullups
WPUBbits.WPUB0 = 1;			// enable pull up on RB0
ANSELH = 0x00;              // AN8-12 are digital inputs (AN12 on RB0)
TRISBbits.TRISB0 = 1;       // PORTB bit 0 (connected to switch) is input (1)

    // Init Timer
INTCONbits.TMR0IF = 0;          // clear roll-over interrupt flag
T0CONbits.T08BIT = 0;           // 16bit operation - reduces the need to use prescale 16   
T0CONbits.PSA = 1;           // WDT enabled prescale 1:1 
TMR0H = 0xE0;                      // clear timer - always write upper byte first
TMR0L = 0xBF;
T0CONbits.TMR0ON = 1;           // start timer

/*Fosc = 32KHz
Fcycle = Fosc/4 = 32KHz/4 = 8KHz
Tcycle = 1/Fcycle = 125us
Cycle count (no prescaler) for one second = 1s/125us = 8000
16bit overflow value = 0xFFFF (65535 in dec)

Calculate TMR0 (H&L) values:

65535-8000 = 57535 (0xE0BF)
*/


while (1)
{


if(INTCONbits.TMR0IF)		// when overflow occurs increment sec integer
{
Sec ++;

}

if (Sec==10) // 10 seconds represent one day for now 
{
Day ++; // in debug mode I am running cursor here it takes ages to return a value of one for days?
Sec = 0;
}
Have I calculated something wrong? As I am using a prescale of 1:1 according to the data sheet the WDT must be enabled, should this be set up in the config files first? Thanks

in fact when setting the prescaler to WDT to get a prescale of 1:1 the value of days is never incremented.
 
Last edited:

spinnaker

Joined Oct 29, 2009
7,830
I am making progress using an external 32Khz crystal to count number of seconds. It appears to work but the timing is well off.


Have I calculated something wrong? As I am using a prescale of 1:1 according to the data sheet the WDT must be enabled, should this be set up in the config files first? Thanks

in fact when setting the prescaler to WDT to get a prescale of 1:1 the value of days is never incremented.

Don't you think it would help to mention the time you expect out of the timer for someone to be able to tell you?
 

Thread Starter

alexm1992

Joined Dec 19, 2013
7
Good point, I forgot that. However by the fact the overflow increments an integer titled sec I think it is fairly self explanatory that I want it to overflow every second.
 

JohnInTX

Joined Jun 26, 2012
4,787
Several problems here.

Basics:
First, are you really running the PIC itself at 32KHz? That's a tough way to get a seconds counter. The PIC is running veeerryyy slow.

Timer 0 is a poor choice for period timing since it has to be reloaded each time it overflows. Reloading timer 0 clears the prescaler adding a few (unknown) Tcycs to the timing. Timer 1 with a CCP or Timer 2/PR2 is better.

Specifics:
Your clock source for TMR 1 is TOCKI (the input pin). Your code suggests that the source should be internal - TOCON is incompletely initialized, the clock source defaults to EXTERNAL. The timer is not running.

TMR0IF is set when the timer rolls over from FFFF to 0000. You must clear it manually each time. You also must reload TMR0 with the count up value each time. Since the timer runs while you are loading you'll have to take that into account. Add the load value to the timer to account for any accumulated time between detecting the overflow and the load.

Consider using TMR1/CCP or TMR2/PR2 for an interval timer. Your life will be easier.

Finally, the code posted is missing braces at the end. Its helpful to post compilable code AND the compiler you are using. I used XC8 with minor mods to check it out.

I think you need to use the 32kHz oscillator with Timer1, not Timer0
That's the usual way to do it. The arithmetic suggests that the PIC itself is running at 32KHz. But yeah, using the built in 32KHz/Timer 1 setup is a good way as well, particularly if you want to sleep the PIC for power savings but keep the timing going. Good call.

EDIT: The WDT (watchdog timer) is OFF by the pragmas. Unlike some PICs, this one does not share TMR0 prescaler with the WDT.

Have fun.
 
Last edited:

Thread Starter

alexm1992

Joined Dec 19, 2013
7
Thanks! I think I am going to give this another go with Timer1, it sounds a much better option. Does anybody have any sample code for using this?
 

JohnInTX

Joined Jun 26, 2012
4,787
Thanks! I think I am going to give this another go with Timer1, it sounds a much better option. Does anybody have any sample code for using this?
Using which implementation?

For the suggested CCP / TMR1 period timer see those sections in the databook particularly CCPxCON:
1011 = Compare mode, trigger special event (ECCP resets TMR1 or TMR3, sets CC1IF bit).
The timer will count off 0-Ncounts then reset and raise TMR1IF.

For GopherT's 32KHz RTC approach see T1CON:
bit 3 T1OSCEN: Timer1 Oscillator Enable bit
1 = Timer1 oscillator is enabled
.
That will enable timer 1's oscillator using your 32KHz XTAL BUT! now you'll have to configure another oscillator to run the PIC itself.

Either way, I would use a real interrupt service routine to increment the various time of day registers so you don't have to poll TMR1IF all the time.

Sample code depends on which one you want to use.
 

spinnaker

Joined Oct 29, 2009
7,830
Good point, I forgot that. However by the fact the overflow increments an integer titled sec I think it is fairly self explanatory that I want it to overflow every second.
So you expect everyone to scan through every line of your code to pick that out? And then assume after that? It makes it a lot easier when you provide as much information as possible even when it might seem obvious to you.

As others have said Timer 1 will be much easier. You can run it off of your external osc, that way you can run the mcu at any speed that you want.

But does this chip have an RTC? Or is it a roll your own? If it has an RTC then I do not recall having to mess with timers. If it is a roll your own then you are headed in the right direction except for the chosen timer.
 

GopherT

Joined Nov 23, 2012
8,009
But does this chip have an RTC? Or is it a roll your own? If it has an RTC then I do not recall having to mess with timers. If it is a roll your own then you are headed in the right direction except for the chosen timer.
Roll your own is the answer. Or, in the case of this OP, he's just stopping by and hoping he can have a toke from the timers that we rolled and passed around long ago.
 

THE_RB

Joined Feb 11, 2008
5,438
Thanks! I think I am going to give this another go with Timer1, it sounds a much better option. Does anybody have any sample code for using this?
You don't need a 32kHz xtal, or to be limited to any particular timer.

There's a ton of code on this page;
http://www.romanblack.com/one_sec.htm

that shows you how to make ANY period (like one second) from any timer, and any xtal speed.

The entire code to generate a 1 second period can be as simple as this;
Rich (BB code):
// C code for a 1 second period with a 1MHz timer (4MHz xtal);
// uses 1 variable; unsigned long bres
// gets here every TMR0 int (every 256 ticks)

bres += 256;          // add 256 ticks to bresenham total

if(bres >= 1000000)   // if reached 1 second!
{
  bres -= 1000000;    // subtract 1 second, retain error
  do_1sec_event();    // update clock, etc
}
(that code can be put in a free running 8bit TMR0 interrupt, and will make seconds automatically).
:)
 
Top