reworked RTC - no need for extra time library

http://www.stm32duino.com/viewtopic.php?f=39&t=775&p=31218#p31218
This commit is contained in:
stevstrong 2017-07-19 19:11:44 +02:00
parent 8b4af3604a
commit b29fed855b
4 changed files with 485 additions and 154 deletions

View File

@ -0,0 +1,209 @@
/*
This is an example of how to use the RTclock of STM32F4 device
This example can also be used to set the RTC to the current epoch time:
- goto: http://www.unixtimestamp.com/
- enter the current date and time to the right field "Timestamp converter"
- press the "Convert" button
-
*/
#include <RTClock.h>
#include <Streaming.h>
//RTClock rt(RTCSEL_LSE); // initialise
RTClock rtc;
time_t tt;
tm_t tm;
const uint32_t DEFAULT_TIME = 1498944019; // 2017.07.01, 21:20:19 used as reference epoch time
#define TIME_HEADER 'T' // Header tag for serial time sync message
#define TIME_REQUEST 7 // ASCII bell character requests a time sync message
#define LED_PIN BOARD_LED_PIN
//-----------------------------------------------------------------------------
void blink ()
{
digitalWrite(LED_PIN, digitalRead(LED_PIN)?LOW:HIGH);
}
uint8_t s[20]; // for serial parsing
//-----------------------------------------------------------------------------
char * read_line()
{
while ( Serial.available() ) Serial.read(); // empty Rx buffer
while ( Serial.available()<=0 ) ; // wait for new characters
uint8_t c, i = 0;
s[0] = 0;
while ( Serial.available() && (i<20) ) {
c = Serial.read();
if ( c=='\n' || c== '\r') {
s[i] = 0;
break;
}
s[i++] = c;
}
while ( Serial.available() ) Serial.read(); // flush Rx
return (char*)&s[0];
}
//-----------------------------------------------------------------------------
void processSyncMessage(void)
{
if ( Serial.available() ) {
if( *read_line()==(TIME_HEADER) ) {
uint32_t pctime = atoi((const char*)&s[1]);
Serial << ("Epoch time received: ") << pctime << endl;
if ( pctime >= DEFAULT_TIME) { // check the integer is a valid epoch time
rtc.setTime(pctime); // Set RTC to the time received via the serial port
}
}
Serial << endl;
}
}
//-----------------------------------------------------------------------------
void Change_DateTime(void)
{
// check and correct the weekday if necessary
rtc.getTime(tm);
Serial << "Current weekday is " << (tm.weekday);
// get time elements
tt = rtc.makeTime(tm);
uint16_t tmp = rtc.weekday(tt);
if ( tmp!=tm.weekday ) {// correct weekday
rtc.setTime(tt);
Serial << " instead of " << tmp << ". Now is corrected.\n";
} else {
Serial << " - seems to be fine, no need to change it.\n";
}
uint8_t chg = 0;
// get time elements
rtc.getTime(tm);
Serial << "\nCurrent RTC date: " << (1970+tm.year) << "." << (tm.month) << (".") << (tm.day) << (", weekday: ") << (tm.weekday) << endl;
Serial << "Do you want to change it? (y/n)\n";
if ( *read_line()=='y' ) {
// change here the date
change_year:
Serial << "Current year: " << (1970+tm.year) << ". Enter new year in \"YYYY\" format (numbers only) or press enter to skip.\n";
if (*read_line()==0) goto change_month;
tmp = atoi((const char*)s);
if ( tmp<1970 ) { Serial << "Please enter a valid number greater than 1970\n"; goto change_year; }
Serial << "You entered: " << tmp << ". Accept value? (y/n)\n";
if ( *read_line()=='n' ) goto change_year;
tm.year = tmp-1970;
chg = 1;
change_month:
Serial << "Current month: " << tm.month << ". Enter new month in \"MM\" format [1..12] or press enter to skip.\n";
if (*read_line()==0) goto change_day;
tmp = atoi((const char*)s);
if ( tmp<1 || tmp>12 ) { Serial << "Please enter a valid number [1..12]\n"; goto change_month; }
Serial << "You entered: " << tmp << ". Accept value? (y/n)\n";
if ( *read_line()=='n' ) goto change_month;
tm.month = tmp;
chg = 1;
change_day:
Serial << "Current day: " << tm.day << ". Enter new day in \"DD\" format [1..31] or press enter to skip.\n";
if (*read_line()==0) goto change_weekday;
tmp = atoi((const char*)s);
if ( tmp<1 || tmp>31 ) { Serial << "Please enter a valid number [1..31]\n"; goto change_day; }
Serial << "You entered: " << tmp << ". Accept value? (y/n)\n";
if ( *read_line()=='n' ) goto change_day;
tm.day = tmp;
chg = 1;
change_weekday:
Serial << "Current weekday: " << tm.weekday << ". Enter new weekday [1(=Monday)..7(=Sunday)] or press enter to skip.\n";
if (*read_line()==0) goto change_time;
tmp = atoi((const char*)s);
if ( tmp<1 || tmp>7 ) { Serial << "Please enter a valid number [1..7]\n"; goto change_weekday; }
Serial << "You entered: " << tmp << ". Accept value? (y/n)\n";
if ( *read_line()=='n' ) goto change_weekday;
tm.weekday = tmp;
chg = 1;
change_time:
Serial << "Current RTC time: " << _TIME(tm.hour, tm.minute, tm.second) << endl;
Serial << "Do you want to change it? (y/n)\n";
if ( *read_line()=='n' ) goto change_end;
change_hour:
Serial << "Current hour: " << tm.hour << ". Enter new hour [0..23] or press enter to skip.\n";
if (*read_line()==0) goto change_minute;
tmp = atoi((const char*)s);
if ( tmp>23 ) { Serial << "Please enter a valid number [0..23]\n"; goto change_hour; }
Serial << "You entered: " << tmp << ". Accept value? (y/n)\n";
if ( *read_line()=='n' ) goto change_hour;
tm.hour = tmp;
chg = 1;
change_minute:
Serial << "Current minute: " << tm.minute << ". Enter new minute [0..59] or press enter to skip.\n";
if (*read_line()==0) goto change_second;
tmp = atoi((const char*)s);
if ( tmp>59 ) { Serial << "Please enter a valid number [0..59]\n"; goto change_minute; }
Serial << "You entered: " << tmp << ". Accept value? (y/n)\n";
if ( *read_line()=='n' ) goto change_minute;
tm.minute = tmp;
chg = 1;
change_second:
Serial << "Current second: " << tm.second << ". Enter new second [0..59] or press enter to skip.\n";
if (*read_line()==0) goto change_end;
tmp = atoi((const char*)s);
if ( tmp>59 ) { Serial << "Please enter a valid number [0..59]\n"; goto change_second; }
Serial << "You entered: " << tmp << ". Accept value? (y/n)\n";
if ( *read_line()=='n' ) goto change_second;
tm.second = tmp;
chg = 1;
} else {
goto change_time;
}
change_end:
if ( chg ) {
// set here the RTC time.
Serial << "Changed date & time: " << (1970+tm.year) << "." << (tm.month) << (".") << (tm.day) << (", weekday: ") << (tm.weekday) << ", " << _TIME(tm.hour, tm.minute, tm.second) << endl;
Serial << "Write now to RTC? (y/n)\n";
read_line();
if ( s[0]=='y' ) {
rtc.setTime(tm);
Serial << "Data written to RTC.\n\n";
}
} else
Serial << "RTC was not changed.\n\n";
}
//-----------------------------------------------------------------------------
void setup()
{
Serial.begin();
delay(3000);
pinMode(LED_PIN, OUTPUT);
Serial << "This is an example of how to use the STM32F4 RTC library.\n\n";
Change_DateTime();
}
//-----------------------------------------------------------------------------
void loop()
{
if ( Serial.available() ) {
// adjust time according to received epoch time from PC
processSyncMessage();
}
if (tt!=rtc.now()) {
// get epoch time
tt = rtc.now();
Serial << ("- RTC epoch timestamp = ") << (tt);
// get time elements
rtc.getTime(tm);
//rtc.breakTime(tt, tm);
Serial << (" == ") << (1970+tm.year) << "." << (tm.month) << (".") << (tm.day) << (", weekday ") << (tm.weekday) << (", ");
Serial << _TIME(tm.hour, tm.minute, tm.second) << endl;
}
}

