/** @file bkp.c * Module handling the Backup (BKP) domain functionalities. * * This module includes management of the Real Time Clock (RTC), Tamper * protection system and a limited space (20 bytes) for persistent data storage * * All features present in this module stay running in standby mode / when * no-longer powered, so long as VBAT is maintained */ //--includes-------------------------------------------------------------------- #include "bkp.h" #include "bkp_regs.h" #include "rcc.h" #include "nvic.h" //--local definitions----------------------------------------------------------- uint32_t compute_prescaler(uint32_t period_ms, enum BkpRtcClockSrc clock_src); //--local variables------------------------------------------------------------- //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; static BkpRtcCallback rtc_callback; //--public functions------------------------------------------------------------ void bkp_configure_rtc(uint32_t period_ms, enum BkpRtcClockSrc clock_src, enum BkpRtcIrq irq_mask, BkpRtcCallback callback) { rcc_enable(RCC_AHB_NONE, RCC_APB1_BKP, RCC_APB2_NONE); //start RTC rcc_regs->BDCR.RTCSEL = clock_src + 1; rcc_regs->BDCR.RTCEN = 1; uint32_t prescaler = compute_prescaler(period_ms, clock_src); //wait for registers to synchronize rtc_regs->CRL.RSF = 0; while (rtc_regs->CRL.RSF != 1) {} //wait for last operation to finish while (rtc_regs->CRL.RTOFF != 1) {} //enable core configuration rtc_regs->CRL.CNF = 1; //configure core registers rtc_regs->PRLH.PRL = prescaler >> 16; rtc_regs->PRLL.PRL = prescaler; //apply irq config rtc_regs->CRH.word |= irq_mask & 0x7; //disable/apply core configuration rtc_regs->CRL.CNF = 0; //wait for last operation to finish while (rtc_regs->CRL.RTOFF != 1) {} if (callback) { rtc_callback = callback; nvic_enable(NVIC_IRQ_RTC); } } void bkp_set_rtc_alam(uint32_t offset) { uint32_t alarm = bkp_read_rtc() + offset; //wait for last operation to finish while (rtc_regs->CRL.RTOFF != 1) {} //enable core configuration rtc_regs->CRL.CNF = 1; //write alarm value rtc_regs->ALRH.RTC_ALR = alarm >> 16; rtc_regs->ALRL.RTC_ALR = alarm; //disable/apply core configuration rtc_regs->CRL.CNF = 0; //wait for last operation to finish while (rtc_regs->CRL.RTOFF != 1) {} } uint32_t bkp_read_rtc(void) { //wait for core registers to be synchronized, immediate most of the time while (rtc_regs->CRL.RSF != 1) {} uint32_t time = rtc_regs->CNTH.RTC_CNT << 16; time |= rtc_regs->CNTL.RTC_CNT << 0; return time; } void bkp_reset(void) { rcc_regs->BDCR.BDRST = 1; rcc_regs->BDCR.BDRST = 0; } //--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) { uint32_t prescaler; switch (clock_src) { case BKP_RTC_CLOCK_SRC_LSE: prescaler = 32768; //32.768kHz break; case BKP_RTC_CLOCK_SRC_LSI: prescaler = 40000; //40khz break; case BKP_RTC_CLOCK_SRC_HSE: prescaler = 62500; //8Mhz / 128 break; default: return 0; } return (period_ms * prescaler) / 1000; } //--ISRs------------------------------------------------------------------------ void hdr_rtc(void) { nvic_clear_pending(NVIC_IRQ_RTC); //copy and clear and pass along src flags enum BkpRtcIrq src = rtc_regs->CRL.word & 0x7; rtc_regs->CRL.word &= ~(0x7); rtc_callback(src); }