Зміна частоти STM32 під час роботи
13 April 2018
Під час роботи мікроконтролера можливо програмно змінювати його частоту. Порядок дій при цьому наступний:
- Перемкнутися з PLL на HSE
- Змінити множники/дільники PLL
- Дочекатися стабілізації його частоти
- Перемкнутися з HSE на PLL
- Переініціалізувати периферію
Реалізація такого функціонала подана нижче (функція TM_SystemClockConfig()
)
#include "main.h"
#include "stm32f1xx_hal.h"
#include "i2c.h"
#include "gpio.h"
#include "oled.h"
void SystemClock_Config(void);
void TM_SystemClock_Config(uint32_t PLLMUL);
void TM_UpdateStatus(void);
void TM_UpdateStatus(void) {
OLED_FillScreen(Black);
OLED_SetCursor(0, 0);
OLED_Printf("~~TechMaker~~\n");
OLED_Printf("Overclocking...\n");
OLED_Printf("CLK: %i MHz\n", HAL_RCC_GetHCLKFreq() / 1000000);
OLED_UpdateScreen();
}
int main(void) {
HAL_Init();
uint32_t mults[] = {
RCC_PLL_MUL2,
RCC_PLL_MUL3,
RCC_PLL_MUL4,
RCC_PLL_MUL5,
RCC_PLL_MUL6,
RCC_PLL_MUL7,
RCC_PLL_MUL8,
RCC_PLL_MUL9,
RCC_PLL_MUL10,
RCC_PLL_MUL11,
RCC_PLL_MUL12,
RCC_PLL_MUL13,
RCC_PLL_MUL14,
RCC_PLL_MUL15,
RCC_PLL_MUL16 };
SystemClock_Config();
MX_GPIO_Init();
MX_I2C2_Init();
OLED_Init(&hi2c2);
OLED_DisplayOn();
TM_UpdateStatus();
while (1) {
for (int i = 0; i < sizeof(mults)/sizeof(mults[0]); i++) {
// set new clock
TM_SystemClock_Config(mults[i]);
// update i2c speed
MX_I2C2_Init();
// Update OLED with new values
TM_UpdateStatus();
// Do some tests and hard work ;)
for (int count = 0; count < 5; ++count) {
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_Delay(100);
}
}
}
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
_Error_Handler(__FILE__, __LINE__);
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
_Error_Handler(__FILE__, __LINE__);
}
HAL_RCC_MCOConfig(RCC_MCO, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
void TM_SystemClock_Config(uint32_t PLLMUL) {
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
// revert to HSE source
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE; // <- HSE
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) {
_Error_Handler(__FILE__, __LINE__);
}
// Setup PLL with new multiplier
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = PLLMUL; // <- multiplier is here :)
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
_Error_Handler(__FILE__, __LINE__);
}
// Go back to PLL source
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // <- PLL welcome back
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
_Error_Handler(__FILE__, __LINE__);
}
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}