Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
ag88 committed Jun 17, 2019
2 parents 23b3edc + 2e1d0f6 commit d59612e
Show file tree
Hide file tree
Showing 5 changed files with 764 additions and 6 deletions.
245 changes: 245 additions & 0 deletions STM32F1/libraries/RTClock/examples/RTCAdj/RTCAdjust.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
/*
* RTCAdjust.cpp
*
* Created on: Dec 10, 2018
*
* @license MIT use at your own risk
*
* Copyright 2018 andrew goh
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <Arduino.h>
#include "rtadjust.h"

/* note that the adjustment functions needs this RTClock
* if you rename variable rt, update rtadjust.h so that the
* extern refers to this RTClock*/
RTClock rt(RTCSEL_LSE); // initialise

#define BUFLEN 100
uint8_t ind = 0;
char cmd = 0;
char buf[BUFLEN];
tm_t tm;

enum LineState {
KEY,
LINE
} state;

void processkey();
void showtime();
void synctime(char *buf, int len);
void settime(char *buf, int len);
void calibrate(char *buf, int len);
void setdriftdur(char *buf, int len);
void help();
void clearbuf();

void setup() {
state = LineState::KEY;

Serial.begin(115200);

/* initialise access to backup registers,
* this is necessary due to the use of backup registers */
bkp_init();

/* adjust rtc */
adjtime();

//turn on the led basic check
//pinMode(LED_BUILTIN, OUTPUT);
//digitalWrite(LED_BUILTIN, HIGH);
}

void loop() {
if(Serial.available())
processkey();
else
delay(1);
}

void processkey() {
char c = Serial.read();
//echo the char;
Serial.print(c);
if( state == LineState::LINE ) {
if (ind < BUFLEN && c != '\r') {
if(c == 8 || c == 127 ) // backspace
ind--;
else
buf[ind++] = c; // copy the chars into buf
} else {
switch(cmd) {
case 's':
synctime(buf,ind);
break;
case 'T':
settime(buf,ind);
break;
case 'c':
calibrate(buf,ind);
break;
case 'x':
setdriftdur(buf,ind);
break;
default:
break;
}
state = LineState::KEY;
cmd = 0;
clearbuf();
}
} else {
switch(c) {
case 't':
showtime();
break;
case 's':
state = LineState::LINE;
clearbuf();
cmd = 's';
break;
case 'T':
state = LineState::LINE;
clearbuf();
cmd = 'T';
break;
case 'c':
state = LineState::LINE;
clearbuf();
cmd = 'c';
break;
case 'x':
state = LineState::LINE;
clearbuf();
cmd = 'x';
break;
case 'h':
case 'H':
case '?':
help();
break;
default:
break;
}
}
}


void showtime() {
// get and print actual RTC timestamp
rt.breakTime(rt.now(), tm);
memset(buf,0,BUFLEN);
sprintf(buf, "RTC timestamp: %u-%u-%u, %02u:%02u:%02u",
tm.year+1970, tm.month, tm.day, tm.hour, tm.minute, tm.second);
Serial.println(buf);
clearbuf();

Serial.print("last adj:");
rt.breakTime(getbkptime(), tm);
memset(buf,0,BUFLEN);
sprintf(buf, "RTC timestamp: %u-%u-%u, %02u:%02u:%02u",
tm.year+1970, tm.month, tm.day, tm.hour, tm.minute, tm.second);
Serial.println(buf);
clearbuf();

Serial.print(F("drift duration, number of seconds for the stm32 rtc to drift 1 secs (faster):"));
Serial.println(getdrift());

Serial.print(F("BKP_RTCCR:"));
Serial.println(getrtccr());
}

void synctime(char *buf, int len) {
if (len == BUFLEN) buf[BUFLEN-1] = 0; //terminate the string for safety
if(parsetimestamp(buf, tm) <0) {
Serial.println(F("invalid date/time"));
return;
}

time_t time = rt.makeTime(tm);
/* this call the actual function to set the RTC and update
* the last adjusted time simultaneously
*/
synctime(time);
}



void calibrate(char *buf, int len) {
if (len == BUFLEN) buf[BUFLEN-1] = 0; //terminate the string for safety
if(parsetimestamp(buf, tm) <0) {
Serial.println(F("invalid date/time"));
return;
}

time_t time = rt.makeTime(tm);

/* this call the calibratertc() function to calculate
* the drift duration
*/
calibratertc(time);
}

/*
* this function sets the rtc directly by-passing all the adjustments
*
* note that this function is used during tests to simulate drifts etc
* hence it is not features in help();
*
* in a normal context use synctime() to set the RTC time so that
* the last adjustment date/time is updated as well
*/
void settime(char *buf, int len) {
if (len == BUFLEN) buf[BUFLEN-1] = 0; //terminate the string for safety
if(parsetimestamp(buf, tm) <0) {
Serial.println(F("invalid date/time"));
return;
}

rt.setTime(tm);
}

void setdriftdur(char *buf, int len) {
if (len == BUFLEN) buf[BUFLEN-1] = 0; //terminate the string for safety
int16_t drift_dur = atoi(buf);
/* this funciton updates the drift duration directly */
setbkpdrift(drift_dur);
}

void help() {
Serial.println(F("h, H, ? - display help, this message"));
Serial.println(F("t - current time"));
Serial.println(F("sYYYY-MM-DD HH:MM:SS - sync/set time"));
Serial.println(F("cYYYY-MM-DD HH:MM:SS - calibrate rtc"));
Serial.println(F("where YYYY-MM-DD HH:MM_SS - is the current accurate time"));
Serial.println(F("xnnnn - set the drift duration where nnnn is the drift duration"));
Serial.println(F(" number of seconds for the stm32 rtc to drift 1 secs (faster)"));
Serial.println(F("the s, c, x commands needs to end with a carriage return at the end of line"));
Serial.println();
}

