Compare commits

...

3 Commits

Author SHA1 Message Date
80c027370b Document BKP module 2024-07-27 22:13:46 +02:00
b1d25561b4 Temporarily fix warning 2024-07-27 21:57:53 +02:00
507f1e6863 Move rtc control to new BKP module
RCC's BCDR register has been moved to the BKP module since it is part of
the backup circuit and thus also aboeys some restrictions access-wise
2024-07-27 20:11:51 +02:00
8 changed files with 291 additions and 212 deletions

128
drv/bkp.c Normal file
View File

@ -0,0 +1,128 @@
/** @file bkp.c
* Module handling the Backup (BKP) domain functionalities.
*
*/
//--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, uint32_t alarm_tick, 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;
rtc_regs->ALRH.RTC_ALR = alarm_tick >> 16;
rtc_regs->ALRL.RTC_ALR = alarm_tick;
//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);
}
}
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 = 32768000; //32.768kHz
break;
case BKP_RTC_CLOCK_SRC_LSI:
prescaler = 40000000; //40khz
break;
case BKP_RTC_CLOCK_SRC_HSE:
prescaler = 62500000; //8Mhz / 128
break;
default:
return 0;
}
return prescaler / period_ms;
}
//--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);
}

85
drv/bkp.h Normal file
View File

@ -0,0 +1,85 @@
/** @file bkp.h
* Module handling the Backup (BKP) domain functionalities.
*
*/
#ifndef _BKP_H_
#define _BKP_H_
//--includes--------------------------------------------------------------------
#include "stdint.h"
//--type definitions------------------------------------------------------------
/**
* Available clock sources for the RTC. See bkp_configure_rtc() for more
* information
*/
enum BkpRtcClockSrc {
BKP_RTC_CLOCK_SRC_LSE = 0x0,
BKP_RTC_CLOCK_SRC_LSI = 0x1,
BKP_RTC_CLOCK_SRC_HSE = 0x2,
};
/**
* Available IRQ sources. This enum is passed to the RTC callback to allow
* differentiating IRQ sources
*/
enum BkpRtcIrq {
BKP_RTC_IRQ_NONE = 0,
BKP_RTC_IRQ_SECOND = 0x1 << 0,
BKP_RTC_IRQ_ALARM = 0x1 << 1,
BKP_RTC_IRQ_OVERFLOW = 0x1 << 2,
};
/**
* Prototype of the IRQ callbacks that the applicative code can provide
*/
typedef void (*BkpRtcCallback)(enum BkpRtcIrq src);
//--functions-------------------------------------------------------------------
/**
* Configures the RTC, starting it immediately. Configuration is saved between
* boots as long as VBAT is present
*
* The RTC can run on a period of up to 1s and using one of 3 clocks. Clock
* choice is a question of compromise :
* - LSE consumes the less energy and continues to run in standby mode, but
* requires an extra oscillator circuit
* - LSI consumes little but isn't very accurate and requires extra calibration
* (see bkp_callibrate_lsi)
* - HSE consumes the most.
* WARNING : once configured, the clock source can only changed by reseting the
* whole backup domain via bkp_reset()
* Clocks must be enabled prior to calling this function
*
* Mulitple IRQs can be enabled and redirected to single callback. The alarm
* IRQ, triggered at the specified tick value can be rerouted to an exti line
* for wakeup from stanby, in wich case it doesn't need to be enabled here.
*/
void bkp_configure_rtc(uint32_t period_ms, enum BkpRtcClockSrc clock_src,
enum BkpRtcIrq irq_mask, uint32_t alarm_tick, BkpRtcCallback callback);
/**
* Returns the current counter value of the RTC
*/
uint32_t bkp_read_rtc(void);
/**
* Resets the entire backup domain, composed of everything configured through
* this module
*/
void bkp_reset(void);
//unimplemented functions
void bkp_configure_tamper();
void bkp_calibrate_lsi(void);
void bkp_configure_lse(bool enable);
#endif //_BKP_H_

View File

