PIC16F88 Timer1 (RTC)

Thread Starter

ATM

Joined Oct 8, 2009
31
Hey, i'm in the process of creating a Real Time Clock using the Timer1 register of a PIC16F88. I will be using a external 32.768KHz crystal to pulse the register.
I've done a little maths and figured that I need to preload the TMR1H and TMR1L registers with the value 32768 in order to achieve a 1 second count until overflow.

I have wrote the following code below in SourceBoost:

#include <system.h>
#include <pic16F88.h>

volatile bit RA1 @ PORTA.1;
volatile bit RA4 @ PORTA.4;
volatile bit RB4 @ PORTB.4;

int seconds=0,minutes=0,hours=0;

void init(void)
{

t1con=00001111b;
pie1.0=1; //set Interrupt Enable bit


trisa=10010b;
trisb=0b;

porta=0b;
portb=0b;

osccon=01111110b; //internal 8Mhz clock
intcon=10000000b; //enable GIE


cmcon=00000010b;
cvrcon=11000100b;

delay_s(1);

}

void isr()
{
tmr1h=10000000b; //pre-load timer1 reg with 32768
pir1.0=1; //clear interrupt flag
++seconds;
if(seconds>59)
{
++minutes;
seconds=0;
}
if(minutes>59)
{
++hours;
minutes=0;
}

}

void main() //start of main function
{

init(); //call initialisations


while(1) //infinite loop
{
REST OF CODE....
}
I have wrote the Interrupt code under the "void isr" heading, now I need this code to be called every time the Timer1 register overflows i.e after every second. I just dont know how to implement this. I've tried just calling the interrupt from the main code but once inside the interrupt code the program never leaves it, but I need the interrupt code to be constantly running in the background whilst the main code runs.

Any help is greatly appreciated, thanks.
 

BMorse

Joined Sep 26, 2009
2,675
If you read through the datasheet for that device, it is all right there....

The TMR1 register pair
(TMR1H:TMR1L) increments from 0000h to FFFFh
and rolls over to 0000h. The TMR1 interrupt, if enabled,
is generated on overflow which is latched in interrupt
flag bit, TMR1IF (PIR1<0>). This interrupt can be
enabled/disabled by setting/clearing TMR1 interrupt
enable bit, TMR1IE (PIE1<0>).
The operating mode is determined by the clock select
bit, TMR1CS (T1CON<1>).
In Timer mode, Timer1 increments every instruction
cycle. In Counter mode, it increments on every rising
edge of the external clock input.
Timer1 can be enabled/disabled by setting/clearing
control bit, TMR1ON (T1CON<0>).
Timer1 also has an internal “Reset input”. This Reset
can be generated by the CCP1 module as the special
event trigger (see Section 9.1 “Capture Mode”).
Note: The Timer1 oscillator shares the T1OSI
and T1OSO pins with the PGD and PGC
pins used for programming and
debugging.
When using the Timer1 oscillator, In-Circuit
Serial Programming™ (ICSP™) may not
function correctly (high voltage or low
voltage), or the In-Circuit Debugger (ICD)
may not communicate with the controller.
As a result of using either ICSP or ICD, the
Timer1 crystal may be damaged.
If ICSP or ICD operations are required, the
crystal should be disconnected from the
circuit (disconnect either lead) or installed
after programming. The oscillator loading
capacitors may remain in-circuit during
ICSP or ICD operation.
Using Timer1 as a Real-Time
Clock
Adding an external LP oscillator to Timer1 (such as the
one described in Section 7.6 “Timer1 Oscillator”)
gives users the option to include RTC functionality to
their applications. This is accomplished with an inexpensive
watch crystal to provide an accurate time base
and several lines of application code to calculate the
time. When operating in Sleep mode and using a
battery or supercapacitor as a power source, it can
completely eliminate the need for a separate RTC
device and battery backup.
The application code routine, RTCisr, shown in
Example 7-3, demonstrates a simple method to
increment a counter at one-second intervals using an
Interrupt Service Routine. Incrementing the TMR1
register pair to overflow triggers the interrupt and calls
the routine, which increments the seconds counter by
one; additional counters for minutes and hours are
incremented as the previous counter overflows.
Since the register pair is 16 bits wide, counting up to
overflow the register directly from a 32.768 kHz clock
would take 2 seconds. To force the overflow at the
required one-second intervals, it is necessary to preload
it; the simplest method is to set the MSb of TMR1H
with a BSF instruction. Note that the TMR1L register is
never preloaded or altered; doing so may introduce
cumulative error over many cycles.
For this method to be accurate, Timer1 must operate in
Asynchronous mode and the Timer1 overflow interrupt
must be enabled (PIE1<0> = 1), as shown in the
routine, RTCinit. The Timer1 oscillator must also be
enabled and running at all times.
Here is the way you would implement an RTC with interrupt using timer1 :
Rich (BB code):
RTCinit BANKSEL TMR1H
MOVLW 0x80 ; Preload TMR1 register pair
MOVWF TMR1H ; for 1 second overflow
CLRF TMR1L
MOVLW b’00001111’ ; Configure for external clock,
MOVWF T1CON ; Asynchronous operation, external oscillator
CLRF secs ; Initialize timekeeping registers
CLRF mins
MOVLW .12
MOVWF hours
BANKSEL PIE1
BSF PIE1, TMR1IE ; Enable Timer1 interrupt
RETURN
RTCisr BANKSEL TMR1H
BSF TMR1H, 7 ; Preload for 1 sec overflow
BCF PIR1, TMR1IF ; Clear interrupt flag
INCF secs, F ; Increment seconds
MOVF secs, w
SUBLW .60
BTFSS STATUS, Z ; 60 seconds elapsed?
RETURN ; No, done
CLRF seconds ; Clear seconds
INCF mins, f ; Increment minutes
MOVF mins, w
SUBLW .60
BTFSS STATUS, Z ; 60 seconds elapsed?
RETURN ; No, done
CLRF mins ; Clear minutes
INCF hours, f ; Increment hours
MOVF hours, w
SUBLW .24
BTFSS STATUS, Z ; 24 hours elapsed?
RETURN ; No, done
CLRF hours ; Clear hours
RETURN ; Done
This was all copied from the datasheet, you would be amzed in how much you can learn about a device when you read its documents :)