void clearbuf() {
ind = 0;
memset(buf,0,BUFLEN);
}

131 changes: 131 additions & 0 deletions STM32F1/libraries/RTClock/examples/RTCAdj/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# stm32duino RTC drift adjustment

This Arduino sketch is built based on Stm32duino libmaple core.
It provides an example of how one could adjust for RTC (real time clock) drifts if one is using a 32k crystal that drift significantly.

For drifts that is less than (304 secs ~ 5 minutes) per month. STM has published an appnote for a different means of slowing down the RTC clock
[AN2604 STM32F101xx and STM32F103xx RTC calibration](https://www.st.com/content/ccc/resource/technical/document/application_note/ff/c1/4f/86/4e/29/42/d1/CD00167326.pdf/files/CD00167326.pdf/jcr:content/translations/en.CD00167326.pdf). This is possibly simpler and more accurate. *this feature has been added in this implementation*

Due to the use of backup registers, you need to power the board/stm32 on VBAT (e.g. using a coin cell) so that the backup memory is maintained. And as the last adjusted date/time (saved in backup register 8 and 9) and drift duration (saved in backup register 7), if power is removed and the backup memory is lost, you would need to re-do the calibration again.

## Building and running the sketch

Build and run the sketch based on STM32duino libmaple core using the Arduino IDE.
The files relevant to the sketch are:

* RTCAdjust.ino - this is the sketch itself
* rtadjust.h - utility functions for the adjustments
* rtadjust.cpp - utility functions for the adjustments

The sketch is a demo that runs on the serial console, the commands on the serial console are

* h, H, ? - display help this message
* t - display current time
* sYYYY-MM-DD HH:MM:SS - sync/set time
* cYYYY-MM-DD HH:MM:SS - calibrate rtc
where YYYY-MM-DD HH:MM_SS - is the current accurate time
* xnnnn - set the drift duration where nnnn is the drift duration
number of seconds for the stm32 rtc to drift 1 secs (faster)

t command displays the time and some additional info related to the adjustments e.g.
```
RTC timestamp: 2018-12-10, 00:00:01
last adj:RTC timestamp: 2018-12-10, 00:00:00
drift duration, number of seconds for the stm32 rtc to drift 1 secs (faster): 3350
```
## How do the adjustments work

In your wiring/arduino setup() function, make it call the function *adjtime();*, e.g.
```
void setup() {
/* initialise access to backup registers,
* this is necessary due to the use of backup registers */
bkp_init();
/* adjust rtc */
adjtime();
}
```
This function would make RTC adjustments based a calibrated drift duration or offset which i named as the drift duration. The drift duration is number of seconds for the stm32 rtc to drift 1 seconds (faster)


## How to do the calibrations using the demo sketch / app

This app/sketch itself has the commands to do the calibration. To do the calibration:

1. run sYYYY-MM-DD HH:MM:SS - sync/set time
where YYYY-MM-DD HH:MM_SS - is the current accurate date/time
This would set the RTC and the last adjustment date/time to the date / time you supplied.
The last adjustment date/time is saved in 2 backup registers 8 and 9.

2. after a day or more, a longer period cumulates more drift and increase accuracy
run cYYYY-MM-DD HH:MM:SS - calibrate rtc
where YYYY-MM-DD HH:MM_SS - is the current accurate date/time at the time you do the calibration.

This would compute the drift duration and save it in backup register 7.

3. reset the device and check the time after the calibration. if setup() calls adjtime(), the RTC would show the drift adjusted/compensated date/time.

## How to do the adjustments and calibrations work in actual code?

1. in setup() run adjtime()
```
void setup() {
/* initialise access to backup registers,
* this is necessary due to the use of backup registers */
bkp_init();
/* adjust rtc */
adjtime();
}
```
This would make the adjustments every time the sketch is started.
Note that adjtime() does the adjustment no more than once in 24 hours. This design is to prevent frequent adjustments which could result in cumulative errors as the granuality of adjustments is 1 sec. if you prefer for the adjustments to be more frequent you could comment the statement in the function.

2. The function to set the RTC and last adjusted date time is:
```
/* synctime() set the RTC clock and saves the time in backup register 8 and 9
* as the time of the last adjustment
*
* call this function with the current accurate clock time to set the rtc
*
* @param time the time_t value of the current accurate clock time
*/
void synctime(time_t time_now);
```

3. The function to perform the RTC calibration to compute the drift duration is
```
/* this function calibrate the rtc by computing the drift_duration
*
* call this function with the current accurate clock time to calibrate the rtc
*
* if the cumulative delay between current time and the last time when synctime()
* is called is lower than 100, a warning would be displayed that the drift
* granulity is low and may result in inaccuracy of the rtc adjustments
*
* note that this function can only be run once to compute the drift_duration
* this is because the time of last adjustment would have been updated
* by adjtime() after calibration and is no longer relevant for purpose of
* computing drift duration
*
* to run it again
* 1) first zero out drift duration using setbkpdrift(0)
* or disconnect VBAT and power to clear backup memory
* next repeat the calibration cycle
* 2) run synctime(time_now) with the accurate clock time
* 3) after a day or more (longer period cumulates more drift and increase accuracy)
* run calibrate(time_now) with the accurate clock time
*
* @param time the time_t value of the current accurate clock time
*/
void calibratertc(time_t time_now);
```

4. Dependencies, in the main sketch:
```
/* note that the adjustment functions needs this RTClock
* if you rename variable rt, update rtadjust.h so that the
* extern refers to this RTClock*/
RTClock rt(RTCSEL_LSE); // initialise
```
Loading

0 comments on commit d59612e

Please sign in to comment.