stm32f1xx_HBL/drivers/rcc.c
Steins7 ca935a7650 Rework comments
Avoid repitions between comments and code. Additionnal usage information
can be added as header comment and additionnal technical information can
be added in the source file
2023-03-31 14:17:39 +02:00

167 lines
3.8 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 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;
//--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();
break;
case RCC_PRESET_SPEED:
apply_speed_preset();
break;
default:
break;
}
//re-enable peripherals
regs->APB1ENR = apb1_enr;
regs->APB2ENR = apb2_enr;
}
void rcc_enable_apb1(enum RccApb1 mask)
{
regs->APB1ENR.word |= mask & APB1_MASK;
}
void rcc_disable_apb1(enum RccApb1 mask)
{
regs->APB1ENR.word &= !(mask & APB1_MASK);
}
void rcc_reset_apb1(enum RccApb1 mask)
{
regs->APB1RSTR.word &= !(mask & APB1_MASK);
}
void rcc_enable_apb2(enum RccApb2 mask)
{
regs->APB2ENR.word |= mask & APB2_MASK;
}
void rcc_disable_apb2(enum RccApb2 mask)
{
regs->APB2ENR.word &= !(mask & APB2_MASK);
}
void rcc_reset_apb2(enum RccApb2 mask)
{
regs->APB2RSTR.word &= !(mask & APB2_MASK);
}
//--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 = 0x1;
while (regs->CR.HSIRDY != 0x1);
//set HSI as main clock source and disable prescalers
regs->CFGR.word &= !0x077fff3;
//disable all options
regs->CR.HSITRIM = 0x10;
regs->CR.HSEON = 0x0;
regs->CR.HSEBYP = 0x0;
regs->CR.CCSON = 0x0;
regs->CR.PLLON = 0x0;
//disable all interrupts
regs->CIR.LSIRDYIE = 0x0;
regs->CIR.LSERDYIE = 0x0;
regs->CIR.HSIRDYIE = 0x0;
regs->CIR.HSERDYIE = 0x0;
regs->CIR.PLLRDYIE = 0x0;
//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 = 0x1;
for (uint32_t i=0; i<1000; ++i) {
__asm__("nop");
}
if (regs->CR.HSERDY == 0x1) {
regs->CFGR.PLLSCR = 0x1;
} else {
regs->CR.HSEON = 0;
}
//configure PLL, fallback to HSI if PLL fails
regs->CFGR.PLLMUL = 0x7; //PLL x9
regs->CR.PLLON = 0x1;
for (uint32_t i=0; i<1000; ++i) {
__asm__("nop");
}
if (regs->CR.PLLRDY != 0x1) {
regs->CR.PLLON = 0x0;
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;
}