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

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

13 April 2018
Embedded

Overclocking in embedded
Overclocking in embedded

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

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

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

Реалізація такого функціонала подана нижче (функція 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);

}


Подібні матеріали ми також пишемо на нашій сторінці TechMaker в Facebook та розповідаємо на наших курсах