stm32f1xx_HBL/drv/rcc.c
Steins7 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

194 lines
4.6 KiB
C

/** @file rcc.c
* Module handling Reset and Clocks Control (RCC).
*
* The module provides functions to configure clocks according to presets as
* well as to enable/disable/reset peripherals.
*/
//--includes--------------------------------------------------------------------
#include "rcc.h"
#include "rcc_regs.h"
#include "flash.h"
//--local definitions-----------------------------------------------------------
#define AHB_MASK 0x00000557
#define APB1_MASK 0x3afec9ff
#define APB2_MASK 0x0038fffd
static void apply_default_preset(void);
static void apply_speed_preset(void);
//--local variables-------------------------------------------------------------
static volatile struct RCC* regs = (struct RCC*)RCC_BASE_ADDRESS;
static enum RccPreset current_preset = RCC_PRESET_DEFAULT;
//--public functions------------------------------------------------------------
/**
* Configures the clock sources, PLL, prescalers, etc. All peripherals are
* disabled prior to any modifications to avoid unwanted side effects.
*/
void rcc_configure(enum RccPreset preset)
{
//disable all peripherals during clock configuration
union RCC_APB1ENR apb1_enr = regs->APB1ENR;
regs->APB1ENR.word = 0x0;
union RCC_APB2ENR apb2_enr = regs->APB2ENR;
regs->APB2ENR.word = 0x0;
switch (preset) {
case RCC_PRESET_DEFAULT:
apply_default_preset();
current_preset = RCC_PRESET_DEFAULT;
break;
case RCC_PRESET_SPEED:
apply_speed_preset();
current_preset = RCC_PRESET_SPEED;
break;
default:
break;
}
//re-enable peripherals
regs->APB1ENR = apb1_enr;
regs->APB2ENR = apb2_enr;
}
void rcc_configure_lsi(bool enable)
{
regs->CSR.LSION = enable;
//ensure LSI is enabled
if (enable) {
while (regs->CSR.LSIRDY != 0x1) {}
}
}
void rcc_enable(enum RccAhb ahb_mask, enum RccApb1 apb1_mask,
enum RccApb2 apb2_mask)
{
regs->AHBENR.word |= ahb_mask & AHB_MASK;
regs->APB1ENR.word |= apb1_mask & APB1_MASK;
regs->APB2ENR.word |= apb2_mask & APB2_MASK;
}
void rcc_disable(enum RccAhb ahb_mask, enum RccApb1 apb1_mask,
enum RccApb2 apb2_mask)
{
regs->AHBENR.word &= !(ahb_mask & AHB_MASK);
regs->APB1ENR.word &= !(apb1_mask & APB1_MASK);
regs->APB2ENR.word &= !(apb2_mask & APB2_MASK);
}
void rcc_reset(enum RccApb1 apb1_mask, enum RccApb2 apb2_mask)
{
regs->APB1RSTR.word &= !(apb1_mask & APB1_MASK);
regs->APB2RSTR.word &= !(apb2_mask & APB2_MASK);
}
void rcc_get_clocks(struct RccClocks* clocks)
{
switch (current_preset)
{
case RCC_PRESET_DEFAULT:
clocks->ahb_freq = 8000000;
clocks->apb1_freq = 8000000;
clocks->apb2_freq = 8000000;
break;
case RCC_PRESET_SPEED:
clocks->ahb_freq = 72000000;
clocks->apb1_freq = 36000000;
clocks->apb2_freq = 72000000;
break;
default:
//TODO hardfault
break;
}
}
//--local functions-------------------------------------------------------------
/**
* Apply the default clock preset, using HSI whithout PLL or prescalers. This is
* similar to the reset configuration. Most buses run at 8Mhz, except ADCs wich
* run at 4Mhz, and Systick wich only reaches 1Mhz.
*/
static void apply_default_preset(void)
{
//ensures HSI is enabled
regs->CR.HSION = 1;
while (regs->CR.HSIRDY != 0x1);
//set HSI as main clock source and disable prescalers
regs->CFGR.word &= ~0x077fff3;
//disable all options
regs->CR.HSITRIM = 0;
regs->CR.HSITRIM = 0x10;
regs->CR.HSEON = 0;
regs->CR.HSEBYP = 0;
regs->CR.CCSON = 0;
regs->CR.PLLON = 0;
//disable all interrupts
regs->CIR.LSIRDYIE = 0;
regs->CIR.LSERDYIE = 0;
regs->CIR.HSIRDYIE = 0;
regs->CIR.HSERDYIE = 0;
regs->CIR.PLLRDYIE = 0;
//reconfigure flash
flash_configure(FLASH_PRESET_LOW_CLOCK_SPEED);
}
/**
* Apply the speed preset, using HSE with PLL to achieve 72MHz on AHB, APB2 and
* timer buses. APB1 is limited to its maximum clock of 36Mhz and the ADCs run
* at 12MHz. Systick runs at 9Mhz.
*/
static void apply_speed_preset(void)
{
//restore sane values
apply_default_preset();
//try enabling HSE, fallback to HSI if HSE fails
regs->CR.HSEON = 1;
for (uint32_t i=0; i<1000; ++i) {
__asm__("nop");
}
if (regs->CR.HSERDY == 0x1) {
regs->CFGR.PLLSCR = 1;
} else {
regs->CR.HSEON = 0;
}
//configure PLL, fallback to HSI if PLL fails
regs->CFGR.PLLMUL = 0x7; //PLL x9
regs->CR.PLLON = 1;
for (uint32_t i=0; i<1000; ++i) {
__asm__("nop");
}
if (regs->CR.PLLRDY != 0x1) {
regs->CR.PLLON = 0;
return; //clock low enough, no need for prescalers
}
//configure prescalers
regs->CFGR.PPRE1 = 0x4; // /2
regs->CFGR.ADCPRE = 0x2; // /6
//reconfigure flash
flash_configure(FLASH_PRESET_HIGH_CLOCK_SPEED);
//switch to PLL output
regs->CFGR.SW = 0x2;
}