/** @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; }