Зміна тактової частоти STM32 під час роботи

Зміна частоти stm32 під час роботи

Дерево тактування stm32f103

Під час роботи мікроконтроллера можливо програмно змінювати його частоту. Порядок дій при цьому наступний:

  • Перемкнутися з PLL на HSE
  • Змінити множники/дільники PLL
  • Дочекатися стабілізації його частоти
  • Перемкнутися з HSE на PLL

Реалізація такого функціонала подана нижче (функція TM_SystemClockConfig() )

github gist

#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);

}