diff --git a/firmware/config/boards/STM32F407VET6_Mini.bat b/firmware/config/boards/STM32F407VET6_Mini.bat index 53f20a7993..c5291721ea 100644 --- a/firmware/config/boards/STM32F407VET6_Mini.bat +++ b/firmware/config/boards/STM32F407VET6_Mini.bat @@ -5,7 +5,7 @@ rem TODO: somehow this -DDUMMY is helping us to not mess up the parameters, why? rem https://github.com/rusefi/rusefi/issues/684 rem this board has only 512K flash so using custom FLASH_ADDR rem You probably want "flash0 : org = 0x08000000, len = 450K" in the .ld file -set EXTRA_PARAMS=-DDUMMY -DEFI_COMMUNICATION_PIN=GPIOB_9 -DSTM32_HSECLK=25000000U -DSTM32_PLLM_VALUE=25 -DSTM32_RTCPRE_VALUE=25 -DDEFAULT_ENGINE_TYPE=MINIMAL_PINS^ +set EXTRA_PARAMS=-DDUMMY -DEFI_COMMUNICATION_PIN=GPIOB_9 -DSTM32_RTCPRE_VALUE=25 -DDEFAULT_ENGINE_TYPE=MINIMAL_PINS^ -DEFI_INTERNAL_FLASH=FALSE ^ -DHAL_USE_RTC=FALSE ^ -DBOARD_OTG_NOVBUSSENS ^ diff --git a/firmware/config/boards/hellen/hellen121nissan/board.h b/firmware/config/boards/hellen/hellen121nissan/board.h index 7d4373f9fd..1468addb50 100644 --- a/firmware/config/boards/hellen/hellen121nissan/board.h +++ b/firmware/config/boards/hellen/hellen121nissan/board.h @@ -57,10 +57,6 @@ #define STM32_LSECLK 32768U #endif -#if !defined(STM32_HSECLK) -#define STM32_HSECLK 8000000U -#endif - /* * Board voltages. * Required for performance limits calculation. diff --git a/firmware/config/boards/hellen/hellen121vag/board.h b/firmware/config/boards/hellen/hellen121vag/board.h index 58efaf6195..3617fe26f3 100644 --- a/firmware/config/boards/hellen/hellen121vag/board.h +++ b/firmware/config/boards/hellen/hellen121vag/board.h @@ -57,10 +57,6 @@ #define STM32_LSECLK 32768U #endif -#if !defined(STM32_HSECLK) -#define STM32_HSECLK 8000000U -#endif - /* * Board voltages. * Required for performance limits calculation. diff --git a/firmware/config/boards/hellen/hellen128/board.h b/firmware/config/boards/hellen/hellen128/board.h index 614c4fadb7..4cdcb5bbfd 100644 --- a/firmware/config/boards/hellen/hellen128/board.h +++ b/firmware/config/boards/hellen/hellen128/board.h @@ -57,10 +57,6 @@ #define STM32_LSECLK 32768U #endif -#if !defined(STM32_HSECLK) -#define STM32_HSECLK 8000000U -#endif - /* * Board voltages. * Required for performance limits calculation. diff --git a/firmware/config/boards/hellen/hellen64_miataNA6_94/board.h b/firmware/config/boards/hellen/hellen64_miataNA6_94/board.h index b167c0f024..aede1263d2 100644 --- a/firmware/config/boards/hellen/hellen64_miataNA6_94/board.h +++ b/firmware/config/boards/hellen/hellen64_miataNA6_94/board.h @@ -57,10 +57,6 @@ #define STM32_LSECLK 32768U #endif -#if !defined(STM32_HSECLK) -#define STM32_HSECLK 8000000U -#endif - /* * Board voltages. * Required for performance limits calculation. diff --git a/firmware/config/boards/hellen/hellen72/board.h b/firmware/config/boards/hellen/hellen72/board.h index f3f92e5fae..3acd68802b 100644 --- a/firmware/config/boards/hellen/hellen72/board.h +++ b/firmware/config/boards/hellen/hellen72/board.h @@ -57,10 +57,6 @@ #define STM32_LSECLK 32768U #endif -#if !defined(STM32_HSECLK) -#define STM32_HSECLK 8000000U -#endif - /* * Board voltages. * Required for performance limits calculation. diff --git a/firmware/config/boards/prometheus/board.h b/firmware/config/boards/prometheus/board.h index 26bec0b75d..0fd668ee8a 100644 --- a/firmware/config/boards/prometheus/board.h +++ b/firmware/config/boards/prometheus/board.h @@ -41,10 +41,6 @@ #define STM32_LSECLK 32768U #endif -#if !defined(STM32_HSECLK) -#define STM32_HSECLK 8000000U -#endif - /* * Board voltages. * Required for performance limits calculation. diff --git a/firmware/config/boards/subaru_eg33/board.h b/firmware/config/boards/subaru_eg33/board.h index 005fa73fee..1aa6acb1f2 100644 --- a/firmware/config/boards/subaru_eg33/board.h +++ b/firmware/config/boards/subaru_eg33/board.h @@ -28,10 +28,6 @@ #define STM32_LSEDRV (3U << 3U) -#if !defined(STM32_HSECLK) -#define STM32_HSECLK 25000000U -#endif - /* * Board voltages. * Required for performance limits calculation. diff --git a/firmware/config/boards/subaru_eg33/mcuconf.h b/firmware/config/boards/subaru_eg33/mcuconf.h index 7c0d382802..2f1467f26a 100644 --- a/firmware/config/boards/subaru_eg33/mcuconf.h +++ b/firmware/config/boards/subaru_eg33/mcuconf.h @@ -11,10 +11,6 @@ #include "../../../hw_layer/ports/stm32/stm32f7/cfg/mcuconf.h" -/* clocks adjust for 25 MHz ocs */ -#undef STM32_PLLM_VALUE -#define STM32_PLLM_VALUE 25 - //#undef STM32_LSE_ENABLED //#define STM32_LSE_ENABLED FALSE diff --git a/firmware/hw_layer/ports/stm32/mcuconf_common_f4_f7.h b/firmware/hw_layer/ports/stm32/mcuconf_common_f4_f7.h index 3031ad6b5d..08ac5adde2 100644 --- a/firmware/hw_layer/ports/stm32/mcuconf_common_f4_f7.h +++ b/firmware/hw_layer/ports/stm32/mcuconf_common_f4_f7.h @@ -323,3 +323,15 @@ * WDG driver system settings. */ #define STM32_WDG_USE_IWDG FALSE + +// We auto detect the value of HSE, so set the default PLLM value to the maximum, +// so we don't accidentially overclock to processor before we know how fast HSE is +#define STM32_PLLM_VALUE 25 + +// This also means we have to pretend (for now) we have a 25MHz HSE fitted +#define STM32_HSECLK 25000000 + +// After boot, we will detect the real frequency, and adjust the PLL M value to suit + +#define ENABLE_AUTO_DETECT_HSE + diff --git a/firmware/hw_layer/ports/stm32/osc_detector.cpp b/firmware/hw_layer/ports/stm32/osc_detector.cpp new file mode 100644 index 0000000000..8db7c872c6 --- /dev/null +++ b/firmware/hw_layer/ports/stm32/osc_detector.cpp @@ -0,0 +1,130 @@ +/** + * @file osc_detector.cpp + * @brief This logic automatically detects the speed of the + * oscillator or crystal connected to HSE. + * @date 12 July 2021 + * + * It works by first using the reasonably-precise HSI oscillator (16MHz) to measure LSI (nominally 32khz, but wide tolerance). + * Then, it switches the system clock source to HSE, and repeats the same measurement. The inaccurate LSI will not drift + * significantly in the short period of time between these two measurements, so use it as a transfer standard to compare the speed + * of HSI and HSE. The ratio between the measured speed of LSI when running on HSE vs. HSI will give the ratio of speeds of HSE + * and HSI themselves. Since we know the value of HSI (16mhz), we can compute the speed of HSE. + * + * Lastly, the PLL is reconfigured to use the correct input divider such that the input frequency is 1MHz + * (PLLM is set to N for an N-MHz HSE crystal). + */ + +#include "hal.h" +#include "efilib.h" + +#ifdef ENABLE_AUTO_DETECT_HSE + +static void useHsi() { + // clear SW to use HSI + RCC->CFGR &= ~RCC_CFGR_SW; +} + +static void useHse() { + // Switch to HSE clock + RCC->CFGR &= ~RCC_CFGR_SW; + RCC->CFGR |= RCC_CFGR_SW_HSE; +} + +static void usePll() { + RCC->CFGR &= ~RCC_CFGR_SW; + RCC->CFGR |= RCC_CFGR_SW_PLL; + while ((RCC->CFGR & RCC_CFGR_SWS) != (STM32_SW << 2)); +} + +static uint32_t getOneCapture() { + // wait for input capture + while ((TIM5->SR & TIM_SR_CC4IF) == 0); + + // Return captured count + return TIM5->CCR4; +} + +static uint32_t getAverageLsiCounts() { + // Burn one count + getOneCapture(); + + uint32_t lastCapture = getOneCapture(); + uint32_t sum = 0; + + for (size_t i = 0; i < 20; i++) + { + auto capture = getOneCapture(); + sum += (capture - lastCapture); + lastCapture = capture; + } + + return sum; +} + +// This only works if you're using the PLL as the configured clock source! +static_assert(STM32_SW == RCC_CFGR_SW_PLL); + +// These clocks must all be enabled for this to work +static_assert(STM32_HSI_ENABLED); +static_assert(STM32_LSI_ENABLED); +static_assert(STM32_HSE_ENABLED); + +static void reprogramPll(uint8_t pllM) { + // Switch back to HSI to configure PLL + useHsi(); + + // Stop the PLL + RCC->CR &= ~RCC_CR_PLLON; + + // Mask out the old PLLM val + RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLM_Msk; + + // Stick in the new PLLM value + RCC->PLLCFGR |= (pllM << RCC_PLLCFGR_PLLM_Pos) & RCC_PLLCFGR_PLLM_Msk; + + // Reenable PLL, wait for lock + RCC->CR |= RCC_CR_PLLON; + while (!(RCC->CR & RCC_CR_PLLRDY)); + + // Switch clock source back to PLL + usePll(); +} + +// __late_init runs after bss/zero initialziation, but before static constructors and main +extern "C" void __late_init() { + // Turn on timer 5 + RCC->APB1ENR |= RCC_APB1ENR_TIM5EN; + + // Remap to connect LSI to input capture channel 4 + TIM5->OR = TIM_OR_TI4_RMP_0; + + // Enable capture on channel 4 + TIM5->CCMR2 = TIM_CCMR2_CC4S_0; + TIM5->CCER = TIM_CCER_CC4E; + + // Start TIM5 + TIM5->CR1 |= TIM_CR1_CEN; + + // Use HSI + useHsi(); + + // Measure LSI against HSI + auto hsiCounts = getAverageLsiCounts(); + + useHse(); + + // Measure LSI against HSE + auto hseCounts = getAverageLsiCounts(); + + // Turn off timer 5 now that we're done with it + RCC->APB1ENR &= ~RCC_APB1ENR_TIM5EN; + + // The external clocks's frequency is the ratio of the measured LSI speed, times HSI's speed (16MHz) + float hseFrequencyMhz = 16.0f * hseCounts / hsiCounts; + + uint8_t pllMValue = efiRound(hseFrequencyMhz, 1); + + reprogramPll(pllMValue); +} + +#endif // defined ENABLE_AUTO_DETECT_HSE diff --git a/firmware/hw_layer/ports/stm32/stm32_common.mk b/firmware/hw_layer/ports/stm32/stm32_common.mk index e6b5e47b46..4c24f4a53e 100644 --- a/firmware/hw_layer/ports/stm32/stm32_common.mk +++ b/firmware/hw_layer/ports/stm32/stm32_common.mk @@ -7,6 +7,7 @@ HW_LAYER_EMS_CPP += \ $(PROJECT_DIR)/hw_layer/ports/stm32/stm32_common.cpp \ $(PROJECT_DIR)/hw_layer/ports/stm32/backup_ram.cpp \ $(PROJECT_DIR)/hw_layer/ports/stm32/microsecond_timer_stm32.cpp \ + $(PROJECT_DIR)/hw_layer/ports/stm32/osc_detector.cpp \ RUSEFIASM = $(PROJECT_DIR)/hw_layer/ports/stm32/rusEfiStartup.S diff --git a/firmware/hw_layer/ports/stm32/stm32f4/board.h b/firmware/hw_layer/ports/stm32/stm32f4/board.h index b60873fc33..773d156409 100644 --- a/firmware/hw_layer/ports/stm32/stm32f4/board.h +++ b/firmware/hw_layer/ports/stm32/stm32f4/board.h @@ -56,10 +56,6 @@ #define STM32_LSECLK 32768U #endif -#if !defined(STM32_HSECLK) -#define STM32_HSECLK 8000000U -#endif - /* * Board voltages. * Required for performance limits calculation. diff --git a/firmware/hw_layer/ports/stm32/stm32f4/cfg/mcuconf.h b/firmware/hw_layer/ports/stm32/stm32f4/cfg/mcuconf.h index dce2fc3644..4b2729953c 100644 --- a/firmware/hw_layer/ports/stm32/stm32f4/cfg/mcuconf.h +++ b/firmware/hw_layer/ports/stm32/stm32f4/cfg/mcuconf.h @@ -53,9 +53,6 @@ #define STM32_SW STM32_SW_PLL #define STM32_PLLSRC STM32_PLLSRC_HSE -#ifndef STM32_PLLM_VALUE -#define STM32_PLLM_VALUE 8 -#endif #define STM32_PLLN_VALUE 336 #define STM32_PLLP_VALUE 2 #define STM32_PLLQ_VALUE 7 diff --git a/firmware/hw_layer/ports/stm32/stm32f7/board.h b/firmware/hw_layer/ports/stm32/stm32f7/board.h index 4160fed6b0..4cde638e5f 100644 --- a/firmware/hw_layer/ports/stm32/stm32f7/board.h +++ b/firmware/hw_layer/ports/stm32/stm32f7/board.h @@ -58,10 +58,6 @@ #define STM32_LSEDRV (3U << 3U) -#if !defined(STM32_HSECLK) -#define STM32_HSECLK 8000000U -#endif - // Nucleo boards use MCO signal from St-Link and NOT oscillator - these need STM32_HSE_BYPASS // if you do not have Sl-Link and MCO on your board, you need EFI_USE_OSC diff --git a/firmware/hw_layer/ports/stm32/stm32f7/cfg/mcuconf.h b/firmware/hw_layer/ports/stm32/stm32f7/cfg/mcuconf.h index 7299c1d509..1c2b440fb8 100644 --- a/firmware/hw_layer/ports/stm32/stm32f7/cfg/mcuconf.h +++ b/firmware/hw_layer/ports/stm32/stm32f7/cfg/mcuconf.h @@ -73,7 +73,6 @@ #define STM32_CLOCK48_REQUIRED TRUE #define STM32_SW STM32_SW_PLL #define STM32_PLLSRC STM32_PLLSRC_HSE -#define STM32_PLLM_VALUE 8 #define STM32_PLLN_VALUE 432 #define STM32_PLLP_VALUE 2 #define STM32_PLLQ_VALUE 9