Real Time Clocks (RTCs for short)
Little attention is payed to the little piece of silicon on your board that is responsible for counting every second of every day. What is taken for granted is the fact that most of your protocols, like tcp, require some reference to time. If time is not accurate, frames can be dropped and basic destruction descends on the world. What is overlooked are some of the features packed on these little time keepers.
Did you know that some contain the ability to output a square wave? "What could I do with that?" you ask. How about connecting the output of the RTC to an input on an MCU. You could wake the MCU at various times or even use that interrupt to update network time. How about alarms? Yes, some have alarms that can wake the MCU at a given time to do a task. Another nice feature on some RTCs is SRAM and EEPROM memory. These make it easy to data-log, store multiple alarms, or be a secret hiding spot for that private data. Luckily for us MikroElektronika has just released a full featured universal RTC library that makes things easy to keep all things time related in their perspective places. We might even be able to take advantage of those hidden add-on peripheral found in the RTC.
Things Needed
One of the various I2C compatible RTCs:
Library or source from either Libstock.com or Github.com
https://github.com/MikroElektronika/Click_RTC_I2C_Universal
Some Features Supported by the Library
Features supported are:
- Daylight savings time ( US Supported at this time )
- Squarewave output ( RTC 1Hz only, RTC2 ( 1 Hz, 4.096 kHz, 8.192 kHz, 32.768 kHz ), RTC6(1 Hz, 4.096 kHz, 8.192 kHz, 32.768 kHz ) )
- Backup battery ( RTC 6 Only )
- Set / Read time in both GMT and local time
- Set / Read time in UNIX time
- Leap year calculation
- Power failure data-logging ( RTC 6 Only )
- Read past power failure times( RTC 6 Only )
- Alarms ( RTC, RTC 6 has 2 alarms )
- Read / Write SRAM kept alive by battery ( RTC2 56 bytes, RTC 6 64 bytes )
- Read / Write EEPRM ( RTC 6 Only 1024 bytes )
- Read / Write Unique ID ( RTC 6 Only )
Initialization
The library requires that the I2C bus that you are going to use with the RTC is already initialized.
#include "rtc.h" void main() { I2C1_Init_Advanced( 100000, &_GPIO_MODULE_I2C1_PB67 ); while(1) { } }
*Note some of the RTCs are able to operate up to 400 kHz in speed. Your platform may or may not support 400 kHz, so check your datasheet.
Once you have initialized the bus, the next step is easy, you need to initialize the RTC library with the correct RTC model. The models supported are:
/** * @enum Supported Device Types */ typedef enum { RTC_PCF8583, /**< RTC1 Click module */ RTC2_DS1307, /**< RTC2 Click module */ RTC3_BQ32000, /**< RTC3 Click module */ RTC6_MCP7941X /**< RTC6 Click module */ } rtc_type_t;
and found in the rtc.h file. The function for initialization requires a type and time zone: int rtc_init( rtc_type_t type, int8_t time_zone );
Let's use the RTC6 and initialize it for the Pacific Time Zone.
#include "rtc.h" void main() { I2C1_Init_Advanced( 100000, &_GPIO_MODULE_I2C1_PB67 ); rtc_init( RTC6_MCP7941X, -8 ); // Pacific Time is -8 hours GMT while( 1 ) { } }
Time zone is used to calculate local time. All times stored on the RTC is kept in GMT time and the time zone is used to add or subtract time from GMT to resolve the local time.
Retrieving Time
Time is formatted within a defined type
/** * @struct Time definition of time elements * */ typedef struct { uint8_t seconds; uint8_t minutes; uint8_t hours; uint8_t weekday; uint8_t monthday; uint8_t month; uint8_t year; } rtc_time_t;
*Note: A static member is contained within the library so no memcpy is needed when calling the read time functions.
Reading time in either GMT or Local is called by either rtc_time_t *rtc_get_gmt_time( void ); or rtc_time_t *rtc_get_local_time( void );
#include "rtc.h" void main() { rtc_time_t *current_time; I2C1_Init_Advanced( 100000, &_GPIO_MODULE_I2C1_PB67 ); rtc_init( RTC6_MCP7941X, -8 ); // Pacific Time is -8 hours GMT while( 1 ) { current_time = rtc_get_local_time(); } }
Likewise we can obtain the GMT time or UNIX time with similar functions:
#include "rtc.h" #includevoid main() { rtc_time_t *current_time; rtc_time_t *gmt_time; uint32_t unix_time; I2C1_Init_Advanced( 100000, &_GPIO_MODULE_I2C1_PB67 ); rtc_init( RTC6_MCP7941X, -8 ); // Pacific Time is -8 hours GMT while( 1 ) { current_time = rtc_get_local_time(); gmt_time = rtc_get_gmt_time(); unix_time = rtc_get_unix_time(); if( current_time->seconds == 30 ) blink_leds(); } }
*Note: Local and GMT time function return a pointer to a struct, while UNIX time is a uint32_t type. This is due to optimization and making the read of local and GMT time as fast as possible.
Setting Time
When setting the time, creation of a rtc_time_t variable will be needed.
#include "rtc.h" void main() { rtc_time_t set_time; rtc_time_t *current_time; I2C1_Init_Advanced( 100000, &_GPIO_MODULE_I2C1_PB67 ); rtc_init( RTC6_MCP7941X, -8 ); // Pacific Time is -8 hours GMT set_time.seconds = 0; set_time.minutes = 10; set_time.hours = 14; set_time.monthday = 25; set_time.month = 12; set_time.year = 15 rtc_set_local_time( set_time ); while( 1 ) { current_time = rtc_get_local_time(); if( current_time->hours == 8 ) eat_breakfast(); } }
Output a Square Wave
Many great usages of a square wave is possible. Time is the universal synchronizer among peripherals. The frequencies available may seem strange at first glance:
- 1 Hz
- 4.096 kHz
- 8.192 kHz
- 32.768 kHz
These frequencies happen to be very convenient when wanting to divide the seconds from the hours. That fundamental goes beyond this basic tutorial, but for those of you working with signal analysis, fine spatial periods, servo control, etc. will find those frequencies very friendly in your calculations.
#include "rtc.h" void main() { rtc_time_t *current_time; I2C1_Init_Advanced( 100000, &_GPIO_MODULE_I2C1_PB67 ); rtc_init( RTC6_MCP7941X, -8 ); // Pacific Time is -8 hours GMT rtc_enable_swo( RTC_1HZ ); // Output a 1 Hz square wave while( 1 ) { current_time = rtc_get_local_time(); if( current_time->seconds == 30 ) blink_leds(); } } void swo_ISR() iv EXT0 ics AUTO { toggle_led(); }
Reading / Writing to SRAM
The RTC 2 and RTC 6 both have SRAM available on them and have the advantage of being kept energized by battery power. SRAM is fast and can be used as a storage location for any type of data. Before, to the new beginner, this idea was shrouded in mystery in how to use it. Thanks to good library implementations it is easy and seamless.
Since their isn't any file system on the SRAM or EEPROM we, as the developer, need to keep track of where we are placing things in memory. The functions we can use are:
void rtc_write_sram( uint8_t addr, uint8_t data_in ); for simple single byte writes.
void rtc_write_sram_bulk( uint8_t addr, void *data_in, size_t data_size ); when we want to write multiple bytes at one time.
uint8_t rtc_read_sram( uint8_t addr ); for simple reading of a single byte.
void rtc_read_sram_bulk( uint8_t addr, void *data_out, uint8_t data_size ); for reading multiple bytes at one time.
In this example, we are going to be writing simple values to the SRAM and then Reading them back.
#include "rtc.h" #includevoid main() { rtc_time_t *current_time; uint8_t alarm_hour = 21; uint8_t alarm_count = 0; I2C1_Init_Advanced( 100000, &_GPIO_MODULE_I2C1_PB67 ); rtc_init( RTC6_MCP7941X, -8 ); // Pacific Time is -8 hours GMT // Write some values to the SRAM rtc_write_sram( 0, alarm_hour ); rtc_write_sram( 1, alarm_count++ ); // Read them back alarm_count = rtc_read_sram( 0 ); alarm_hour = rtc_read_sram( 1 ); /* values reversed alarm_count -> 21 alarm_hour -> 1 */ }
How about reading and writing multiple bytes to the SRAM? Works about the same way with a small twist.
#include "rtc.h" #includevoid main() { rtc_time_t *current_time; char string1[] = "MikroElektronika"; char string2[] = "RTCs are Great! "; I2C1_Init_Advanced( 100000, &_GPIO_MODULE_I2C1_PB67 ); rtc_init( RTC6_MCP7941X, -8 ); // Pacific Time is -8 hours GMT // Write to the SRAM rtc_write_sram_bulk( 0, string1, strlen( string1 ) + 1 ); // +1 for null terminator rtc_write_sram_bulk( strlen( string1 ) + 1, string2, strlen( string2 ) + 1 ); // Move the address forward past the last write // Read them back in reverse rtc_read_sram_bulk( 0, string2, strlen( string1 ) + 1 ); rtc_read_sram_bulk( strlen( string1 ) + 1, string1, strlen( string2 ) + 1 ); }
Notes
Some MCUs have RTCs onboard, but the majority of them do not. When you are looking to keep track of that elusive time, and RTC is the best thing since in the invention of the electric razor. Several options exist and having the MikroE. RTC library can make an easy job of managing the features included on an RTC.
References
wikipedia.org "Time to Live" - wikipedia 2016
wikipedia.org "Unix Time" - wikipedia 2016
wikipedia.org "Square Wave" - wikipedia 2016