Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "GPIO.hpp"
- #include <cmath>
- #include <cstddef>
- #include <cstdint>
- #include <tuple>
- #include <type_traits>
- #pragma once
- using reg32_t = volatile uint32_t;
- /**
- * @brief Reset and clock control registers class
- *
- */
- class RCC {
- private:
- union {
- reg32_t reg;
- struct {
- reg32_t : 4;
- reg32_t PLLI2SRDY : 1;
- reg32_t PLLI2SON : 1;
- reg32_t PLLRDY : 1;
- reg32_t PLLON : 1;
- reg32_t : 4;
- reg32_t CSSON : 1;
- reg32_t HSEBYP : 1;
- reg32_t HSERDY : 1;
- reg32_t HSEON : 1;
- reg32_t HSICAL : 8;
- reg32_t HSITRIM : 5;
- reg32_t : 1;
- reg32_t HSIRDY : 1;
- reg32_t HSION : 1;
- } bits;
- } CR;
- union {
- reg32_t reg;
- struct {
- reg32_t : 4;
- reg32_t PLLQ : 4;
- reg32_t : 1;
- reg32_t PLLSRC : 1;
- reg32_t : 4;
- reg32_t PLLP : 2;
- reg32_t : 1;
- reg32_t PLLN : 9;
- reg32_t PLLM : 6;
- } bits;
- } PLLCFGR;
- union {
- reg32_t reg;
- struct {
- reg32_t MCO2 : 2;
- reg32_t MCO2PRE : 3;
- reg32_t MCO1PRE : 3;
- reg32_t I2SSRC : 1;
- reg32_t MCO1 : 2;
- reg32_t RTCPRE : 5;
- reg32_t PPRE2 : 3;
- reg32_t PPRE1 : 3;
- reg32_t : 2;
- reg32_t HPRE : 4;
- reg32_t SWS : 2;
- reg32_t SW : 2;
- } bits;
- } CFGR;
- reg32_t CIR;
- reg32_t AHB1RSTR;
- reg32_t AHB2RSTR;
- reg32_t : 32;
- reg32_t : 32;
- reg32_t APB1RSTR;
- reg32_t APB2RSTR;
- reg32_t : 32;
- reg32_t : 32;
- union {
- reg32_t reg;
- struct {
- reg32_t : 9;
- reg32_t DMA2EN : 1;
- reg32_t DMA1EN : 1;
- reg32_t : 8;
- reg32_t CRCEN : 1;
- reg32_t : 4;
- reg32_t GPIOHEN : 1;
- reg32_t : 2;
- reg32_t GPIOEEN : 1;
- reg32_t GPIODEN : 1;
- reg32_t GPIOCEN : 1;
- reg32_t GPIOBEN : 1;
- reg32_t GPIOAEN : 1;
- } bits;
- } AHB1ENR;
- reg32_t AHB2ENR;
- reg32_t : 32;
- reg32_t : 32;
- union {
- reg32_t reg;
- struct {
- reg32_t : 3;
- reg32_t PWREN : 1;
- reg32_t : 4;
- reg32_t I2C3EN : 1;
- reg32_t I2C2EN : 1;
- reg32_t I2C1EN : 1;
- reg32_t : 3;
- reg32_t USART2EN : 1;
- reg32_t : 1;
- reg32_t SPI3EN : 1;
- reg32_t SPI2EN : 1;
- reg32_t : 2;
- reg32_t WWDGEN : 1;
- reg32_t : 7;
- reg32_t TIM5EN : 1;
- reg32_t TIM4EN : 1;
- reg32_t TIM3EN : 1;
- reg32_t TIM2EN : 1;
- } bits;
- } APB1ENR;
- union {
- reg32_t reg;
- struct {
- reg32_t : 13;
- reg32_t TIM11EN : 1;
- reg32_t TIM10EN : 1;
- reg32_t TIM9EN : 1;
- reg32_t : 1;
- reg32_t SYSCFGEN : 1;
- reg32_t SPI4EN : 1;
- reg32_t SPI1EN : 1;
- reg32_t SDIOEN : 1;
- reg32_t : 2;
- reg32_t ADC1EN : 1;
- reg32_t : 2;
- reg32_t USART6EN : 1;
- reg32_t USART1EN : 1;
- reg32_t : 3;
- reg32_t TIM1EN : 1;
- } bits;
- } APB2ENR;
- reg32_t : 32;
- reg32_t : 32;
- reg32_t AHB1LPENR;
- reg32_t AHB2LPENR;
- reg32_t : 32;
- reg32_t : 32;
- reg32_t APB1LPENR;
- reg32_t APB2LPENR;
- reg32_t : 32;
- reg32_t : 32;
- reg32_t BDCR;
- reg32_t CSR;
- reg32_t : 32;
- reg32_t : 32;
- reg32_t SSCGR;
- reg32_t PLLI2SCFGR;
- reg32_t : 32;
- reg32_t DCKCFGR;
- public:
- /**
- * @brief 3 PLL register scaler values
- */
- struct PLLClocks {
- uint32_t pll_m;
- uint32_t pll_n;
- uint32_t pll_p;
- };
- /**
- * @brief Frequency of external oscillator
- */
- static constexpr int HSE_CRYSTAL_FREQ_HZ = 25000000;
- /**
- * @brief Frequency of internal oscillator
- */
- static constexpr int HSI_CRYSTAL_FREQ_HZ = 16000000;
- /**
- * @brief Target CPU frequency
- */
- static constexpr int TARGET_SYSTEM_CLOCK = 84000000;
- /**
- * @brief Maximum recommended VCO input frequency
- * @note Documented in MCU datasheet PLL electrical characteristics documentation
- */
- static constexpr int VCO_INPUT_FREQUENCY_CEILING = 2000000;
- /**
- * @brief Maximum system clock frequency
- * @note Documented in MCU datasheet PLL electrical characteristics documentation
- */
- static constexpr int SYSTEM_CLOCK_FREQUENCY_CEILING = 84000000;
- /**
- * @brief Maximum internal PLL clock frequency
- * @note Documented in MCU datasheet PLL electrical characteristics documentation
- */
- static constexpr int PLL_INTERNAL_FREQUENCY_CEILING = 432000000;
- /**
- * @brief Manual timeout value for HSE ready
- *
- */
- static constexpr int HSE_TIMEOUT = 1000;
- RCC();
- void *operator new(std::size_t);
- void EnableExternalSystemClock();
- /**
- * @brief Enables clock signal to specific GPIO peripheral
- *
- * @param selection GPIO selection enum value
- */
- inline void EnableGPIO(const GPIO::Selection selection) {
- this->AHB1ENR.reg |= (1 << selection);
- }
- inline void DisableGPIO(const enum GPIO::Selection selection) {
- this->AHB1ENR.reg &= ~(1 << selection);
- }
- /**
- * @brief Calculate system clock frequency through PLL register values
- *
- * @param hs_crystal_freq HS clock signal frequency in Hz
- * @param pll_m M divisor
- * @param pll_n N mulitplier
- * @param pll_p P divisor in register form
- * @return constexpr int System clock frequency
- */
- static constexpr int CalculateSysClock(uint32_t hs_crystal_freq, uint32_t pll_m, uint32_t pll_n,
- uint32_t pll_p) {
- return ((hs_crystal_freq / pll_m) * pll_n) / ((pll_p * 2) + 2);
- }
- /**
- * @brief Returns best match for PLL M, N, and P register values for main MCU PLL
- *
- * @param hs_crystal_freq Input HS clock signal frequency in Hz
- * @param target Target system clock frequency
- * @return constexpr struct PLLClocks M, N, and P values in struct
- */
- static constexpr struct PLLClocks CalculatePLLClocks(uint32_t hs_crystal_freq,
- uint32_t target) {
- uint32_t pll_m = 1, optimum_pll_n = 0, optimum_pll_p = 0;
- int64_t min_diff = INT64_MAX;
- // Calculate PLL M value to satisfy frequency constraints
- while (hs_crystal_freq / pll_m > RCC::VCO_INPUT_FREQUENCY_CEILING) {
- pll_m++;
- }
- // Iterate through all values of N and P to find closest match to target
- for (uint32_t pll_n = 430; pll_n > 50; pll_n--) {
- for (uint32_t pll_p = 2; pll_p <= 8; pll_p += 2) {
- // Calculate system clock with crystal, M, N, and P
- uint32_t clock =
- RCC::CalculateSysClock(hs_crystal_freq, pll_m, pll_n, (pll_p - 2) / 2);
- if ((hs_crystal_freq / pll_m) * pll_n < RCC::PLL_INTERNAL_FREQUENCY_CEILING &&
- clock < RCC::SYSTEM_CLOCK_FREQUENCY_CEILING) {
- // Calcualte absolute difference between target and calculated clock
- int64_t diff = (static_cast<int64_t>(target) - static_cast<int64_t>(clock));
- diff *= ((diff < 0) ? -1 : 1);
- // If it's closer than previously saved result, overwrite it
- if (diff <= min_diff) {
- min_diff = diff;
- optimum_pll_n = pll_n;
- optimum_pll_p = pll_p;
- }
- }
- }
- }
- // Return closest answer in register form
- return {pll_m, optimum_pll_n, (optimum_pll_p - 2) / 2};
- }
- /**
- * @brief Calculate system clock frequency through target clock frequency
- *
- * @param hs_crystal_freq HS clock signal frequency in Hz
- * @return constexpr int System clock frequency
- */
- static constexpr int GetSystemClock(uint32_t hs_crystal_freq) {
- auto pll_clocks = RCC::CalculatePLLClocks(hs_crystal_freq, RCC::TARGET_SYSTEM_CLOCK);
- return RCC::CalculateSysClock(HSE_CRYSTAL_FREQ_HZ, pll_clocks.pll_m, pll_clocks.pll_n,
- pll_clocks.pll_p);
- }
- /**
- * @brief Calculate APB2 clock prescaler
- *
- * @param sys_clock Current system clock
- * @return constexpr int APB2 prescaler value
- */
- static constexpr int CalculateLSAPB2Prescaler(uint32_t sys_clock) {
- int divisor = 1;
- while (sys_clock / divisor > 42000000) {
- divisor *= 2;
- }
- switch (divisor) {
- case 1:
- return 0b000;
- case 2:
- return 0b100;
- case 4:
- return 0b101;
- case 8:
- return 0b110;
- case 16:
- return 0b111;
- }
- }
- };
- static_assert(sizeof(RCC) == 36 * sizeof(reg32_t), "RCC contains padding bytes.");
- static_assert(std::is_standard_layout<RCC>::value, "RCC isn't standard layout.");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement