11 Getting Started with RTC
11.1 Introduction
Author: Victor Berzan, Microchip Technology Inc. |
This technical brief describes how the Real-Time Counter (RTC) module works on tinyAVR® 0- and 1-series, megaAVR® 0-series and AVR® DA devices. It covers the following use cases:
- RTC Overflow
Interrupt:
Initialize the RTC, enable the overflow interrupt, and toggle an LED on each overflow.
- RTC Periodic
Interrupt:
Initialize the RTC PIT, enable the periodic interrupt, and toggle an LED on each periodic interrupt.
- RTC PIT Wake from
Sleep:
Initialize the RTC PIT, enable the periodic interrupt, configure device sleep mode, put CPU in Sleep, the PIT interrupt will wake the CPU.
11.2 Overview
The RTC peripheral offers two timing functions: A Real-Time Counter (RTC) and a Periodic Interrupt Timer (PIT). The PIT functionality can be enabled independently of the RTC functionality.
The RTC counts (prescaled) clock cycles in a Counter register and compares the content of the Counter register to a Period register and a Compare register. The RTC can generate both interrupts and events on compare match or overflow. It will generate a compare interrupt and/or event at the first count after the counter equals the Compare register value, and an overflow interrupt and/or event at the first count after the counter value equals the Period register value. The overflow will also reset the counter value to zero.
Using the same clock source as the RTC function, the PIT can request an interrupt or trigger an output event on every nth clock period (‘n’ can be selected from {4, 8, 16,.. 32768} for interrupts, and from {64, 128, 256,... 8192} for events).
The PIT and RTC functions are running off the same counter inside the prescaler. Writing the PRESCALER bit field in the RTC.CTRLA register configures the period of the clock signal that increments the CNT. The PERIOD bit field in RTC.PITCTRLA selects the bit from the 15-bit prescaler counter to be used as PIT period output.
11.3 RTC Overflow Interrupt
This code example shows how to use the RTC with overflow interrupt enabled to toggle an LED. The overflow period is 500 ms. The on-board LED will be toggled each time the overflow interrupt occurs.
To operate the RTC, the source clock for the RTC counter must be configured before enabling the RTC peripheral and the desired actions (interrupt requests, output events). In this example, the 32.768 kHz external oscillator is used as the source clock.
To configure the oscillator, first, it must be disabled by clearing the ENABLE bit in the CLKCTRL.XOSC32KCTRLA register:
uint8_t temp; temp = CLKCTRL.XOSC32KCTRLA; temp &= ~CLKCTRL_ENABLE_bm; ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp);
The user must then wait for the corresponding status bit to become
‘0
’:
while(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm) { ; }
Select the external oscillator by clearing the SEL bit in the CLKCTRL.XOSC32KCTRLA register:
temp = CLKCTRL.XOSC32KCTRLA; temp &= ~CLKCTRL_SEL_bm; ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp);
Then, enable the oscillator by setting the ENABLE bit in the CLKCTRL.XOSC32KCTRLA register:
temp = CLKCTRL.XOSC32KCTRLA; temp |= CLKCTRL_ENABLE_bm; ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp);
Afterward, the user must wait for all registers to be synchronized:
while (RTC.STATUS > 0)
{
;
}
The RTC period is set in the RTC.PER register:
The 32.768 kHz External Crystal Oscillator clock is selected in the RTC.CLKSEL register:
RTC.CLKSEL = RTC_CLKSEL_TOSC32K_gc;
To enable the RTC to also run in Debug mode, the DBGRUN bit is set in the RTC.DBGCTRL register:
RTC.DBGCTRL |= RTC_DBGRUN_bm;
The RTC prescaler is set in the RTC.CTRLA register. Set the RUNSTDBY bit in RTC.CTRLA to enable the RTC to also run in Standby mode. Set the RTCEN bit in RTC.CTRLA to enable the RTC.
RTC.CTRLA = RTC_PRESCALER_DIV32_gc | RTC_RTCEN_bm | RTC_RUNSTDBY_bm;
Enable the overflow interrupt by setting the OVF bit in the RTC.INTCTRL register:
RTC.INTCTRL |= RTC_OVF_bm;
For the interrupt to occur, the global interrupts must be enabled:
sei();
The Interrupt Service Routine (ISR) for the RTC overflow will toggle an LED in the example below:
ISR(RTC_CNT_vect) { RTC.INTFLAGS = RTC_OVF_bm; LED0_toggle(); }
1
’ to it inside the ISR function.An MCC generated code example for AVR128DA48, with the same functionality as the one described in this section, can be found here:
11.4 RTC Periodic Interrupt
This code example shows how to use the PIT timing function of the RTC. The on-board LED will be toggled each second when the periodic interrupt occurs.
The source clock configuration for this particular example is the same as for the RTC overflow interrupt example. Enable the periodic interrupt by setting the PI bit in the RTC.PITINTCTRL register.
RTC.PITINTCTRL = RTC_PI_bm;
The PIT period is set in the RTC.PITCTRLA register. Enable the PIT by setting the PITEN bit in RTC.PITCTRLA.
RTC.PITCTRLA = RTC_PERIOD_CYC32768_gc | RTC_PITEN_bm;
For the interrupt to occur, the global interrupts must be enabled:
sei();
The Interrupt Service Routine (ISR) for the RTC PIT will toggle an LED in the example below:
ISR(RTC_PIT_vect) { RTC.PITINTFLAGS = RTC_PI_bm; LED0_toggle(); }
1
’ to it inside the ISR
function.An MCC generated code example for AVR128DA48, with the same functionality as the one described in this section, can be found here:
11.5 RTC PIT Wake from Sleep
This code example shows how to use the PIT timing function of the RTC to wake up the CPU from sleep. The on-board LED will be toggled each second when the periodic interrupt occurs, meaning it will be toggled when the CPU wakes up from sleep.
The sleep mode is configured in the SLPCTRL.CTRLA register. The sleep feature is enabled by setting the SEN bit in SLPCTRL.CTRLA.
SLPCTRL.CTRLA |= SLPCTRL_SMODE_PDOWN_gc; SLPCTRL.CTRLA |= SLPCTRL_SEN_bm;
The CPU can be put to sleep by calling the following function:
sleep_cpu();
The PIT interrupt will wake the CPU from sleep. For the interrupt to occur, the global interrupts must be enabled:
sei();
The Interrupt Service Routine (ISR) for the RTC PIT will toggle an LED in the example below:
ISR(RTC_PIT_vect) { RTC.PITINTFLAGS = RTC_PI_bm; LED0_toggle(); }
1
’ to it inside the ISR
function.An MCC generated code example for AVR128DA48, with the same functionality as the one described in this section, can be found here:
11.6 Appendix
RTC Overflow Interrupt Code Example
/* RTC Period */ #define RTC_EXAMPLE_PERIOD (511) #include <avr/io.h> #include <avr/interrupt.h> #include <avr/cpufunc.h> void RTC_init(void); void LED0_init(void); inline void LED0_toggle(void); void RTC_init(void) { uint8_t temp; /* Initialize 32.768kHz Oscillator: */ /* Disable oscillator: */ temp = CLKCTRL.XOSC32KCTRLA; temp &= ~CLKCTRL_ENABLE_bm; /* Writing to protected register */ ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp); while(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm) { ; /* Wait until XOSC32KS becomes 0 */ } /* SEL = 0 (Use External Crystal): */ temp = CLKCTRL.XOSC32KCTRLA; temp &= ~CLKCTRL_SEL_bm; /* Writing to protected register */ ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp); /* Enable oscillator: */ temp = CLKCTRL.XOSC32KCTRLA; temp |= CLKCTRL_ENABLE_bm; /* Writing to protected register */ ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp); /* Initialize RTC: */ while (RTC.STATUS > 0) { ; /* Wait for all register to be synchronized */ } /* Set period */ RTC.PER = RTC_EXAMPLE_PERIOD; /* 32.768kHz External Crystal Oscillator (XOSC32K) */ RTC.CLKSEL = RTC_CLKSEL_TOSC32K_gc; /* Run in debug: enabled */ RTC.DBGCTRL |= RTC_DBGRUN_bm; RTC.CTRLA = RTC_PRESCALER_DIV32_gc /* 32 */ | RTC_RTCEN_bm /* Enable: enabled */ | RTC_RUNSTDBY_bm; /* Run In Standby: enabled */ /* Enable Overflow Interrupt */ RTC.INTCTRL |= RTC_OVF_bm; } void LED0_init(void) { /* Make High (OFF) */ PORTB.OUT |= PIN5_bm; /* Make output */ PORTB.DIR |= PIN5_bm; } inline void LED0_toggle(void) { PORTB.OUTTGL |= PIN5_bm; } ISR(RTC_CNT_vect) { /* Clear flag by writing '1': */ RTC.INTFLAGS = RTC_OVF_bm; LED0_toggle(); } int main(void) { LED0_init(); RTC_init(); /* Enable Global Interrupts */ sei(); while (1) { } }
RTC Periodic Interrupt Code Example
#include <avr/io.h> #include <avr/interrupt.h> #include <avr/cpufunc.h> void RTC_init(void); void LED0_init(void); inline void LED0_toggle(void); void RTC_init(void) { uint8_t temp; /* Initialize 32.768kHz Oscillator: */ /* Disable oscillator: */ temp = CLKCTRL.XOSC32KCTRLA; temp &= ~CLKCTRL_ENABLE_bm; /* Writing to protected register */ ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp); while(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm) { ; /* Wait until XOSC32KS becomes 0 */ } /* SEL = 0 (Use External Crystal): */ temp = CLKCTRL.XOSC32KCTRLA; temp &= ~CLKCTRL_SEL_bm; /* Writing to protected register */ ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp); /* Enable oscillator: */ temp = CLKCTRL.XOSC32KCTRLA; temp |= CLKCTRL_ENABLE_bm; /* Writing to protected register */ ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp); /* Initialize RTC: */ while (RTC.STATUS > 0) { ; /* Wait for all register to be synchronized */ } /* 32.768kHz External Crystal Oscillator (XOSC32K) */ RTC.CLKSEL = RTC_CLKSEL_TOSC32K_gc; /* Run in debug: enabled */ RTC.DBGCTRL = RTC_DBGRUN_bm; RTC.PITINTCTRL = RTC_PI_bm; /* Periodic Interrupt: enabled */ RTC.PITCTRLA = RTC_PERIOD_CYC32768_gc /* RTC Clock Cycles 32768 */ | RTC_PITEN_bm; /* Enable: enabled */ } void LED0_init(void) { /* Make High (OFF) */ PORTB.OUT |= PIN5_bm; /* Make output */ PORTB.DIR |= PIN5_bm; } inline void LED0_toggle(void) { PORTB.OUTTGL |= PIN5_bm; } ISR(RTC_PIT_vect) { /* Clear flag by writing '1': */ RTC.PITINTFLAGS = RTC_PI_bm; LED0_toggle(); } int main(void) { LED0_init(); RTC_init(); /* Enable Global Interrupts */ sei(); while (1) { } }
RTC PIT Wake from Sleep Code Example
#include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> #include <avr/cpufunc.h> void RTC_init(void); void LED0_init(void); inline void LED0_toggle(void); void SLPCTRL_init(void); void RTC_init(void) { uint8_t temp; /* Initialize 32.768kHz Oscillator: */ /* Disable oscillator: */ temp = CLKCTRL.XOSC32KCTRLA; temp &= ~CLKCTRL_ENABLE_bm; /* Writing to protected register */ ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp); while(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm) { ; /* Wait until XOSC32KS becomes 0 */ } /* SEL = 0 (Use External Crystal): */ temp = CLKCTRL.XOSC32KCTRLA; temp &= ~CLKCTRL_SEL_bm; /* Writing to protected register */ ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp); /* Enable oscillator: */ temp = CLKCTRL.XOSC32KCTRLA; temp |= CLKCTRL_ENABLE_bm; /* Writing to protected register */ ccp_write_io((void*)&CLKCTRL.XOSC32KCTRLA, temp); /* Initialize RTC: */ while (RTC.STATUS > 0) { ; /* Wait for all register to be synchronized */ } /* 32.768kHz External Crystal Oscillator (XOSC32K) */ RTC.CLKSEL = RTC_CLKSEL_TOSC32K_gc; /* Run in debug: enabled */ RTC.DBGCTRL = RTC_DBGRUN_bm; RTC.PITINTCTRL = RTC_PI_bm; /* Periodic Interrupt: enabled */ RTC.PITCTRLA = RTC_PERIOD_CYC32768_gc /* RTC Clock Cycles 32768 */ | RTC_PITEN_bm; /* Enable: enabled */ } void LED0_init(void) { /* Make High (OFF) */ PORTB.OUT |= PIN5_bm; /* Make output */ PORTB.DIR |= PIN5_bm; } inline void LED0_toggle(void) { PORTB.OUTTGL |= PIN5_bm; } ISR(RTC_PIT_vect) { /* Clear flag by writing '1': */ RTC.PITINTFLAGS = RTC_PI_bm; LED0_toggle(); } void SLPCTRL_init(void) { SLPCTRL.CTRLA |= SLPCTRL_SMODE_PDOWN_gc; SLPCTRL.CTRLA |= SLPCTRL_SEN_bm; } int main(void) { LED0_init(); RTC_init(); SLPCTRL_init(); /* Enable Global Interrupts */ sei(); while (1) { /* Put the CPU in sleep */ sleep_cpu(); /* The PIT interrupt will wake the CPU */ } }