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