View File

@ -7,3 +7,4 @@ paragraph=Real Time Clock for STM32F4
category=Other
url=
architectures=STM32F4
maintainer=www.stm32duino.com

View File

@ -42,15 +42,7 @@ voidFuncPtr handlerAlarmA = NULL;
voidFuncPtr handlerAlarmB = NULL;
voidFuncPtr handlerPeriodicWakeup = NULL;
RTClock::RTClock() {
RTClock(RTCSEL_HSE, 7999, 124);
}
RTClock::RTClock(rtc_clk_src src) {
RTClock(src, 0, 0);
}
//-----------------------------------------------------------------------------
RTClock::RTClock(rtc_clk_src src, uint16 sync_prescaler, uint16 async_prescaler) {
uint32 t = 0;
RCC_BASE->APB1ENR |= RCC_APB1RSTR_PWRRST;
@ -144,117 +136,155 @@ RTClock::~RTClock() {
}
*/
void RTClock::setTime (time_t time_stamp) {
unsigned char years = 0;
unsigned char months = 0;
unsigned char monthLength = 0;
unsigned char wdays = 0;
unsigned char hours = 0;
unsigned char mins = 0;
unsigned char secs = 0;
unsigned long days;
//-----------------------------------------------------------------------------
void RTClock::setTime (time_t time_stamp)
{
breakTime(time_stamp, tm); // time will be broken to tm
setTime(tm);
}
secs = time_stamp % 60;
time_stamp /= 60; // now it is minutes
mins = time_stamp % 60;
time_stamp /= 60; // now it is hours
hours = time_stamp % 24;
time_stamp /= 24; // now it is days
wdays = ((time_stamp + 4) % 7) + 1; // Sunday is day 1
while((unsigned)(days += (LEAP_YEAR(years) ? 366 : 365)) <= time_stamp) {
years++;
}
days -= LEAP_YEAR(years) ? 366 : 365;
time_stamp -= days; // now it is days in this year, starting at 0
for (months = 0; months < 12; months++) {
if (months == 1) { // february
if (LEAP_YEAR(years)) {
monthLength = 29;
} else {
monthLength = 28;
}
} else {
monthLength = monthDays[months];
}
if (time_stamp >= monthLength) {
time_stamp -= monthLength;
} else {
break;
}
}
months++; // jan is month 1
days = time_stamp + 1; // day of month
//-----------------------------------------------------------------------------
void RTClock::setTime (tm_t & tm)
{
if (tm.year > 99)
tm.year = tm.year % 100;
rtc_enter_config_mode();
RTC_BASE->TR = ((hours / 10) << 20) | ((hours % 10) << 16) | ((mins / 10) << 12) | ((mins % 10) << 8) | ((secs / 10) << 4) | (secs % 10);
RTC_BASE->DR = ((years / 10) << 20) | ((years % 10) << 16) | (wdays << 13) | ((months / 10) << 12) | ((months % 10) << 8) | ((days / 10) << 4) | (days % 10);
RTC_BASE->TR = BUILD_TIME_REGISTER(tm.hour, tm.minute, tm.second);
RTC_BASE->DR = BUILD_DATE_REGISTER(tm.year, tm.month, tm.day, tm.weekday);
rtc_exit_config_mode();
}
void RTClock::setTime (struct tm* tm_ptr) {
rtc_enter_config_mode();
if (tm_ptr->tm_year > 99)
tm_ptr->tm_year = tm_ptr->tm_year % 100;
tm_ptr->tm_wday = tm_ptr->tm_wday & 0x7;
RTC_BASE->TR = ((tm_ptr->tm_hour / 10) << 20) | ((tm_ptr->tm_hour % 10) << 16) |
((tm_ptr->tm_min / 10) << 12) | ((tm_ptr->tm_min % 10) << 8) |
((tm_ptr->tm_sec / 10) << 4) | (tm_ptr->tm_sec % 10);
RTC_BASE->DR = ((tm_ptr->tm_year / 10) << 20) | ((tm_ptr->tm_year % 10) << 16) | (tm_ptr->tm_wday << 13) |
((tm_ptr->tm_mon / 10) << 12) | ((tm_ptr->tm_mon % 10) << 8) |
((tm_ptr->tm_mday / 10) << 4) | (tm_ptr->tm_mday % 10);
rtc_exit_config_mode();
}
time_t RTClock::getTime() {
uint32 dr_reg = RTC_BASE->DR;
uint32 tr_reg = RTC_BASE->TR;
int years = 10 * ((dr_reg & 0x00F00000) >> 20) + ((dr_reg & 0x000F0000) >> 16);
int months = 10 * ((dr_reg & 0x00001000) >> 12) + ((dr_reg & 0x00000F00) >> 8);
int days = 10 * ((dr_reg & 0x00000030) >> 4) + (dr_reg & 0x000000F);
int hours = 10 * ((tr_reg & 0x00300000) >> 20) + ((tr_reg & 0x000F0000) >> 16);
int mins = 10 * ((tr_reg & 0x00007000) >> 12) + ((tr_reg & 0x0000F00) >> 8);
int secs = 10 * ((tr_reg & 0x00000070) >> 4) + (tr_reg & 0x0000000F);
// seconds from 1970 till 1 jan 00:00:00 of the given year
time_t t = (years + 30) * SECS_PER_DAY * 365;
for (int i = 0; i < (years + 30); i++) {
if (LEAP_YEAR(i)) {
t += SECS_PER_DAY; // add extra days for leap years
/*============================================================================*/
/* functions to convert to and from system time */
/* These are for interfacing with time serivces and are not normally needed in a sketch */
// leap year calulator expects year argument as years offset from 1970
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
//static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0
//-----------------------------------------------------------------------------
void RTClock::breakTime(time_t timeInput, tm_t & tm)
{
// break the given time_t into time components
// this is a more compact version of the C library localtime function
// note that year is offset from 1970 !!!
uint8_t year;
uint8_t month, monthLength;
uint32_t time;
uint32_t days;
time = (uint32_t)timeInput;
tm.second = time % 60;
time /= 60; // now it is minutes
tm.minute = time % 60;
time /= 60; // now it is hours
tm.hour = time % 24;
time /= 24; // now it is days
tm.weekday = ((time + 4) % 7); // Monday is day 1 // + 1; // Sunday is day 1
year = 0;
days = 0;
while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
year++;
}
}
// add days for this year, months start from 1
for (int i = 1; i < months; i++) {
if ( (i == 2) && LEAP_YEAR(years)) {
t += SECS_PER_DAY * 29;
} else {
t += SECS_PER_DAY * monthDays[i - 1]; //monthDays array starts from 0
tm.year = year; // year is offset from 1970
days -= LEAP_YEAR(year) ? 366 : 365;
time -= days; // now it is days in this year, starting at 0
days = 0;
month = 0;
monthLength = 0;
for (month=0; month<12; month++) {
if (month==1) { // february
if (LEAP_YEAR(year)) {
monthLength=29;
} else {
monthLength=28;
}
} else {
monthLength = monthDays[month];
}
if (time >= monthLength) {
time -= monthLength;
} else {
break;
}
}
}
t += (days - 1) * SECS_PER_DAY + hours * SECS_PER_HOUR + mins * SECS_PER_MIN + secs;
return t;
tm.month = month + 1; // jan is month 1
tm.day = time + 1; // day of month
}
struct tm* RTClock::getTime(struct tm* tm_ptr) {
uint32 dr_reg = RTC_BASE->DR;
uint32 tr_reg = RTC_BASE->TR;
tm_ptr->tm_year = 10 * ((dr_reg & 0x00F00000) >> 20) + ((dr_reg & 0x000F0000) >> 16);
tm_ptr->tm_mon = 10 * ((dr_reg & 0x00001000) >> 12) + ((dr_reg & 0x00000F00) >> 8);
tm_ptr->tm_mday = 10 * ((dr_reg & 0x00000030) >> 4) + (dr_reg & 0x000000F);
tm_ptr->tm_hour = 10 * ((tr_reg & 0x00300000) >> 20) + ((tr_reg & 0x000F0000) >> 16);
tm_ptr->tm_min = 10 * ((tr_reg & 0x00007000) >> 12) + ((tr_reg & 0x0000F00) >> 8);
tm_ptr->tm_sec = 10 * ((tr_reg & 0x00000070) >> 4) + (tr_reg & 0x0000000F);
return tm_ptr;
//-----------------------------------------------------------------------------
time_t RTClock::makeTime(tm_t & tm)
{
// assemble time elements into time_t
// note year argument is offset from 1970 (see macros in time.h to convert to other formats)
// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9
int i;
uint32_t seconds;
// seconds from 1970 till 1 jan 00:00:00 of the given year
seconds = tm.year*(SECS_PER_DAY * 365);
for (i = 0; i < tm.year; i++) {
if (LEAP_YEAR(i)) {
seconds += SECS_PER_DAY; // add extra days for leap years
}
}
// add days for this year, months start from 1
for (i = 1; i < tm.month; i++) {
if ( (i == 2) && LEAP_YEAR(tm.year)) {
seconds += SECS_PER_DAY * 29;
} else {
seconds += SECS_PER_DAY * monthDays[i-1]; //monthDay array starts from 0
}
}
seconds+= (tm.day-1) * SECS_PER_DAY;
seconds+= tm.hour * SECS_PER_HOUR;
seconds+= tm.minute * SECS_PER_MIN;
seconds+= tm.second;
return (time_t)seconds;
}
void RTClock::setAlarmATime (tm * tm_ptr, bool hours_match, bool mins_match, bool secs_match, bool date_match) {
//-----------------------------------------------------------------------------
void RTClock::getTime(tm_t & tm)
{
uint32_t dr_reg, tr_reg;
do { // read multiple time till both readings are equal
dr_reg = getDReg();
tr_reg = getTReg();
} while ( (dr_reg!=getDReg()) || (tr_reg!=getTReg()) );
tm.year = _year(dr_reg);
tm.month = _month(dr_reg);
tm.day = _day(dr_reg);
tm.weekday = _weekday(dr_reg);
tm.pm = _pm(tr_reg);
tm.hour = _hour(tr_reg);
tm.minute = _minute(tr_reg);
tm.second = _second(tr_reg);
}
//-----------------------------------------------------------------------------
time_t RTClock::getTime()
{
getTime(tm);
return makeTime(tm);
}
//-----------------------------------------------------------------------------
void RTClock::setAlarmATime (tm_t * tm_ptr, bool hours_match, bool mins_match, bool secs_match, bool date_match)
{
uint32 t = 0;
rtc_enter_config_mode();
unsigned int bits = ((tm_ptr->tm_mday / 10) << 28) | ((tm_ptr->tm_mday % 10) << 24) |
((tm_ptr->tm_hour / 10) << 20) | ((tm_ptr->tm_hour % 10) << 16) |
((tm_ptr->tm_min / 10) << 12) | ((tm_ptr->tm_min % 10) << 8) |
((tm_ptr->tm_sec / 10) << 4) | (tm_ptr->tm_sec % 10);
unsigned int bits = ((tm_ptr->day / 10) << 28) | ((tm.day % 10) << 24) |
((tm_ptr->hour / 10) << 20) | ((tm_ptr->hour % 10) << 16) |
((tm_ptr->minute / 10) << 12) | ((tm_ptr->minute % 10) << 8) |
((tm_ptr->second / 10) << 4) | (tm_ptr->second % 10);
if (!date_match) bits |= (1 << 31);
if (!hours_match) bits |= (1 << 23);
if (!mins_match) bits |= (1 << 15);
@ -276,27 +306,30 @@ void RTClock::setAlarmATime (tm * tm_ptr, bool hours_match, bool mins_match, boo
rtc_enable_alarm_event();
}
void RTClock::setAlarmATime (time_t alarm_time, bool hours_match, bool mins_match, bool secs_match, bool date_match) {
struct tm* tm_ptr = gmtime(&alarm_time);
setAlarmATime(tm_ptr, hours_match, mins_match, secs_match, date_match);
//-----------------------------------------------------------------------------
void RTClock::setAlarmATime (time_t alarm_time, bool hours_match, bool mins_match, bool secs_match, bool date_match)
{
breakTime(alarm_time, tm);
setAlarmATime(&tm, hours_match, mins_match, secs_match, date_match);
}
void RTClock::turnOffAlarmA() {
//-----------------------------------------------------------------------------
void RTClock::turnOffAlarmA(void)
{
rtc_enter_config_mode();
RTC_BASE->CR &= ~(1 << RTC_CR_ALRAIE_BIT); // turn off ALRAIE
rtc_exit_config_mode();
}
void RTClock::setAlarmBTime (tm * tm_ptr, bool hours_match, bool mins_match, bool secs_match, bool date_match) {
//-----------------------------------------------------------------------------
void RTClock::setAlarmBTime (tm_t * tm_ptr, bool hours_match, bool mins_match, bool secs_match, bool date_match)
{
uint32 t = 0;
rtc_enter_config_mode();
unsigned int bits = ((tm_ptr->tm_mday / 10) << 28) | ((tm_ptr->tm_mday % 10) << 24) |
((tm_ptr->tm_hour / 10) << 20) | ((tm_ptr->tm_hour % 10) << 16) |
((tm_ptr->tm_min / 10) << 12) | ((tm_ptr->tm_min % 10) << 8) |
((tm_ptr->tm_sec / 10) << 4) | (tm_ptr->tm_sec % 10);
unsigned int bits = ((tm_ptr->day / 10) << 28) | ((tm_ptr->day % 10) << 24) |
((tm_ptr->hour / 10) << 20) | ((tm_ptr->hour % 10) << 16) |
((tm_ptr->minute / 10) << 12) | ((tm_ptr->minute % 10) << 8) |
((tm_ptr->second / 10) << 4) | (tm_ptr->second % 10);
if (!date_match) bits |= (1 << 31);
if (!hours_match) bits |= (1 << 23);
if (!mins_match) bits |= (1 << 15);
@ -318,21 +351,23 @@ void RTClock::setAlarmBTime (tm * tm_ptr, bool hours_match, bool mins_match, boo
rtc_enable_alarm_event();
}
void RTClock::setAlarmBTime (time_t alarm_time, bool hours_match, bool mins_match, bool secs_match, bool date_match) {
struct tm* tm_ptr = gmtime(&alarm_time);
setAlarmBTime(tm_ptr, hours_match, mins_match, secs_match, date_match);
//-----------------------------------------------------------------------------
void RTClock::setAlarmBTime (time_t alarm_time, bool hours_match, bool mins_match, bool secs_match, bool date_match)
{
breakTime(alarm_time, tm);
setAlarmBTime(&tm, hours_match, mins_match, secs_match, date_match);
}
//-----------------------------------------------------------------------------
void RTClock::turnOffAlarmB() {
rtc_enter_config_mode();
RTC_BASE->CR &= ~(1 << RTC_CR_ALRBIE_BIT); // turn off ALRBIE
rtc_exit_config_mode();
}
void RTClock::setPeriodicWakeup(uint16 period) {
//-----------------------------------------------------------------------------
void RTClock::setPeriodicWakeup(uint16 period)
{
uint32 t = 0;
rtc_enter_config_mode();
RTC_BASE->CR &= ~(1 << RTC_CR_WUTE_BIT);

View File

@ -1,29 +1,35 @@
#include <libmaple/rcc.h>
#include <libmaple/bitband.h>
#include <libmaple/pwr.h>
#include <libmaple/bkp.h>
#include <libmaple/nvic.h>
#include <libmaple/exti.h>
#include <time.h>
#ifndef _RTCLOCK_H_
#define _RTCLOCK_H_
//#define RTC_DEBUG
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/types.h> // for __time_t_defined
#include <libmaple/rcc.h>
#include <libmaple/bkp.h>
#include <libmaple/exti.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#define RTC_DEBUG
#if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc
#warning "Using private time_t definintion"
typedef uint32_t time_t;
#endif
#ifdef RTC_DEBUG
extern void dbg_printf(const char *fmt, ... );
#define rtc_debug_printf(fmt, ...) dbg_printf(fmt, ##__VA_ARGS__);
#else
#define rtc_debug_printf(...)
#endif
#ifdef __cplusplus
}
#endif
#define SECS_PER_MIN (60UL)
@ -33,7 +39,7 @@ extern "C" {
#define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK)
#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL)
#define SECS_YR_2000 (946684800UL) // the time at the start of y2k
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
static const unsigned char monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0
@ -91,6 +97,28 @@ typedef enum rtc_clk_src {
RTCSEL_HSE = 0x13,
} rtc_clk_src;
// Time register
#define RTC_TR_PM_BIT 22
#define RTC_TR_HOUR_BIT 16
#define RTC_TR_MINUTE_BIT 8
#define RTC_TR_SECOND_BIT 0
#define RTC_TR_PM_MASK (0x01)//<<RTC_TR_PM_BIT)
#define RTC_TR_HOUR_MASK (0x3F)//<<RTC_TR_HOUR_BIT)
#define RTC_TR_MINUTE_MASK (0x7F)//<<RTC_TR_MINUTE_BIT)
#define RTC_TR_SECOND_MASK (0x7F)//<<RTC_TR_SECOND_BIT)
// Date register
#define RTC_DR_YEAR_BIT 16
#define RTC_DR_WEEKDAY_BIT 13
#define RTC_DR_MONTH_BIT 8
#define RTC_DR_DAY_BIT 0
#define RTC_DR_YEAR_MASK (0xFF)//<<RTC_TR_YEAR_BIT)
#define RTC_DR_WEEKDAY_MASK (0x07)//<<RTC_TR_WEEKDAY_BIT)
#define RTC_DR_MONTH_MASK (0x1F)//<<RTC_TR_MONTH_BIT)
#define RTC_DR_DAY_MASK (0x3F)//<<RTC_TR_DAY_BIT)
/* Control Register */
#define RTC_CR_TSIE_BIT 15
@ -118,24 +146,69 @@ typedef enum rtc_clk_src {
#define RTC_ISR_ALRAWF_BIT 0
#define BUILD_TIME_REGISTER(h, m, s) ( ( bin2bcd((h&RTC_TR_HOUR_MASK)) << RTC_TR_HOUR_BIT ) | \
( bin2bcd((m&RTC_TR_MINUTE_MASK)) << RTC_TR_MINUTE_BIT ) | \
( bin2bcd((s&RTC_TR_SECOND_MASK)) << RTC_TR_SECOND_BIT) )
#define BUILD_DATE_REGISTER(y, m, d, wd) ( ( bin2bcd((y&RTC_DR_YEAR_MASK)) << RTC_DR_YEAR_BIT ) | \
( bin2bcd((m&RTC_DR_MONTH_MASK)) << RTC_DR_MONTH_BIT) | \
( bin2bcd((d&RTC_DR_DAY_MASK)) << RTC_DR_DAY_BIT ) | \
( (wd&RTC_DR_WEEKDAY_MASK) << RTC_DR_WEEKDAY_BIT ) )
typedef struct tm_t {
uint8_t year; // years since 1970
uint8_t month; // month of a year - [ 1 to 12 ]
uint8_t day; // day of a month - [ 1 to 31 ]
uint8_t weekday; // day of a week (first day is Monday) - [ 1 to 7 ]
uint8_t pm; // AM: 0, PM: 1
uint8_t hour; // hour of a day - [ 0 to 23 ]
uint8_t minute; // minute of an hour - [ 0 to 59 ]
uint8_t second; // second of a minute - [ 0 to 59 ]
} tm_t;
static inline uint8_t bcd2bin(uint8_t b) { return ( (10*(b>>4)) + (b&0x0F) ); }
static inline uint8_t bin2bcd(uint8_t b) { return ( ((b/10)<<4) + (b%10) ); }
class RTClock {
public:
RTClock();
RTClock(rtc_clk_src src );
public:
RTClock() { RTClock(RTCSEL_LSE, 0, 0); }
RTClock(rtc_clk_src src ) { RTClock(src, 0, 0); }
RTClock(rtc_clk_src src, uint16 sync_prescaler, uint16 async_prescaler);
//~RTClock(); //to implement
void breakTime(time_t epoch_time, tm_t & tm);
time_t makeTime(tm_t & tm);
void setTime (time_t time_stamp);
void setTime (struct tm * tm_ptr);
void setTime (tm_t & tm);
struct tm* getTime(struct tm* tm_ptr);
time_t getTime();
void getTime(tm_t & tm);
#define now getTime
uint8_t year(void) { getTime(tm); return tm.year; }
uint8_t month(void) { getTime(tm); return tm.month; }
uint8_t day(void) { getTime(tm); return tm.day; }
uint8_t weekday(void) { getTime(tm); return tm.weekday; }
uint8_t hour(void) { getTime(tm); return tm.hour; }
uint8_t minute(void) { getTime(tm); return tm.minute; }
uint8_t second(void) { getTime(tm); return tm.second; }
//uint8_t pm(void) { return _pm(RTC_BASE->TR); }
uint8_t isPM(void) { return ( hour()>=12 ); }
void setAlarmATime (tm * tm_ptr, bool hours_match = true, bool mins_match = true, bool secs_match = true, bool date_match = false);
uint8_t year(time_t t) { breakTime(t, tm); return tm.year; }
uint8_t month(time_t t) { breakTime(t, tm); return tm.month; }
uint8_t day(time_t t) { breakTime(t, tm); return tm.day; }
uint8_t weekday(time_t t) { breakTime(t, tm); return tm.weekday; }
uint8_t hour(time_t t) { breakTime(t, tm); return tm.hour; }
uint8_t minute(time_t t) { breakTime(t, tm); return tm.minute; }
uint8_t second(time_t t) { breakTime(t, tm); return tm.second; }
uint8_t isPM(time_t t) { return (hour(t)>=12); }
void setAlarmATime (tm_t * tm_ptr, bool hours_match = true, bool mins_match = true, bool secs_match = true, bool date_match = false);
void setAlarmATime (time_t alarm_time, bool hours_match = true, bool mins_match = true, bool secs_match = true, bool date_match = false);
void turnOffAlarmA();
void setAlarmBTime (tm * tm_ptr, bool hours_match = true, bool mins_match = true, bool secs_match = true, bool date_match = false);
void setAlarmBTime (tm_t * tm_ptr, bool hours_match = true, bool mins_match = true, bool secs_match = true, bool date_match = false);
void setAlarmBTime (time_t alarm_time, bool hours_match = true, bool mins_match = true, bool secs_match = true, bool date_match = false);
void turnOffAlarmB();
@ -143,17 +216,29 @@ class RTClock {
void attachPeriodicWakeupInterrupt(voidFuncPtr function);
void detachPeriodicWakeupInterrupt();
inline void attachSecondsInterrupt(voidFuncPtr function) { attachPeriodicWakeupInterrupt(function); }
inline void detachSecondsInterrupt() { detachPeriodicWakeupInterrupt(); }
void attachAlarmAInterrupt(voidFuncPtr function);
void detachAlarmAInterrupt();
void attachAlarmBInterrupt(voidFuncPtr function);
void detachAlarmBInterrupt();
//private:
} ;
private:
inline uint8_t _year(uint32_t dr) { return bcd2bin( (dr>>RTC_DR_YEAR_BIT) & RTC_DR_YEAR_MASK ); }
inline uint8_t _month(uint32_t dr) { return bcd2bin( (dr>>RTC_DR_MONTH_BIT) & RTC_DR_MONTH_MASK ); }
inline uint8_t _day(uint32_t dr) { return bcd2bin( (dr>>RTC_DR_DAY_BIT) & RTC_DR_DAY_MASK ); }
inline uint8_t _weekday(uint32_t dr) { return bcd2bin( (dr>>RTC_DR_WEEKDAY_BIT) & RTC_DR_WEEKDAY_MASK ); }
inline uint8_t _pm(uint32_t tr) { return ( (tr>>RTC_TR_PM_BIT) & RTC_TR_PM_MASK ); }
inline uint8_t _hour(uint32_t tr) { return bcd2bin( (tr>>RTC_TR_HOUR_BIT) & RTC_TR_HOUR_MASK ); }
inline uint8_t _minute(uint32_t tr) { return bcd2bin( (tr>>RTC_TR_MINUTE_BIT) & RTC_TR_MINUTE_MASK ); }
inline uint8_t _second(uint32_t tr) { return bcd2bin( (tr>>RTC_TR_SECOND_BIT) & RTC_TR_SECOND_MASK ); }
tm_t tm;
};
inline uint32_t getTReg(void) { return (uint32_t)(RTC_BASE->TR); }
inline uint32_t getDReg(void) { return (uint32_t)(RTC_BASE->DR); }
/**
* @brief Clear the register synchronized flag. The flag is then set by hardware after a write to PRL/DIV or CNT.
@ -246,7 +331,8 @@ static inline void rtc_disable_wakeup_event() {
*bb_perip(&EXTI_BASE->RTSR, EXTI_RTC_WAKEUP_BIT) = 0;
}
#ifdef __cplusplus
}
#endif
#endif // _RTCLOCK_H_