stm32duino / STM32RTC

Arduino RTC library for STM32.
125 stars 48 forks source link

VBAT powered LSE_CLOCK not keeping time with STM32F407VET6 #96

Closed pazi88 closed 11 months ago

pazi88 commented 11 months ago

Board used:

STM32F4VE Black (STM32F407VET6 board with brand new CR1220 battery installed)

Environment:

ST STM32 16.0.0 (2.5.0 core) and 2.6.0 core on Arduino IDE (other versions tried too. Same issue)

Here's the code I'm using, which is modified version on the SimpleRTC:

#include <STM32RTC.h>

/* Get the rtc object */
STM32RTC& rtc = STM32RTC::getInstance();

/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 0;
const byte hours = 16;

/* Change these values to set the current initial date */
/* Monday 15th June 2015 */
const byte weekDay = 1;
const byte day = 15;
const byte month = 6;
const byte year = 15;

void setup()
{
  Serial.begin(9600);

  if (rtc.isTimeSet()) {
    rtc.setClockSource(STM32RTC::LSE_CLOCK); //Initialise external clock for RTC if clock is set. That is the only clock running of VBAT
  }
  rtc.begin(); // initialize RTC 24H format

  // you can use also
  //rtc.setTime(hours, minutes, seconds);
  //rtc.setDate(weekDay, day, month, year);
}

void loop()
{
  // Print date...
  Serial.printf("%02d/%02d/%02d ", rtc.getDay(), rtc.getMonth(), rtc.getYear());

  // ...and time
  Serial.printf("%02d:%02d:%02d.%03d\n", rtc.getHours(), rtc.getMinutes(), rtc.getSeconds(), rtc.getSubSeconds());

  delay(2000);
  if (!rtc.isTimeSet()) {
    rtc.end();
    rtc.setClockSource(STM32RTC::LSE_CLOCK);
    rtc.begin();
    // Set the time
    rtc.setHours(hours);
    rtc.setMinutes(minutes);
    rtc.setSeconds(seconds);

    // Set the date
    rtc.setWeekDay(weekDay);
    rtc.setDay(day);
    rtc.setMonth(month);
    rtc.setYear(year);
  }
}

This code works nicely on 1.2.0 library version. But on any newer library version, it fails to keep track on time when running from VBAT. The time always stays where it was when power was removed from the board. And only updates when board is powered on. When using 1.2.0 library version the time is updated correctly also when the board is not powered, so it's only on VBAT.

fpistm commented 11 months ago

ST STM32 16.0.0 (other versions tried too. Same issue)

What is this version? If PIO, no support provided as STM32Core not updated.

pazi88 commented 11 months ago

ST STM32 16.0.0 (other versions tried too. Same issue)

What is this version? If PIO, no support provided as STM32Core not updated.

Yes for PIO: https://github.com/platformio/platform-ststm32 and it uses 2.5.0 core version. I just tried on Arduino IDE and using the latest 2.6.0 core version. And same thing. 1.2.0 is the newest RTC library version that this works.

fpistm commented 11 months ago

That's strange, I've tested and it works.

pazi88 commented 11 months ago

That's strange, I've tested and it works.

Everything else seems to work but running the RTC from the VBAT only doesn't work on the newer library version. Which means using the LSE_CLOCK, because it's the only one that is powered from VBAT.

fpistm commented 11 months ago

I understood anyway, I can't reproduce.

pazi88 commented 11 months ago

I understood anyway, I can't reproduce.

With the code I posted and same board or mcu? For me the time doesn't reset or anything. But if I leave the board on the table without power and come back in like 15 minutes, the RTC still shows 16:00:something and not 16:15:something.

fpistm commented 11 months ago

This one: https://github.com/mcauser/BLACK_F407VE

fpistm commented 11 months ago

Hi @pazi88 Had some time to investigate with your code. Your code is not correct, try this one:

#include <STM32RTC.h>

/* Get the rtc object */
STM32RTC& rtc = STM32RTC::getInstance();

/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 0;
const byte hours = 16;

/* Change these values to set the current initial date */
/* Monday 15th June 2015 */
const byte weekDay = 1;
const byte day = 15;
const byte month = 6;
const byte year = 15;

void setup() {
  Serial.begin(9600);

  rtc.setClockSource(STM32RTC::LSE_CLOCK);  //Initialise external clock for RTC if clock is set. That is the only clock running of VBAT
  rtc.begin();                              // initialize RTC 24H format
  if (!rtc.isTimeSet()) {
    // Set the time
    rtc.setHours(hours);
    rtc.setMinutes(minutes);
    rtc.setSeconds(seconds);

    // Set the date
    rtc.setWeekDay(weekDay);
    rtc.setDay(day);
    rtc.setMonth(month);
    rtc.setYear(year);
  }
  // you can use also
  //rtc.setTime(hours, minutes, seconds);
  //rtc.setDate(weekDay, day, month, year);
}

