Compensate for LSE error

Post here first, or if you can't find a relevant section!
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: Compensate for LSE error

Post by ag123 »

one 'simple' but rather inaccurate way to measure drift on the rtc is to compare it against the HFE crystal e.g.8mhz assuming that that is more accurate compared to a highly inaccurate 32k crystal. i think it is possible for rtc to generate per second interrupts. and for stm32 the timers are basically counters and one could use them to make the fine counts, memory can supplement the registers so that with that one could emulate 32 bit counters.

so by starting the rtc interrupts and say counting 100 rtc seconds. one could run the hardware timer counters in sync and stop the hardware timer at the end of the intended measurement e.g. 100 rtc seconds.

As the timer clock frequencies are known e.g. sysclock / 2, then there should be sysclock / 2 * 100 (seconds) counts in the hardware timer if they are both in sync.if the hardware timer is more, presumably rtc clock is slower and vice versa.that'd give one an idea of the drift in the lse crystal.

i went with the much slower way with rtcadjust as i assumed that the rtc drift isn't systemically the same say over a few days, perhaps differences due to temperatures etc. that'd give an average error that somewhat includes uncertainties.
umejopa
Posts: 9
Joined: Sat Oct 16, 2021 7:37 pm
Location: Stockholm

Re: Compensate for LSE error

Post by umejopa »

I hade same problem with Olimex F3 board. On other board BlackPill F303 it work with as i should
The frekvens from cristal was not stabel .
The found you can make drive stronger an then I get right frekvens.
And could use the the hardware compensate in RTC->CALR register.
Here is som exempel code ( Sorry som comment in swedish)
Will work for most F3 ,F4 ,F7 that have the RTC
/**
******************************************************************************
* @file RTCClockSelection.ino
* @author WI6LABS
* @version V1.0.0
* @date 15-March-2018
* @brief RTC clock selection: LSI, LSE or HSE. Refer to board datasheet to
* know available clock.
*
******************************************************************************
* @attention
*
* <h2><center>© COPYRIGHT(c) 2018 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/

#include <STM32RTC.h>

#define predivA 127 // Std 127+1 Verkar vara 32Khz med - tolerans kristal Test 31949
//( 99+1 vid test utgång för se kvarts frekvens
#define predivS 255 // Std 255+1 prov att snabba på klocka (318)

#define RECALPF_TIMEOUT ((uint32_t) 0x00001000)

#define RCC_LSEDrive_Low ((uint32_t)0x00000000)
#define RCC_LSEDrive_MediumLow RCC_BDCR_LSEDRV_0
#define RCC_LSEDrive_MediumHigh RCC_BDCR_LSEDRV_1
#define RCC_LSEDrive_High RCC_BDCR_LSEDRV
#define IS_RCC_LSE_DRIVE(DRIVE) (((DRIVE) == RCC_LSEDrive_Low) || ((DRIVE) == RCC_LSEDrive_MediumLow) || \
((DRIVE) == RCC_LSEDrive_MediumHigh) || ((DRIVE) == RCC_LSEDrive_High))
// HAL DEF.
//#define IS_RCC_LSE_DRIVE(__DRIVE__) (((__DRIVE__) == RCC_LSEDRIVE_LOW) || \
// ((__DRIVE__) == RCC_LSEDRIVE_MEDIUMLOW) || \
// ((__DRIVE__) == RCC_LSEDRIVE_MEDIUMHIGH) || \
// ((__DRIVE__) == RCC_LSEDRIVE_HIGH))



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

/* Change these values to set the current initial time */
const byte seconds = 12;
const byte minutes = 37;
const byte hours = 19;

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

int hrtc;
char TID_Prog [5];

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

// Select RTC clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK.
// By default the LSI is selected as source.
rtc.setClockSource(STM32RTC::LSE_CLOCK);



rtc.begin(); // initialize RTC 24H format

/* Set RTC state */


uint32_t recalpfcount = 0;
// __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_MEDIUMLOW); // funkar också bilr rätt enligt referens manuall
// Sätt drivstyrka RTC Ossilator
RCC_LSEDriveConfig(RCC_LSEDrive_MediumLow); // Detta blir MediumHigh

/* Disable the write protection for RTC registers */

RTC->WPR = 0xCA;
RTC->WPR = 0x53;

// Kolla om upptagen
/* check if a calibration is pending*/
if ((RTC->ISR & RTC_ISR_RECALPF) != RESET)
{
/* wait until the Calibration is completed*/
while (((RTC->ISR & RTC_ISR_RECALPF) != RESET) && (recalpfcount != RECALPF_TIMEOUT))
{
recalpfcount++;
}
}

/* check if the calibration pending is completed or if there is no calibration operation at all*/
if ((RTC->ISR & RTC_ISR_RECALPF) == RESET)
{
/* Configure the Smooth calibration settings */
RTC->CALR = 0x00000000 ; //(uint32_t)((uint32_t)RTC_SmoothCalibPeriod | (uint32_t)RTC_SmoothCalibPlusPulses | (uint32_t)RTC_SmouthCalibMinusPulsesValue);

// status = SUCCESS;
}
// else
// {
// status = ERROR;
// }

/* Enable the write protection for RTC registers */
// __HAL_RTC_WRITEPROTECTION_ENABLE(hrtc);

RTC->WPR = 0xFF;

// 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);

IS_RTC_CALIB_OUTPUT(RTC_CALIBOUTPUT_1HZ);


}

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

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

if (Serial.available()) {
Serial.readBytes(TID_Prog,5);

Serial.println (TID_Prog);
if (TID_Prog[0] == '&') // L
if (TID_Prog[1] == 'T') // E
if (TID_Prog[2] == 'I') //D
if (TID_Prog[3] == 'D') // E
if (TID_Prog[4] == '%') //D
{
Serial.println (" Tid Prog Start ");
// int Ar = Serial.parseInt();
// int Manad = Serial.parseInt();
// int Dag = Serial.parseInt();
// int Veckodag = Serial.parseInt();
int Timme = Serial.parseInt();
int Minut = Serial.parseInt();
int Sekund = Serial.parseInt();

if (Serial.read() == '\n') {

rtc.setTime(Timme, Minut, Sekund);
// rtc.setDate(Veckodag, Dag, Manad, A&TID%r);

}
}
}
delay(1000);

}


void print2digits(int number) {
if (number < 10) {
Serial.print("0"); // print a 0 before if the number is < than 10
}
Serial.print(number);
}

/**
* @brief Configures the External Low Speed oscillator (LSE) drive capability.
* @param RCC_LSEDrive: specifies the new state of the LSE drive capability.
* This parameter can be one of the following values:
* @arg RCC_LSEDrive_Low: LSE oscillator low drive capability.
* @arg RCC_LSEDrive_MediumLow: LSE oscillator medium low drive capability.
* @arg RCC_LSEDrive_MediumHigh: LSE oscillator medium high drive capability.
* @arg RCC_LSEDrive_High: LSE oscillator high drive capability.
* @retval None
*/
void RCC_LSEDriveConfig(uint32_t RCC_LSEDrive)
{
/* Check the parameters */
assert_param(IS_RCC_LSE_DRIVE(RCC_LSEDrive));

/* Clear LSEDRV[1:0] bits */
RCC->BDCR &= ~(RCC_BDCR_LSEDRV);

/* Set the LSE Drive */
RCC->BDCR |= RCC_LSEDrive;
}

/**
* @brief Configures the Smooth Calibration Settings.
* @param RTC_SmoothCalibPeriod : Select the Smooth Calibration Period.
* This parameter can be can be one of the following values:
* @arg RTC_SmoothCalibPeriod_32sec : The smooth calibration periode is 32s.
* @arg RTC_SmoothCalibPeriod_16sec : The smooth calibration periode is 16s.
* @arg RTC_SmoothCalibPeriod_8sec : The smooth calibartion periode is 8s.
* @param RTC_SmoothCalibPlusPulses : Select to Set or reset the CALP bit.
* This parameter can be one of the following values:
* @arg RTC_SmoothCalibPlusPulses_Set : Add one RTCCLK puls every 2**11 pulses.
* @arg RTC_SmoothCalibPlusPulses_Reset: No RTCCLK pulses are added.
* @param RTC_SmouthCalibMinusPulsesValue: Select the value of CALM[8:0] bits.
* This parameter can be one any value from 0 to 0x000001FF.
* @retval An ErrorStatus enumeration value:
* - SUCCESS: RTC Calib registers are configured
* - ERROR: RTC Calib registers are not configured
*/
ErrorStatus RTC_SmoothCalibConfig(uint32_t RTC_SmoothCalibPeriod,
uint32_t RTC_SmoothCalibPlusPulses,
uint32_t RTC_SmouthCalibMinusPulsesValue)
{
ErrorStatus status = ERROR;
uint32_t recalpfcount = 0;

/* Check the parameters */
assert_param(IS_RTC_SMOOTH_CALIB_PERIOD(RTC_SmoothCalibPeriod));
assert_param(IS_RTC_SMOOTH_CALIB_PLUS(RTC_SmoothCalibPlusPulses));
assert_param(IS_RTC_SMOOTH_CALIB_MINUS(RTC_SmouthCalibMinusPulsesValue));

/* Disable the write protection for RTC registers */
RTC->WPR = 0xCA;
RTC->WPR = 0x53;

/* check if a calibration is pending*/
// if ((RTC->ISR & RTC_ISR_RECALPF) != RESET)
// {
/* wait until the Calibration is completed*/
// while (((RTC->ISR & RTC_ISR_RECALPF) != RESET) && (recalpfcount != RECALPF_TIMEOUT))
// {
// recalpfcount++;
// }
// }

/* check if the calibration pending is completed or if there is no calibration operation at all*/
if ((RTC->ISR & RTC_ISR_RECALPF) == RESET)
{
/* Configure the Smooth calibration settings */
RTC->CALR = (uint32_t)((uint32_t)RTC_SmoothCalibPeriod | (uint32_t)RTC_SmoothCalibPlusPulses | (uint32_t)RTC_SmouthCalibMinusPulsesValue);

status = SUCCESS;
}
else
{
status = ERROR;
}

/* Enable the write protection for RTC registers */
RTC->WPR = 0xFF;

return (ErrorStatus)(status);
}

/**
* @}
*/
benkayle9
Posts: 1
Joined: Thu Nov 04, 2021 9:58 pm

Re: Compensate for LSE error

Post by benkayle9 »

I have never tried to use the RTC on any STM32,

so I did not even know it was possible to compensate for crystal frequency.

However, by taking a quick look at the link you were given above by ag123,

and the AN2604 note that is mentioned there,

it seems the register you seek is BKP_RTCCR. Due to the way that works,

it looks like you might also have to change the RTC prescaler to allow for both "faster" or "slower" adjustments.

Given you are using the official STM32 core,

you can't just pick up the code given there,

but before attacking the registers directly,

you could maybe look to see what HAL functions are provided.
Post Reply

Return to “General discussion”