STM32使用cubemx生成代码的系统时钟频率配置
当使用cubemx软件自动生成hal库代码时,我们在可视化界面配置的系统时钟频率会通过SystemClock_Config()
函数进行配置。如下图所示:
下面则是cubemx中可视化界面配置时钟频率的页面。
使用了外部高速时钟HSE当做时钟源,随后对外部高速时钟进行了1分频;然后进入PLL:选择HSE为PLL时钟源,配置PLL Mul为x9,将系统频率选择为PLLCLK输入,然后得到SYSCLK。SYSCLK通过AHB Prescaler(1分频)得到HCLK时钟,通过APB1 Prescaler(2分频)得到PCLK1时钟,通过APB2 Prescaler(1分频)得到PCLK2时钟。
其实与SystemClock_Config()
函数中的配置是一模一样的:
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; //对外部高速时钟进行1分频
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; //选择HSE为PLL时钟源
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; //配置PLL Mul为x9
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; //系统频率选择PLLCLK输入
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; //AHB 1分频
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; //APB1 2分频
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; //APB2 1分频
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
系统频率配置过程
首先,STM32上电或复位后,通过汇编代码可以看出来:程序首先会进入SystemInit()
函数中做一些初始化的工作,然后再进入main
函数中执行。如下图所示:
但是hal库中的SystemInit()
函数并没有做配置时钟相关的操作。所以时钟配置只有在main
函数中的SystemClock_Config()
函数进行。
那么,在STM32上电后到执行SystemClock_Config()
函数之前,STM32的运行时钟频率是多少呢?
以STM32F1系列为例,在system_stm32f1xx.c文件中,有具体注释说明,如下图:
- 该文件提供了两个函数和一个全局变量:
SystemInit()
:初始化系统时钟。SystemCoreClockUpdate()
:更新SystemCoreClock全局变量。SystemCoreClock
:表示系统运行频率。
- 当设备(单片机)复位之后,HSI(内部高速时钟,在f1系列为8MHz)会作为系统的时钟源。在"startup_stm32f1xx_xx.s"文件中会在进入
main
函数之前调用SystemInit()
函数去配置系统时钟(显然没有进行配置)。 - HSE晶振的默认值设置为了8MHz(根据产品不同,也会设置成25MHz),定义为了HSE_VALUE宏。如果HSE直接或者通过PLL作为了系统时钟源,而且你使用了不同频率的外部晶振,那就必须修改HSE_VALUE的值为当前所用的时钟频率。
所以说,在程序还没执行到SystemClock_Config()
函数之前,单片机都是使用内部高速时钟源HSI来作为系统时钟频率的,该HSI的频率会根据单片机的类型不同而有所不同。在执行了SystemClock_Config()
函数之后,系统时钟频率便会设置为我们在cubemx中设置的频率。
SystemCoreClock变量
当前系统时钟频率的值保存在了SystemCoreClock
全局变量中。可以通过该变量来得到当前系统的运行频率。
在system_stm32f1xx.c文件中,定义了SystemCoreClock
变量,如下图。但是初始值给了一个16000000。这个初始值其实并不是一开始的系统运行频率。用户可以通过几种方式来更新该变量的值,更新之后才代表真正的系统运行频率。
更新SystemCoreClock
变量的三种方法:
- 调用
SystemCoreClockUpdate()
函数。 - 调用
HAL_RCC_GetHCLKFreq()
函数。 - 调用
HAL_RCC_ClockConfig()
函数。
总结
- 在system_stm32xx.c文件中,定义了
HSI_VALUE
和HSE_VALUE
两个宏,分别表示该单片机的内部高速时钟频率和外部高速时钟频率,两个值一定要与实际的相对应。 - 当单片机复位之后,HSI(内部高速时钟,在f1系列为8MHz)会作为系统的时钟源。直到执行了
SystemClock_Config()
函数,系统时钟频率便会设置为在cubemx中设置的频率。 SystemCoreClock
全局变量表示当前系统时钟频率,并不是实时的,需要更新。SystemCoreClock
全局变量会用于配置滴答定时器(SysTick timer)和其他参数。