@ -1,13 +1,13 @@
/** @file rtc_regs.h
* Module defining the Real-Time Clock (RTC) registers.
/** @file bkp_regs.h
* Module defining the Backup (bkp) domain registers.
*
* Mainly made to be used by the rtc module. It is recommanded to go through
* Mainly made to be used by the bkp module. It is recommanded to go through
* the functions provided by that module instead of directly using the registers
* defined here.
*/
#ifndef _RTC_REGS_H_
#define _RTC_REGS_H_
#ifndef _BKP_REGS_H_
#define _BKP_REGS_H_
//--includes--------------------------------------------------------------------
@ -16,8 +16,79 @@
//--type definitions------------------------------------------------------------
#define BKP_BASE_ADDRESS 0x40006C00
#define RCC_BASE_ADDRESS 0x40021020
#define RTC_BASE_ADDRESS 0x40002800
union BKP_DR {
struct {
uint32_t D:16;
uint32_t reserved1:16;
};
uint32_t word;
};
union BKP_RTCCR {
struct {
uint32_t CAL:7;
uint32_t CCO:1;
uint32_t ASOE:1;
uint32_t ASOS:1;
uint32_t reserved1:22;
};
uint32_t word;
};
union BKP_CR {
struct {
uint32_t TPE:1;
uint32_t TPAL:1;
uint32_t reserved1:30;
};
uint32_t word;
};
union BKP_CSR {
struct {
uint32_t CTE:1;
uint32_t CTI:1;
uint32_t TPIE:1;
uint32_t reserved1:5;
uint32_t TEF:1;
uint32_t TIF:1;
uint32_t reserved2:22;
};
uint32_t word;
};
struct BKP {
union BKP_DR DR[20];
union BKP_RTCCR RTCCR;
union BKP_CR CR;
union BKP_CSR CSR;
};
union RCC_BDCR {
struct {
uint32_t LSEON:1;
uint32_t LSERDY:1;
uint32_t LSEBYP:1;
uint32_t reserved1:5;
uint32_t RTCSEL:2;
uint32_t reserved2:5;
uint32_t RTCEN:1;
uint32_t BDRST:1;
uint32_t reserved3:15;
};
uint32_t word;
};
struct RCC {
union RCC_BDCR BDCR;
};
union RTC_CRH {
struct {
uint32_t SECIE:1;
@ -121,5 +192,5 @@ struct RTC {
//--functions-------------------------------------------------------------------
#endif //_RTC_REGS_H_
#endif //_BKP_REGS_H_

View File

@ -70,12 +70,6 @@ void rcc_configure_lsi(bool enable)
}
}
void rcc_configure_rtc(bool enable, enum RccRtcClockSrc clock_src)
{
regs->BDCR.RTCSEL = clock_src;
regs->BDCR.RTCEN = enable;
}
void rcc_enable(enum RccAhb ahb_mask, enum RccApb1 apb1_mask,
enum RccApb2 apb2_mask)
{

View File

@ -125,8 +125,6 @@ void rcc_configure(enum RccPreset preset);
*/
void rcc_configure_lsi(bool enable);
void rcc_configure_rtc(bool enable, enum RccRtcClockSrc clock_src);
/**
* Enables peripherals on the different buses. The enums values can used as
* masks to enable multiple peripherals at the same time. Invalid values will be

View File

@ -225,21 +225,6 @@ union RCC_APB1ENR {
uint32_t word;
};
union RCC_BDCR {
struct {
uint32_t LSEON:1;
uint32_t LSERDY:1;
uint32_t LSEBYP:1;
uint32_t reserved1:5;
uint32_t RTCSEL:2;
uint32_t reserved2:5;
uint32_t RTCEN:1;
uint32_t BDRST:1;
uint32_t reserved3:15;
};
uint32_t word;
};
union RCC_CSR {
struct {
uint32_t LSION:1;
@ -266,7 +251,7 @@ struct RCC {
union RCC_AHBENR AHBENR;
union RCC_APB2ENR APB2ENR;
union RCC_APB1ENR APB1ENR;
union RCC_BDCR BDCR;
uint32_t reserved1;
union RCC_CSR CSR;
};

139
drv/rtc.c
View File

@ -1,139 +0,0 @@
//--includes--------------------------------------------------------------------
#include "rtc.h"
#include "rtc_regs.h"
#include "rcc.h"
#include "pwr.h"
#include "nvic.h"
//--local definitions-----------------------------------------------------------
//--local variables-------------------------------------------------------------
static volatile struct RTC* regs = (struct RTC*)RTC_BASE_ADDRESS;
static RtcCallback rtc_callback;
//--public functions------------------------------------------------------------
void rtc_configure(uint32_t period_ms, enum RtcClockSrc clock_src,
enum RtcIrq irq_mask, uint32_t alarm_tick, RtcCallback callback)
{
pwr_configure_bkp_write(true);
//start RTC
rcc_configure_rtc(true, clock_src + 1);
//rtc_reset();
rcc_enable(RCC_AHB_NONE, RCC_APB1_BKP, RCC_APB2_NONE);
//rcc_configure_rtc(false, RCC_RTC_CLOCK_SRC_NONE);
//compute prescaler
uint32_t prescaler = 0;
switch (clock_src) {
case RTC_CLOCK_SRC_LSE:
prescaler = 32768000; //32.768kHz
break;
case RTC_CLOCK_SRC_LSI:
prescaler = 40000000; //40khz
break;
case RTC_CLOCK_SRC_HSE:
prescaler = 62500000; //8Mhz / 128
break;
default:
return;
}
prescaler /= period_ms;
//wait for registers to synchronize
regs->CRL.RSF = 0;
while (regs->CRL.RSF != 1) {}
//wait for last operation to finish
while (regs->CRL.RTOFF != 1) {}
//enable core configuration
regs->CRL.CNF = 1;
//configure core registers
regs->PRLH.PRL = prescaler >> 16;
regs->PRLL.PRL = prescaler;
regs->ALRH.RTC_ALR = alarm_tick >> 16;
regs->ALRL.RTC_ALR = alarm_tick;
//apply irq config
regs->CRH.word |= irq_mask & 0x7;
//disable/apply core configuration
regs->CRL.CNF = 0;
//wait for last operation to finish
while (regs->CRL.RTOFF != 1) {}
pwr_configure_bkp_write(false);
if (callback) {
rtc_callback = callback;
nvic_enable(NVIC_IRQ_RTC);
}
}
void rtc_reset(void)
{
nvic_disable(NVIC_IRQ_RTC);
pwr_configure_bkp_write(true);
rcc_enable(RCC_AHB_NONE, RCC_APB1_BKP, RCC_APB2_NONE);
//wait for registers to synchronize
regs->CRL.RSF = 0;
//while (regs->CRL.RSF != 1) {}
//wait for last operation to finish
while (regs->CRL.RTOFF != 1) {}
//clear config registers
regs->CRH.word &= ~0x7;
regs->CRL.word &= ~0xf;
//enable core configuration
regs->CRL.CNF = 1;
//reset core registers. DIV register can be ignore since it is reset on any
//changes of the other 2
regs->PRLH.PRL = 0x0;
regs->PRLL.PRL = 0x8000;
regs->CNTH.RTC_CNT = 0x0;
regs->CNTL.RTC_CNT = 0x0;
//disable/apply core configuration
regs->CRL.CNF = 0;
//wait for last operation to finish
while (regs->CRL.RTOFF != 1) {}
rcc_configure_rtc(false, RCC_RTC_CLOCK_SRC_NONE);
rcc_disable(RCC_AHB_NONE, RCC_APB1_BKP, RCC_APB2_NONE);
pwr_configure_bkp_write(false);
}
uint32_t stk_read_s(void)
{
//wait for core registers to be synchronized, immediate most of the time
while (regs->CRL.RSF != 1) {}
uint32_t time = regs->CNTH.RTC_CNT << 16;
time |= regs->CNTL.RTC_CNT << 0;
return time;
}
//--local functions-------------------------------------------------------------
void hdr_rtc(void)
{
nvic_clear_pending(NVIC_IRQ_RTC);
//copy and clear and pass along src flags
enum RtcIrq src = regs->CRL.word & 0x7;
regs->CRL.word &= ~(0x7);
rtc_callback(src);
}

View File

@ -1,43 +0,0 @@
/** @file rtc.h
* Module handling the Real-Time Clock (RTC).
*
*/
#ifndef _RTC_H_
#define _RTC_H_
//--includes--------------------------------------------------------------------
#include "stdint.h"
//--type definitions------------------------------------------------------------
enum RtcClockSrc {
RTC_CLOCK_SRC_LSE = 0x0,
RTC_CLOCK_SRC_LSI = 0x1,
RTC_CLOCK_SRC_HSE = 0x2,
};
enum RtcIrq {
RTC_IRQ_NONE = 0,
RTC_IRQ_SECOND = 0x1 << 0,
RTC_IRQ_ALARM = 0x1 << 1,
RTC_IRQ_OVERFLOW = 0x1 << 2,
};
typedef void (*RtcCallback)(enum RtcIrq src);
//--functions-------------------------------------------------------------------
void rtc_configure(uint32_t period_ms, enum RtcClockSrc clock_src,
enum RtcIrq irq_mask, uint32_t alarm_tick, RtcCallback callback);
void rtc_reset(void);
uint32_t stk_read_s(void);
#endif //_RTC_H_