From 569f0bae627981ca8d3b3fb58532fd5dfc2827e5 Mon Sep 17 00:00:00 2001 From: jflyper Date: Sat, 10 Nov 2018 20:44:05 +0900 Subject: [PATCH] Implement configurable system clock generation PLL-HSE working PLL-HSI working Move SystemCoreClockUpdate in SystemInit to end Switching from HSI-PLL to HSE-PLL (and back) is working It works during various levels of overclocking. Renamed CLI variable hse_mhz to system_hse_mhz Restored the original position of the spectrum bind code The internal logic of the spectrum bind code will prevent binding process to fire if executed after soft reset. Remove stale call to delay Add a comment about call to spektrumBind placement Declared SystemXXXSource functions, handled sign-ness warning. Cleaned up commented out sections USB clock generation for F446 Default HSE value for backward compatibility Cleaned up more unused stuff Handle non-F4 targets Added comment about PLL_M selection Removed fake gyro/acc from test target --- src/main/fc/config.c | 3 +- src/main/fc/config.h | 1 + src/main/fc/init.c | 16 +- src/main/interface/cli.c | 16 ++ src/main/interface/settings.c | 1 + src/main/target/STM32F7X2/config/NERO.config | 3 + src/main/target/common_post.h | 12 ++ src/main/target/system_stm32f4xx.c | 254 +++++++++++++++------------ src/main/target/system_stm32f4xx.h | 3 + 9 files changed, 188 insertions(+), 121 deletions(-) diff --git a/src/main/fc/config.c b/src/main/fc/config.c index 164f68e0f..b08a9a5fc 100644 --- a/src/main/fc/config.c +++ b/src/main/fc/config.c @@ -85,7 +85,8 @@ PG_RESET_TEMPLATE(systemConfig_t, systemConfig, .task_statistics = true, .cpu_overclock = 0, .powerOnArmingGraceTime = 5, - .boardIdentifier = TARGET_BOARD_IDENTIFIER + .boardIdentifier = TARGET_BOARD_IDENTIFIER, + .hseMhz = SYSTEM_HSE_VALUE, // Not used for non-F4 targets ); uint8_t getCurrentPidProfileIndex(void) diff --git a/src/main/fc/config.h b/src/main/fc/config.h index 5a665b691..7816a8eb0 100644 --- a/src/main/fc/config.h +++ b/src/main/fc/config.h @@ -42,6 +42,7 @@ typedef struct systemConfig_s { uint8_t cpu_overclock; uint8_t powerOnArmingGraceTime; // in seconds char boardIdentifier[sizeof(TARGET_BOARD_IDENTIFIER) + 1]; + uint8_t hseMhz; // Not used for non-F4 targets } systemConfig_t; PG_DECLARE(systemConfig_t, systemConfig); diff --git a/src/main/fc/init.c b/src/main/fc/init.c index d49d2723c..367124716 100644 --- a/src/main/fc/init.c +++ b/src/main/fc/init.c @@ -274,8 +274,7 @@ void init(void) buttonsInit(); - // Check status of bind plug and exit if not active - delayMicroseconds(10); // allow configuration to settle + delayMicroseconds(10); // allow configuration to settle // XXX Could be removed, too? if (!isMPUSoftReset()) { #if defined(BUTTON_A_PIN) && defined(BUTTON_B_PIN) @@ -303,6 +302,12 @@ void init(void) mcoInit(mcoConfig()); #endif + // Note that spektrumBind checks if a call is immediately after + // hard reset (including power cycle), so it should be called before + // systemClockSetHSEValue and OverclockRebootIfNecessary, as these + // may cause soft reset which will prevent spektrumBind not to execute + // the bind procedure. + #if defined(USE_SPEKTRUM_BIND) if (featureIsEnabled(FEATURE_RX_SERIAL)) { switch (rxConfig()->serialrx_provider) { @@ -318,12 +323,15 @@ void init(void) } #endif +#ifdef STM32F4 + // Only F4 has non-8MHz boards + systemClockSetHSEValue(systemConfig()->hseMhz * 1000000U); +#endif + #ifdef USE_OVERCLOCK OverclockRebootIfNecessary(systemConfig()->cpu_overclock); #endif - delay(100); - timerInit(); // timer must be initialized before any channel is allocated #ifdef BUS_SWITCH_PIN diff --git a/src/main/interface/cli.c b/src/main/interface/cli.c index 6b9c094c0..3d86c1dd3 100644 --- a/src/main/interface/cli.c +++ b/src/main/interface/cli.c @@ -3594,6 +3594,22 @@ static void cliStatus(char *cmdline) cliPrintf("CPU Clock=%dMHz", (SystemCoreClock / 1000000)); +#ifdef STM32F4 + // Only F4 is capable of switching between HSE/HSI (for now) + int sysclkSource = SystemSYSCLKSource(); + + const char *SYSCLKSource[] = { "HSI", "HSE", "PLLP", "PLLR" }; + const char *PLLSource[] = { "-HSI", "-HSE" }; + + int pllSource; + + if (sysclkSource >= 2) { + pllSource = SystemPLLSource(); + } + + cliPrintf(" (%s%s)", SYSCLKSource[sysclkSource], (sysclkSource < 2) ? "" : PLLSource[pllSource]); +#endif + #ifdef USE_ADC_INTERNAL uint16_t vrefintMv = getVrefMv(); int16_t coretemp = getCoreTemperatureCelsius(); diff --git a/src/main/interface/settings.c b/src/main/interface/settings.c index 68a93c70c..a11103ee8 100644 --- a/src/main/interface/settings.c +++ b/src/main/interface/settings.c @@ -1112,6 +1112,7 @@ const clivalue_t valueTable[] = { #endif // PG_SYSTEM_CONFIG + { "system_hse_mhz", VAR_UINT8 | MASTER_VALUE, .config.minmax = { 0, 30 }, PG_SYSTEM_CONFIG, offsetof(systemConfig_t, hseMhz) }, #if defined(USE_TASK_STATISTICS) { "task_statistics", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_SYSTEM_CONFIG, offsetof(systemConfig_t, task_statistics) }, #endif diff --git a/src/main/target/STM32F7X2/config/NERO.config b/src/main/target/STM32F7X2/config/NERO.config index 0a5b084d4..97267a9bb 100644 --- a/src/main/target/STM32F7X2/config/NERO.config +++ b/src/main/target/STM32F7X2/config/NERO.config @@ -6,6 +6,9 @@ defaults nosave +# External crystal frequency +set system_hse_mhz = 8 + # Basic I/O resource LED 1 B06 resource LED 2 B05 diff --git a/src/main/target/common_post.h b/src/main/target/common_post.h index 866b2034a..776871dc2 100644 --- a/src/main/target/common_post.h +++ b/src/main/target/common_post.h @@ -204,3 +204,15 @@ #if defined(USE_RX_CX10) #define USE_RX_XN297 #endif + +// Setup crystal frequency for backward compatibility +// Should be set to zero for generic targets and set with CLI variable set system_hse_value. +#ifdef GENERIC_TARGET +#define SYSTEM_HSE_VALUE 0 +#else +#ifdef TARGET_XTAL_MHZ +#define SYSTEM_HSE_VALUE TARGET_XTAL_MHZ +#else +#define SYSTEM_HSE_VALUE (HSE_VALUE/1000000U) +#endif +#endif diff --git a/src/main/target/system_stm32f4xx.c b/src/main/target/system_stm32f4xx.c index 6346470f2..377559d57 100644 --- a/src/main/target/system_stm32f4xx.c +++ b/src/main/target/system_stm32f4xx.c @@ -318,8 +318,6 @@ #include "system_stm32f4xx.h" #include "platform.h" -uint32_t hse_value = HSE_VALUE; - /** * @} */ @@ -354,59 +352,6 @@ uint32_t hse_value = HSE_VALUE; This value must be a multiple of 0x200. */ /******************************************************************************/ -/************************* PLL Parameters *************************************/ -#if defined(TARGET_XTAL_MHZ) - #define PLL_M TARGET_XTAL_MHZ -#else -#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F469_479xx) - #define PLL_M 8 -#elif defined (STM32F446xx) - #define PLL_M 8 -#elif defined (STM32F410xx) || defined (STM32F411xE) - #define PLL_M 8 -#else -#endif /* STM32F40_41xxx || STM32F427_437xx || STM32F429_439xx || STM32F401xx || STM32F469_479xx */ -#endif - -#if defined(STM32F446xx) -/* PLL division factor for I2S, SAI, SYSTEM and SPDIF: Clock = PLL_VCO / PLLR */ -#define PLL_R 7 -#endif /* STM32F446xx */ - -#if defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F446xx) || defined(STM32F469_479xx) -#define PLL_N 360 -/* SYSCLK = PLL_VCO / PLL_P */ -#define PLL_P 2 -/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ -#define PLL_Q 7 -#endif /* STM32F427_437x || STM32F429_439xx || STM32F446xx || STM32F469_479xx */ - -#if defined (STM32F40_41xxx) -#define PLL_N 336 -/* SYSCLK = PLL_VCO / PLL_P */ -#define PLL_P 2 -/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ -#define PLL_Q 7 -#endif /* STM32F40_41xxx */ - -#if defined(STM32F401xx) -#define PLL_N 336 -/* SYSCLK = PLL_VCO / PLL_P */ -#define PLL_P 4 -/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ -#define PLL_Q 7 -#endif /* STM32F401xx */ - -#if defined(STM32F410xx) || defined(STM32F411xE) -#define PLL_N 384 -/* SYSCLK = PLL_VCO / PLL_P */ -#define PLL_P 4 -/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ -#define PLL_Q 8 -#endif /* STM32F410xx || STM32F411xE */ - -/******************************************************************************/ - /** * @} */ @@ -456,33 +401,69 @@ static void SystemInit_ExtMemCtl(void); */ uint32_t SystemCoreClock; -uint32_t pll_p = PLL_P, pll_n = PLL_N, pll_q = PLL_Q; +uint32_t pll_src, pll_input, pll_m, pll_p, pll_n, pll_q; + +// SystemSYSCLKSource +// 0: HSI +// 1; HSE +// 2: PLLP +// 3: PLLR (F446 only) + +int SystemSYSCLKSource(void) +{ + return (RCC->CFGR & RCC_CFGR_SWS) >> 2; +} + +// SystemPLLSource +// 0: HSI +// 1: HSE + +int SystemPLLSource(void) +{ + return (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; +} typedef struct pllConfig_s { + uint16_t mhz; // target SYSCLK uint16_t n; uint16_t p; uint16_t q; } pllConfig_t; -static const pllConfig_t overclockLevels[] = { - { PLL_N, PLL_P, PLL_Q }, // default +// PLL parameters for PLL input = 1MHz. +// For PLL input = 2MHz, divide n by 2; see SystemInitPLLParameters below. +static const pllConfig_t overclockLevels[] = { #if defined(STM32F40_41xxx) - { 384, 2, 8 }, // 192 MHz - { 432, 2, 9 }, // 216 MHz - { 480, 2, 10 } // 240 MHz + { 168, 336, 2, 7 }, // 168 MHz + { 192, 384, 2, 8 }, // 192 MHz + { 216, 432, 2, 9 }, // 216 MHz + { 240, 480, 2, 10 } // 240 MHz #elif defined(STM32F411xE) - { 432, 4, 9 }, // 108 MHz - { 480, 4, 10 }, // 120 MHz + { 84, 336, 4, 7 }, // 84 MHz + { 96, 384, 4, 8 }, // 96 MHz + { 108, 432, 4, 9 }, // 108 MHz + { 120, 480, 4, 10 }, // 120 MHz +#elif defined(STM32F446xx) + // Main PLL for F446 is not constrained by USB clock generation, + // as we generate it with PLLSAI. + // Here, for the moment, we start with default 180MHz and increment in steps of 24MHz. + // May be made variable in steps of 1MHz in the future... + { 180, 360, 2, 2 }, // 180 MHz + { 202, 404, 2, 2 }, // 202 MHz + { 226, 452, 2, 2 }, // 226 MHz + { 250, 500, 2, 2 }, // 250 MHz, operation not verified #endif - - // XXX Doesn't work for F446 with this configuration. - // XXX Need to use smaller M to reduce N? }; +#if defined(STM32F446xx) +#define PLL_R 7 // PLL_R output is not used, can be any descent number +#endif + static PERSISTENT uint32_t currentOverclockLevel = 0; +static PERSISTENT uint32_t hse_value = 8000000; -void SystemInitOC(void) +void SystemInitPLLParameters(void) { /* PLL setting for overclocking */ if (currentOverclockLevel >= ARRAYLEN(overclockLevels)) { @@ -491,7 +472,7 @@ void SystemInitOC(void) const pllConfig_t * const pll = overclockLevels + currentOverclockLevel; - pll_n = pll->n; + pll_n = pll->n / pll_input; pll_p = pll->p; pll_q = pll->q; } @@ -505,19 +486,28 @@ void OverclockRebootIfNecessary(uint32_t overclockLevel) const pllConfig_t * const pll = overclockLevels + overclockLevel; // Reboot to adjust overclock frequency - if (SystemCoreClock != (pll->n / pll->p) * 1000000U) { + if (SystemCoreClock != pll->mhz * 1000000U) { currentOverclockLevel = overclockLevel; __disable_irq(); NVIC_SystemReset(); } } -void SystemInit(void) +void systemClockSetHSEValue(uint32_t frequency) { - SystemInitOC(); + if (hse_value != frequency) { + hse_value = frequency; + __disable_irq(); + NVIC_SystemReset(); + } +} - /* core clock is simply a mhz of PLL_N / PLL_P */ - SystemCoreClock = (pll_n / pll_p) * 1000000; +void SystemInit(void) +{ + if (!(RCC->CSR & RCC_CSR_SFTRSTF)) { + currentOverclockLevel = 0; + hse_value = 0; + } /* FPU settings ------------------------------------------------------------*/ #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) @@ -556,6 +546,8 @@ void SystemInit(void) #else SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ #endif + + SystemCoreClockUpdate(); } /** @@ -609,7 +601,7 @@ void SystemCoreClockUpdate(void) SystemCoreClock = HSI_VALUE; break; case 0x04: /* HSE used as system clock source */ - SystemCoreClock = HSE_VALUE; + SystemCoreClock = hse_value; break; case 0x08: /* PLL P used as system clock source */ /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N @@ -621,7 +613,7 @@ void SystemCoreClockUpdate(void) if (pllsource != 0) { /* HSE used as PLL clock source */ - pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + pllvco = (hse_value / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); } else { @@ -642,7 +634,7 @@ void SystemCoreClockUpdate(void) if (pllsource != 0) { /* HSE used as PLL clock source */ - pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + pllvco = (hse_value / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); } else { @@ -658,11 +650,17 @@ void SystemCoreClockUpdate(void) SystemCoreClock = HSI_VALUE; break; } - /* Compute HCLK frequency --------------------------------------------------*/ - /* Get HCLK prescaler */ - tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; - /* HCLK frequency */ - SystemCoreClock >>= tmp; +} + +static int StartHSx(uint32_t onBit, uint32_t readyBit, int maxWaitCount) +{ + RCC->CR |= onBit; + for (int waitCounter = 0 ; waitCounter < maxWaitCount ; waitCounter++) { + if (RCC->CR & readyBit) { + return 1; + } + } + return 0; } /** @@ -675,32 +673,51 @@ void SystemCoreClockUpdate(void) */ void SetSysClock(void) { -/******************************************************************************/ -/* PLL (clocked by HSE) used as System clock source */ -/******************************************************************************/ - __IO uint32_t StartUpCounter = 0, HSEStatus = 0; - - /* Enable HSE */ - RCC->CR |= ((uint32_t)RCC_CR_HSEON); - - /* Wait till HSE is ready and if Time out is reached exit */ - do - { - HSEStatus = RCC->CR & RCC_CR_HSERDY; - StartUpCounter++; - } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + uint32_t hse_mhz = hse_value / 1000000; + + // Switch to HSI during clock manipulation + + RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_HSI; + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_HSI); + + // We want to use 2MHz input to PLL, as it will provide greater + // flexibility in choice of PLL_N and compatible with generation + // of 48MHz for USB requirement at the same time. + // + // Here, if the frequency (in MHz) is multiples of 2, then pll_m is + // set to a value that derives 2MHz as input to PLL. + // Otherwise, pll_m is set to the frequency (in MHz) to derive + // 1MHz as input to PLL. + + if (hse_value == 0) { + // HSE frequency unknown; use PLL with HSI as source + if (!StartHSx(RCC_CR_HSION, RCC_CR_HSIRDY, 5000)) { + return; + } + + pll_src = RCC_PLLCFGR_PLLSRC_HSI; + + // HSI is fixed at 16MHz. + pll_m = 8; + pll_input = 2; + } else { + // HSE frequency is given. + + if (!StartHSx(RCC_CR_HSEON, RCC_CR_HSERDY, 5000)) { + return; + } + + pll_src = RCC_PLLCFGR_PLLSRC_HSE; + + pll_m = hse_mhz / 2; + if (pll_m * 2 != hse_mhz) { + pll_m = hse_mhz; + } + pll_input = hse_mhz / pll_m; + } - if ((RCC->CR & RCC_CR_HSERDY) != RESET) - { - HSEStatus = (uint32_t)0x01; - } - else - { - HSEStatus = (uint32_t)0x00; - } + SystemInitPLLParameters(); - if (HSEStatus == (uint32_t)0x01) - { /* Select regulator voltage output Scale 1 mode */ RCC->APB1ENR |= RCC_APB1ENR_PWREN; PWR->CR |= PWR_CR_VOS; @@ -734,12 +751,12 @@ void SetSysClock(void) #if defined(STM32F446xx) /* Configure the main PLL */ - RCC->PLLCFGR = PLL_M | (pll_n << 6) | (((pll_p >> 1) -1) << 16) | - (RCC_PLLCFGR_PLLSRC_HSE) | (pll_q << 24) | (PLL_R << 28); + RCC->PLLCFGR = pll_m | (pll_n << 6) | (((pll_p >> 1) -1) << 16) | + (pll_src) | (pll_q << 24) | (PLL_R << 28); #else /* Configure the main PLL */ - RCC->PLLCFGR = PLL_M | (pll_n << 6) | (((pll_p >> 1) -1) << 16) | - (RCC_PLLCFGR_PLLSRC_HSE) | (pll_q << 24); + RCC->PLLCFGR = pll_m | (pll_n << 6) | (((pll_p >> 1) -1) << 16) | + (pll_src) | (pll_q << 24); #endif /* STM32F446xx */ /* Enable the main PLL */ @@ -780,17 +797,14 @@ void SetSysClock(void) while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL); { } - } - else - { /* If HSE fails to start-up, the application will have wrong clock - configuration. User can add here some code to deal with this error */ - } #if defined(STM32F446xx) // Always use PLLSAI to derive USB 48MHz clock. // - This also works under arbitral overclocking situations. // - Only handles HSE case. +uint32_t pllsai_m; + #ifdef TARGET_XTAL_MHZ #define PLLSAI_M TARGET_XTAL_MHZ #else @@ -802,7 +816,13 @@ void SetSysClock(void) #define RCC_PLLSAI_IS_READY() ((RCC->CR & (RCC_CR_PLLSAIRDY)) == (RCC_CR_PLLSAIRDY)) - /* Configure 48MHz clock for USB */ + // Scale PLLSAI input to 1MHz. + if (hse_value) { + pllsai_m = hse_value / 1000000U; + } else { + pllsai_m = 16; + } + // Set 48MHz clock source RCC_48MHzClockSourceConfig(RCC_48MHZCLKSource_PLLSAI); @@ -812,7 +832,7 @@ void SetSysClock(void) // wait for PLLSAI to be disabled while (RCC_PLLSAI_IS_READY()) {} - RCC_PLLSAIConfig(PLLSAI_M, PLLSAI_N, PLLSAI_P, PLLSAI_Q); + RCC_PLLSAIConfig(pllsai_m, PLLSAI_N, PLLSAI_P, PLLSAI_Q); RCC_PLLSAICmd(ENABLE); @@ -823,6 +843,8 @@ void SetSysClock(void) #undef RCC_PLLSAI_GET_FLAG #endif /* STM32F446xx */ + + SystemCoreClockUpdate(); } /** diff --git a/src/main/target/system_stm32f4xx.h b/src/main/target/system_stm32f4xx.h index 3da0f1b07..6ac055a84 100644 --- a/src/main/target/system_stm32f4xx.h +++ b/src/main/target/system_stm32f4xx.h @@ -36,6 +36,9 @@ extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Cloc extern void SystemInit(void); extern void SystemCoreClockUpdate(void); extern void OverclockRebootIfNecessary(uint32_t overclockLevel); +extern void systemClockSetHSEValue(uint32_t frequency); +extern int SystemSYSCLKSource(void); +extern int SystemPLLSource(void); #ifdef __cplusplus } -- 2.11.4.GIT