Hey all! I decided to get the LC's RTC module "working". Maybe it'll help someone else do something interesting.
Now, to be extremely clear, the reasons Paul and others have decided not to support the LC's RTC are very good - you can't just use an external crystal like with the 3.1/3.2, you actually need a full, power hungry, expensive oscillator. Even if you add that, there is no way to separately power the RTC module (like the VBat connection in 3.1/3.2), so if you lose power, you lose your set time.
With my code, you don't need any external components to be able to sync the RTC and track time, but you will still lose the set time if you lose power, and it's not nearly as accurate as the RTC on the other Teensys.
The way I got this working is to set the RTC module to use the "LPO" clock, which is an internally generated 1 kHz clock, and really not that accurate (way worse than the 20ppm crystal we normally use, anyway). Normally, the RTC is set up so that it increments by one second every 32,768 cycles (which is why we use a 32.768 kHz crystal on the 3.1/3.2). With the LPO, the RTC will run at 1/32.768 times the speed, so a reading of +1 second actually equals +32.768 seconds. This isn't really useful for what I'm doing (though it could be for some), so what I've done is set the prescaler so that the RTC increments by one second every 1000 cycles (so 1 kHz = 1 increment/second). You have to continuously do this, so I used the RTC seconds interrupt to set the prescaler like this every tick, so it might lose a millisecond here and there. If you want, you can certainly write/port compensation functions to try and improve the accuracy.
Overall, it's not pretty, it's not accurate, but it "works".
Here's the library (LC_RTC.h):
Here's some code to see it running:
To use it, you just need to include the library and call LC_RTC_enable(). You can set the time with LC_RTC_set(seconds) (e.g. if you want to sync the clock). I usually just start it at 0.
I'm pretty new to making portable/optimized code, so if anyone has any recommendations/improvements, I'd love to hear them (e.g. if these should be inline/static/private functions, safer ways of banging on hardware registers etc.).
Now, to be extremely clear, the reasons Paul and others have decided not to support the LC's RTC are very good - you can't just use an external crystal like with the 3.1/3.2, you actually need a full, power hungry, expensive oscillator. Even if you add that, there is no way to separately power the RTC module (like the VBat connection in 3.1/3.2), so if you lose power, you lose your set time.
With my code, you don't need any external components to be able to sync the RTC and track time, but you will still lose the set time if you lose power, and it's not nearly as accurate as the RTC on the other Teensys.
The way I got this working is to set the RTC module to use the "LPO" clock, which is an internally generated 1 kHz clock, and really not that accurate (way worse than the 20ppm crystal we normally use, anyway). Normally, the RTC is set up so that it increments by one second every 32,768 cycles (which is why we use a 32.768 kHz crystal on the 3.1/3.2). With the LPO, the RTC will run at 1/32.768 times the speed, so a reading of +1 second actually equals +32.768 seconds. This isn't really useful for what I'm doing (though it could be for some), so what I've done is set the prescaler so that the RTC increments by one second every 1000 cycles (so 1 kHz = 1 increment/second). You have to continuously do this, so I used the RTC seconds interrupt to set the prescaler like this every tick, so it might lose a millisecond here and there. If you want, you can certainly write/port compensation functions to try and improve the accuracy.
Overall, it's not pretty, it's not accurate, but it "works".
Here's the library (LC_RTC.h):
Code:
/* Code to create a working, semi-accurate RTC in the Teensy LC
My strategy:
Make the RTC run off of the 1kHz LPO clock.
This makes the RTC 1/32.768 * normal speed
To fix this, we set the RTC prescaler to add 1 second on every 1k clock cycles
instead of the normal 32768 cycles. Now the RTC is approx. normal speed.
The prescaler would normally just rollover back to 32768 cycles, so we use
an interrupt on every RTC second that resets the prescaler.
*/
#ifndef LC_RTC_h
#define LC_RTC_h
#if defined(KINETISL)
unsigned long LC_RTC_get(){
return RTC_TSR;
}
void LC_RTC_set(unsigned long t){
RTC_SR = 0; // status register - disable RTC, only way to write other registers
RTC_TPR = 31768; // prescaler register, 16bit
RTC_TSR = t; // inits the seconds
RTC_SR = RTC_SR_TCE; // status register again - enable RTC
}
void LC_RTC_enable(){
// Enable write-access for RTC registers
SIM_SCGC6 |= SIM_SCGC6_RTC;
// Disable RTC so we can write registers
RTC_SR = 0;
// Set the prescaler to overflow in 1k cycles
RTC_TPR = 31768;
// Disable the 32kHz oscillator
RTC_CR = 0;
// Set the RTC clock source to the 1kHz LPO
SIM_SOPT1 = SIM_SOPT1 | SIM_SOPT1_OSC32KSEL(3);
// Enable the interrupt for every RTC second
RTC_IER |= 0x10; // set the TSIE bit (Time Seconds Interrupt Enable)
NVIC_ENABLE_IRQ(IRQ_RTC_SECOND);
// Enable RTC
RTC_SR = RTC_SR_TCE;
}
void rtc_seconds_isr(){
// Disable RTC so we can write registers
RTC_SR = 0;
// Set the prescaler to overflow in 1k cycles
RTC_TPR = 31768;
// Enable RTC
RTC_SR = RTC_SR_TCE;
}
#endif
#endif
Here's some code to see it running:
Code:
#include "LC_RTC.h"
void setup(){
Serial.begin(115200);
while(!Serial);
Serial.println("Enabling RTC");
LC_RTC_enable();
Serial.println("Setting t to 100");
LC_RTC_set(100);
Serial.print("Value from the RTC: ");
Serial.println(LC_RTC_get());
Serial.println("Begin RTC!");
}
unsigned long timer = 0;
void loop(){
if(LC_RTC_get() != timer){
timer = LC_RTC_get();
Serial.print("Current time: ");
Serial.print(timer);
Serial.println(" seconds.");
}
}
To use it, you just need to include the library and call LC_RTC_enable(). You can set the time with LC_RTC_set(seconds) (e.g. if you want to sync the clock). I usually just start it at 0.
I'm pretty new to making portable/optimized code, so if anyone has any recommendations/improvements, I'd love to hear them (e.g. if these should be inline/static/private functions, safer ways of banging on hardware registers etc.).
Last edited: