From 05e3397d950dbba6cc5a20ac26efda1457e67b30 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Mon, 21 Oct 2024 17:05:26 +0200 Subject: [PATCH] Implement rtc's LSI calibration --- drv/bkp.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++---- drv/bkp.h | 5 ++++- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/drv/bkp.c b/drv/bkp.c index 907b3fc..7b9be0f 100644 --- a/drv/bkp.c +++ b/drv/bkp.c @@ -18,12 +18,14 @@ //--local definitions----------------------------------------------------------- -uint32_t compute_prescaler(uint32_t period_ms, enum BkpRtcClockSrc clock_src); +static uint32_t compute_prescaler(uint32_t period_ms, + enum BkpRtcClockSrc clock_src); +static void lsi_calib_callback(enum TimIRQSource src); //--local variables------------------------------------------------------------- -//static volatile struct BKP* bkp_regs = (struct BKP*)BKP_BASE_ADDRESS; +static volatile struct BKP* bkp_regs = (struct BKP*)BKP_BASE_ADDRESS; static volatile struct RCC* rcc_regs = (struct RCC*)RCC_BASE_ADDRESS; static volatile struct RTC* rtc_regs = (struct RTC*)RTC_BASE_ADDRESS; @@ -103,6 +105,17 @@ uint32_t bkp_read_rtc(void) return time; } +uint32_t bkp_read_rtc_div(void) +{ + //wait for core registers to be synchronized, immediate most of the time + while (rtc_regs->CRL.RSF != 1) {} + + uint32_t div = rtc_regs->DIVH.RTC_DIV << 16; + div |= rtc_regs->DIVL.RTC_DIV << 0; + + return div; +} + void bkp_reset(void) { rcc_regs->BDCR.BDRST = 1; @@ -119,13 +132,44 @@ uint16_t bkp_read_data(enum BkpData data_index) return bkp_regs->DR[data_index].D; } +void bkp_calibrate_lsi(enum TimPeriph timer) +{ + rcc_enable(RCC_AHB_NONE, RCC_APB1_BKP, RCC_APB2_NONE); + + //do not calibrate if already calibrated + if (bkp_read_data(BKP_DATA_LSI_CALIB) != 0) { + return; + } + + //configure timer to count 1s exactly + struct RccClocks clocks; + rcc_get_clocks(&clocks); + + tim_set_prescaler(timer, (clocks.tim_freq / 10000)); //10000 to avoid + //prescaler overflow + tim_set_auto_reload(timer, 10000); + tim_configure_master(timer, TIM_CONFIG_ONE_SHOT + | TIM_CONFIG_DIR_UP, TIM_MASTER_CONFIG_MODE_RESET, + lsi_calib_callback); + + //configure rtc to tick every 2s so the div value isn't reset during the + //timer's delay if the clock is too fast + bkp_configure_rtc(2000, BKP_RTC_CLOCK_SRC_LSI, BKP_RTC_IRQ_NONE, nullptr); + tim_start(timer); + + while (bkp_read_data(BKP_DATA_LSI_CALIB) == 0) {} + + //TODO reset timer +} + //--local functions------------------------------------------------------------- /** * Computes the prescaler value based on the clock source and the required * period */ -uint32_t compute_prescaler(uint32_t period_ms, enum BkpRtcClockSrc clock_src) +static uint32_t compute_prescaler(uint32_t period_ms, + enum BkpRtcClockSrc clock_src) { uint32_t prescaler; @@ -134,7 +178,10 @@ uint32_t compute_prescaler(uint32_t period_ms, enum BkpRtcClockSrc clock_src) prescaler = 32768; //32.768kHz break; case BKP_RTC_CLOCK_SRC_LSI: - prescaler = 40000; //40khz + prescaler = bkp_read_data(BKP_DATA_LSI_CALIB); + if (prescaler == 0) { + prescaler = 40000; //40khz + } break; case BKP_RTC_CLOCK_SRC_HSE: prescaler = 62500; //8Mhz / 128 @@ -146,6 +193,15 @@ uint32_t compute_prescaler(uint32_t period_ms, enum BkpRtcClockSrc clock_src) return (period_ms * prescaler) / 1000; } +static void lsi_calib_callback(enum TimIRQSource src) +{ + if (src == TIM_IRQ_SOURCE_UPDATE) { + //div is decremented from 40kHz * programmed delay (in s) + uint32_t lsi_freq = (40000 * 2) - bkp_read_rtc_div(); + bkp_write_data(BKP_DATA_LSI_CALIB, lsi_freq); + } +} + //--ISRs------------------------------------------------------------------------ diff --git a/drv/bkp.h b/drv/bkp.h index a2bb417..7be7294 100644 --- a/drv/bkp.h +++ b/drv/bkp.h @@ -14,6 +14,7 @@ //--includes-------------------------------------------------------------------- #include "stdint.h" +#include "tim.h" //--type definitions------------------------------------------------------------ @@ -98,6 +99,7 @@ void bkp_set_rtc_alam(uint32_t offset); * Returns the current counter value of the RTC */ uint32_t bkp_read_rtc(void); +uint32_t bkp_read_rtc_div(void); /** * Resets the entire backup domain, composed of everything configured through @@ -108,9 +110,10 @@ void bkp_reset(void); void bkp_write_data(enum BkpData data_index, uint16_t data); uint16_t bkp_read_data(enum BkpData data_index); +void bkp_calibrate_lsi(enum TimPeriph timer); + //unimplemented functions void bkp_configure_tamper(); -void bkp_calibrate_lsi(void); void bkp_configure_lse(bool enable);