void loop() {
  // Print date...
  Serial.printf("%02d/%02d/%02d ", rtc.getDay(), rtc.getMonth(), rtc.getYear());

  // ...and time
  Serial.printf("%02d:%02d:%02d.%03d\n", rtc.getHours(), rtc.getMinutes(), rtc.getSeconds(), rtc.getSubSeconds());

  delay(2000);
}
fpistm commented 11 months ago

Here the result: image

As you can see thanks timestamp the RTC keeps running as expected and the duration is 2 hours, 26 minutes and 14 seconds.

pazi88 commented 11 months ago

Have you tried that code without the RTC battery installed? If you don't have it in, the board will fail to boot. Which is big no-no in my books. The device needs to be able to boot if the RTC battery has died. That's why the code uses the default clock source and only uses the LSE_CLOCK if time is set (this only happens when battery is installed). The LSE_CLOCK will take roughly 2 seconds to come up if the RTC battery isn't installed and at least with USB-CDC that 2-second wait will cause failed boot.

fpistm commented 11 months ago

Have you tried that code without the RTC battery installed? If you don't have it in, the board will fail to boot.

Yes and it works with my code, the board well init at 16:00:00 which is normal if the battery is died or not present always using LSE. Don't know why you want to fallback on LSI.

Your code is not correct, for example:

  if (rtc.isTimeSet()) {
    rtc.setClockSource(STM32RTC::LSE_CLOCK); //Initialise external clock for RTC if clock is set. That is the only clock running of VBAT
  }

will always be false and so setclockSource will be never called.

pazi88 commented 8 months ago

Sorry to comment this so late. The suggested code doesn't work for my purpose. On 1.4.0 library it works. But the problem is that the board takes 2 seconds to boot if there is no RTC battery installed. And that's a big problem. It needs to boot up immediately as it does with RTC battery installed. That's because starting up the LSE_CLOCK takes roughly that 2 seconds and for that time the code is stuck. That's the reason why I had that fallback to LSI clock, because it doesn't take time to get it up. And then only use the LSE clock if the time is set (basically meaning that RTC battery is there and time is set). Isn't there any way to know that if LSE clock needs to be used, before setting the rtc.begin()?

Again. On 1.2.0 FW my code works.

fpistm commented 8 months ago

Hi @pazi88 We made several fixes with the last release which harden RTC. Even if your use case worked within 1.2.0 I guess it was a side effect. Now, the fact LSE took 2 seconds to boot is not linked to the RTC library. As example the LSE_TIMEOUT_VALUE for F4 is by default set to 5000 ms. Switching RTC clock source requires to reinit it. And as stated your code is not correct:

 void setup()
{
  Serial.begin(9600);

 /* Here RTC instance is not initialized so it will always return false. */
/*********/
  if (rtc.isTimeSet()) {
/*********/
    rtc.setClockSource(STM32RTC::LSE_CLOCK); //Initialise external clock for RTC if clock is set. That is the only clock running of VBAT
  }
  rtc.begin(); // initialize RTC 24H format

So you always start using HSI if no VBAT. If VBAT then it is the one configured used as clock source (LSE).

For your loop, if no VBAT, you should start with no time set then you set the LSE as clock source. Then next loop will never pass to this code as the time is set and while the VBAT are present LSE is used.

If you want to speed up the start you can try to redefine the system core clock config to enable the LSE sooner.

pazi88 commented 8 months ago

Yes it's not from the library. The datasheet mentions the bootup time and it comes from the hardware. That's why I try to go around that limitation in code. The code will use the LSE only if the RTC has already been set which means that the LSE is running and there is no bootup time. If the RTC has not been set, it uses LSI. And later when RTC is set, it then re-initializes the RTC to use LSE. The delay later is not a problem (my example is just RTC parts from much bigger project).

I can see that the 1.2.0 library uses (getBackupRegister(RTC_BKP_INDEX) == RTC_BKP_VALUE) for the rtc.isTimeSet. And that's why it works before rtc.begin() is called to tell if the time has been set. The later library versions seem to use method inside the library to define that and, that's why rtc.isTimeSet always returns false if called before rtc.begin(). Is there reason why (getBackupRegister(RTC_BKP_INDEX) == RTC_BKP_VALUE) is not used anymore?

fpistm commented 8 months ago

Yes, as stated we made lot of fixes, it was not the correct behavior. Example, if battery was dead then the RTC is not set while with the backup register it could be set to true. The correct way to get the status is to call begin() then the isTimeSet(). In that case you have the correct status. When you call the begin(), the RTC IP allows to know if it is really initialized or not.