STM32使用cubemx生成代码的系统时钟频率配置

发布时间 2023-12-25 22:04:04作者: HAOstudio

STM32使用cubemx生成代码的系统时钟频率配置

当使用cubemx软件自动生成hal库代码时,我们在可视化界面配置的系统时钟频率会通过SystemClock_Config()函数进行配置。如下图所示:

img1

下面则是cubemx中可视化界面配置时钟频率的页面。
使用了外部高速时钟HSE当做时钟源,随后对外部高速时钟进行了1分频;然后进入PLL:选择HSE为PLL时钟源,配置PLL Mul为x9,将系统频率选择为PLLCLK输入,然后得到SYSCLK。SYSCLK通过AHB Prescaler(1分频)得到HCLK时钟,通过APB1 Prescaler(2分频)得到PCLK1时钟,通过APB2 Prescaler(1分频)得到PCLK2时钟。

img2

其实与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函数中执行。如下图所示:

img3

但是hal库中的SystemInit()函数并没有做配置时钟相关的操作。所以时钟配置只有在main函数中的SystemClock_Config()函数进行。
img4

那么,在STM32上电后到执行SystemClock_Config()函数之前,STM32的运行时钟频率是多少呢?

以STM32F1系列为例,在system_stm32f1xx.c文件中,有具体注释说明,如下图:
img5

  1. 该文件提供了两个函数和一个全局变量
  • SystemInit():初始化系统时钟。
  • SystemCoreClockUpdate():更新SystemCoreClock全局变量。
  • SystemCoreClock:表示系统运行频率。
  1. 当设备(单片机)复位之后,HSI(内部高速时钟,在f1系列为8MHz)会作为系统的时钟源。在"startup_stm32f1xx_xx.s"文件中会在进入main函数之前调用SystemInit()函数去配置系统时钟(显然没有进行配置)。
  2. HSE晶振的默认值设置为了8MHz(根据产品不同,也会设置成25MHz),定义为了HSE_VALUE宏。如果HSE直接或者通过PLL作为了系统时钟源,而且你使用了不同频率的外部晶振,那就必须修改HSE_VALUE的值为当前所用的时钟频率。

所以说,在程序还没执行到SystemClock_Config()函数之前,单片机都是使用内部高速时钟源HSI来作为系统时钟频率的,该HSI的频率会根据单片机的类型不同而有所不同。在执行了SystemClock_Config()函数之后,系统时钟频率便会设置为我们在cubemx中设置的频率。

SystemCoreClock变量

当前系统时钟频率的值保存在了SystemCoreClock全局变量中。可以通过该变量来得到当前系统的运行频率。

system_stm32f1xx.c文件中,定义了SystemCoreClock变量,如下图。但是初始值给了一个16000000。这个初始值其实并不是一开始的系统运行频率。用户可以通过几种方式来更新该变量的值,更新之后才代表真正的系统运行频率。
img6

更新SystemCoreClock变量的三种方法:

  1. 调用SystemCoreClockUpdate()函数。
  2. 调用HAL_RCC_GetHCLKFreq()函数。
  3. 调用HAL_RCC_ClockConfig()函数。

总结

  1. system_stm32xx.c文件中,定义了HSI_VALUEHSE_VALUE两个宏,分别表示该单片机的内部高速时钟频率外部高速时钟频率,两个值一定要与实际的相对应。
  2. 当单片机复位之后,HSI(内部高速时钟,在f1系列为8MHz)会作为系统的时钟源。直到执行了SystemClock_Config()函数,系统时钟频率便会设置为在cubemx中设置的频率。
  3. SystemCoreClock全局变量表示当前系统时钟频率,并不是实时的,需要更新。
  4. SystemCoreClock全局变量会用于配置滴答定时器(SysTick timer)和其他参数。