-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of https://github.com/rogerclarkmelbourne/Ardui…
- Loading branch information
Showing
5 changed files
with
764 additions
and
6 deletions.
There are no files selected for viewing
245 changes: 245 additions & 0 deletions
245
STM32F1/libraries/RTClock/examples/RTCAdj/RTCAdjust.ino
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` |
Oops, something went wrong.