My .02
 

t06afre

Joined May 11, 2009
5,934
but I need the interrupt code to be constantly running in the background whilst the main code runs.

Any help is greatly appreciated, thanks.
You can not have your interrupt code to be constantly running in the background. But the the timer will run constantly. Then the timer overflows a interrupt will be generated . This will force the CPU to step out of the main code can execute the ISR code segment. Then the ISR code is done it will return to the main code at the point there it was interrupted.
 

Thread Starter

ATM

Joined Oct 8, 2009
31
If you read through the datasheet for that device, it is all right there....





Here is the way you would implement an RTC with interrupt using timer1 :
Rich (BB code):
RTCinit BANKSEL TMR1H
MOVLW 0x80 ; Preload TMR1 register pair
MOVWF TMR1H ; for 1 second overflow
CLRF TMR1L
MOVLW b’00001111’ ; Configure for external clock,
MOVWF T1CON ; Asynchronous operation, external oscillator
CLRF secs ; Initialize timekeeping registers
CLRF mins
MOVLW .12
MOVWF hours
BANKSEL PIE1
BSF PIE1, TMR1IE ; Enable Timer1 interrupt
RETURN
RTCisr BANKSEL TMR1H
BSF TMR1H, 7 ; Preload for 1 sec overflow
BCF PIR1, TMR1IF ; Clear interrupt flag
INCF secs, F ; Increment seconds
MOVF secs, w
SUBLW .60
BTFSS STATUS, Z ; 60 seconds elapsed?
RETURN ; No, done
CLRF seconds ; Clear seconds
INCF mins, f ; Increment minutes
MOVF mins, w
SUBLW .60
BTFSS STATUS, Z ; 60 seconds elapsed?
RETURN ; No, done
CLRF mins ; Clear minutes
INCF hours, f ; Increment hours
MOVF hours, w
SUBLW .24
BTFSS STATUS, Z ; 24 hours elapsed?
RETURN ; No, done
CLRF hours ; Clear hours
RETURN ; Done
This was all copied from the datasheet, you would be amzed in how much you can learn about a device when you read its documents :)

My .02
I've been through the data sheet. I'm writing the code in C, (see above..) as i've never studied assembler language so I don't know how to follow that code, and I'd rather not just copy and paste something without understanding how it works.

In my actually code im unclear as how to make the program execute the interrupt code once the T1 reg has overflowed.
 
Last edited:

BMorse

Joined Sep 26, 2009
2,675
In my actually code im unclear as how to make the program execute the interrupt code once the T1 reg has overflowed.

This interrupt can be
enabled/disabled by setting/clearing TMR1 interrupt
enable bit, TMR1IE (PIE1<0>).
The code will execute on its own when the interrupts occurs, you have to enable this interrupt and place the code in the interrupt vector, and you also have to set the config options to enable the Timer1 interrupt, and configure the timer for counter mode with external clock source so it will trigger the interrupt when its count overflows......
 

Thread Starter

ATM

Joined Oct 8, 2009
31
The code will execute on its own when the interrupts occurs, you have to enable this interrupt and place the code in the interrupt vector, and you also have to set the config options to enable the Timer1 interrupt, and configure the timer for counter mode with external clock source so it will trigger the interrupt when its count overflows......
Could you elaborate on this please, I already understand everything else, forgive my naivety. The thing i'm unsure with is, how does the program know which segment of code is the interrupt code. Surely just naming it 'ISR' is not enough.
 

BMorse

Joined Sep 26, 2009
2,675
Could you elaborate on this please, I already understand everything else, forgive my naivety. The thing i'm unsure with is, how does the program know which segment of code is the interrupt code. Surely just naming it 'ISR' is not enough.


That all depends on the compiler you are using, by placing it in a code segment starting with void ISR() the compiler knows to place this part of the code in the Interrupt Service Routine..... I have not personally used source boost but in Microchips C compiler you would write the code like this....

Rich (BB code):
void __ISR( _RTCC_VECTOR,ipl1) RTCCInterrupt( void)
{
   //code goes here for the RTCC interrupt for the Pic32MX
    mRTCCClearIntFlag();    //Clear Interrupt Flag
    
}//RTCC Interrupt
check the compilers help files to see how it handles ISR code.....
 
Top