diff --git a/drivers/rcc.c b/drivers/rcc.c new file mode 100644 index 0000000..c071afa --- /dev/null +++ b/drivers/rcc.c @@ -0,0 +1,126 @@ +/** @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" + + +//--local definitions----------------------------------------------------------- + +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; +} + + +//--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; +} + +/** + * 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 + + //configure flash + volatile uint32_t* ACR = (uint32_t*)0x40022000; + *ACR |= 0x12; + + //switch to PLL output + regs->CFGR.SW = 0x2; +} + diff --git a/drivers/rcc.h b/drivers/rcc.h new file mode 100644 index 0000000..09d71d4 --- /dev/null +++ b/drivers/rcc.h @@ -0,0 +1,38 @@ +/** @file rcc.h + * 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. + */ + +#ifndef _RCC_H_ +#define _RCC_H_ + +//--includes-------------------------------------------------------------------- + +#include "stdint.h" + + +//--type definitions------------------------------------------------------------ + +/** + * Available clock configuration presets + */ +enum RccPreset { + RCC_PRESET_DEFAULT, //sane values, identical to reset config + RCC_PRESET_SPEED, //highest clocks, uses 8MHz HSE if available +}; + + +//--functions------------------------------------------------------------------- + +/** + * Configures the clocks and buses according to the given preset. + * + * @param preset the preset to use for configuration + */ +void rcc_configure(enum RccPreset preset); + + +#endif //_RCC_H_ + diff --git a/drivers/rcc_regs.h b/drivers/rcc_regs.h index 8177b3e..3c804fb 100644 --- a/drivers/rcc_regs.h +++ b/drivers/rcc_regs.h @@ -1,3 +1,10 @@ +/** @file rcc_regs.h + * Module defining Reset and Clocks Control (RCC) registers. + * + * Mainly made to be used by the rcc module. It is recommanded to go through + * the functions provided by that module instead of directly using the registers + * defined here. + */ #ifndef _RCC_REGS_H_ #define _RCC_REGS_H_