stm32f103学习笔记-16-RCC(第2节)-讲解系统时钟配置函数SetSysClockTo72()

引言

在上一节中,我们深入了解了STM32的时钟树,知道了时钟源、PLL、系统时钟和总线时钟之间的关系。今天,我们进入实战环节,重点讲解STM32标准库中一个非常重要的函数------系统时钟配置函数SetSysClockTo72()。这个函数负责将系统时钟配置为72MHz,是STM32高性能运行的基础。理解这个函数如何工作,会让你对STM32的时钟配置有更扎实的掌握。

一、SetSysClockTo72()函数是什么?

SetSysClockTo72() 是STM32标准库中的一个函数,它的作用就是按照ST官方推荐的配置,将系统时钟(SYSCLK)设置为72MHz。在STM32启动时,默认使用的是8MHz的HSI(内部时钟),但通过调用这个函数,我们可以切换到更高速的72MHz时钟,从而提升整个系统的性能。这个函数通常定义在STM32标准库的 system_stm32f10x.c文件中。当你使用库开发时,在 SystemInit()函数中会调用它。理解它的实现,能帮助我们日后自定义时钟配置。

1.1 SetSysClockTo72() 函数的位置

从图片可以看出,SetSysClockTo72()函数位于代码文件 system_stm32f10x.c中。具体行号大约在第987行(图片中显示行号范围为974-1002,第987行被红色方框标注,并有"官方默认72M"的提示)。这是该函数的定义位置。

1.2 配合讲解:从启动到 SetSysClockTo72() 的调用流程

  • 启动文件调用 SystemInit :第1张图片显示启动文件 startup_stm32f10x_hd.s中的复位中断服务程序(Reset_Handler)。代码中有 LDR R0, =SystemInitBLX R0指令,这表明在芯片复位后,会跳转到 SystemInit函数执行。
  • SystemInit 函数调用 SetSysClock :图片显示在 system_stm32f10x.c文件的 SystemInit函数中(约第262行),有 SetSysClock()函数调用。通过右键菜单的"Go To Definition Of 'SetSysClock'"选项,可以跳转到其定义。
  • SetSysClock 函数根据宏定义选择时钟配置 :图片显示 SetSysClock函数的定义(约第419-437行)。它是一个静态函数,内部通过条件编译(如 #ifdef SYSCLK_FREQ_72MHz)根据宏定义调用不同的时钟设置函数。

默认情况下,我们会定义 SYSCLK_FREQ_72MHz这个宏(在这个c文件的前面定义)。如果我们定义成24兆、36兆、48兆、56兆等,那么就会调用不同的函数把系统时钟配置成相应的频率。

SYSCLK_FREQ_72MHz被定义(如图片所示,第115行有 #define SYSCLK_FREQ_72MHz 72000000),因此会调用 SetSysClockTo72()函数将系统时钟配置为72MHz。

  • 最终定位到 SetSysClockTo72() :如图片所示,SetSysClockTo72()函数是实际实现72MHz时钟配置的具体函数。官方推荐使用72MHz作为最高稳定运行频率,因此默认定义了这个宏。

二、函数工作流程概览

2.1 函数概述与芯片系列区分

SetSysClockTo72()函数的核心任务是将STM32F103的系统时钟配置为72MHz ,并同步设置HCLK、PCLK2和PCLK1的分频系数。该函数通过条件编译宏STM32F10X_CL来区分芯片系列:基础型(小容量、中容量、大容量)和互联型(STM32F105/107)。为简化分析,我们删除互联型相关的代码,专注于基础型芯片的配置流程。

2.2 使能HSE(外部高速时钟)

复制代码
static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
      
/* 使能HSE */
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);

首先,代码执行 RCC->CR |= ((uint32_t)RCC_CR_HSEON);来使能HSE。它控制的是RCC_CR寄存器的HSEON位(第16位)来启动外部8MHz晶振,将该位置1以使能HSE。因为开发板上用的是无源晶振,起振时需要配合起振电容,需要一段时间。启动完毕后,RCC_CR寄存器的HSERDY位(硬件)会置1,我们通过判断这个位是否置1来判断HSE是否启动成功。

比如指南者开发板,这个8MHz的无源晶振就是HSE。旁边还有一个晶振是LSE,是给RTC实时时钟提供的,频率是32.768kHz。霸道开发板也是类似的,8MHz的晶振就是HSE。

2.3 等待HSE就绪与超时处理

复制代码
/* 等待HSE就绪,若超时则退出 */
do
{
  HSEStatus = RCC->CR & RCC_CR_HSERDY;
  StartUpCounter++;  
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  

接着是等待HSE就绪(Wait till HSE is ready),如果出现故障(比如晶振没焊好),需要有超时处理机制(if Time out is reached exit)。代码通过一个do-while循环来实现:读取CR寄存器的HSERDY位(第17位),如果HSE就绪了,该位由硬件在HSE稳定后置1,循环条件不满足就跳出。

2.4 配置Flash预取指和等待周期

之后还会再判断一次,如果HSE启动成功,程序就继续往下执行。当然,有成功就有失败,对应着后面的else分支。HSE启动成功后,配置Flash以确保CPU能稳定读取指令。STM32程序代码是存储在Flash里面的。内核(比如Cortex-M3)从Flash读取指令执行时,需要一定的等待时间。预取指是指,在执行当前指令时,提前将下一条可能要执行的指令从Flash读到缓冲区,提高效率。同时,因为Flash的读取速度跟不上很高的系统时钟频率,所以需要插入等待周期。这个等待周期的数量需要根据我们最终配置的系统时钟频率来设置。这两三句代码操作的寄存器属于Flash模块,不在RCC部分,具体可以参考STM32的《闪存编程手册》中关于ACR寄存器的说明。

复制代码
if (HSEStatus == (uint32_t)0x01)
{
/* 使能预取指缓冲区 */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* Flash 2个等待状态 */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
......
else
{ 
  /*如果HSE启动失败,应用程序将会有错误的时钟配置,用户可以在这里添加代码来处理这个错误*/
}
  • 预取指功能:CPU(内核,比如Cortex-M3)------>Flash------>STM32程序代码。

  • 等待状态 :当系统时钟超过一定频率(如≥48MHz),必须配置Flash等待周期。FLASH_Latency_2表示插入2个等待状态,适用于48MHz到72MHz的系统时钟。

2.5 配置总线分频因子

复制代码
/* HCLK = SYSCLK = 72M*/
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK = 72M */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK = 36M*/
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

通过配置RCC_CFGR寄存器的不同位段,设置各总线时钟:

  • HPRE(AHB预分频) :设置为1分频(HPRE_DIV1),使HCLK = SYSCLK = 72MHz

  • PPRE2(APB2预分频) :设置为1分频(PPRE2_DIV1),使PCLK2 = HCLK = 72MHz(APB2总线最高支持72MHz)。

  • PPRE1(APB1预分频) :设置为2分频(PPRE1_DIV2),使PCLK1 = HCLK/2 = 36MHz(APB1总线最高支持36MHz)。

2.6 配置并使能PLL(锁相环)

复制代码
/* PLL配置: PLLCLK = HSE * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);

这几条语句配置PLL的时钟源和倍频系数:

  • PLLXTPRE:控制HSE是否2分频后进入PLL。此处默认HSE不分频。

  • PLLSRC:选择HSE作为PLL的输入时钟源(而非HSI/2)。

  • PLLMUL :设置倍频因子为9。基于8MHz的HSE,PLLCLK = 8MHz * 9 = 72MHz

配置完成后,使能PLL并等待其稳定:

复制代码
/* 使能PLL */
RCC->CR |= RCC_CR_PLLON;
/* 等待PLL就绪 */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{

通过循环检测PLLRDY位,等待PLL输出稳定。

2.7 选择系统时钟并等待切换完成

复制代码
  /* 选择PLLCLK作为系统时钟源 */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
  /* 等待PLLCLK切换为系统时钟源 */
  while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
  {
  }
}
  else
  { /* If HSE fails to start-up, the application will have wrong clock 
         configuration. User can add here some code to deal with this error */
  }
}
  • 通过配置CFGR寄存器的SW位,将系统时钟SYSCLK的来源切换为PLL输出

  • 切换过程需要时间,通过循环检测SWS状态位,确认PLL已成功成为系统时钟源(SWS值为0x08)。

2.8 HSE启动失败处理

若HSE启动失败(例如晶振故障),程序会执行else分支的代码。此处默认仅有注释,开发者可在此添加错误处理机制,如切换至HSI或触发安全响应。

通过以上步骤,SetSysClockTo72()函数完成了将STM32F103系统时钟配置为72MHz的整个过程,为后续外设提供了稳定的时钟基础。

三、详细步骤解析(选看)

结合STM32中文参考手册和零死角文档,我们来分析 SetSysClockTo72()的关键代码(以STM32F103系列为例)。注意:以下代码是简化版,重点在原理说明。

步骤1: 启用HSE(外部高速时钟)

HSE是外部晶振提供的时钟,精度高,是达到72MHz的基础。函数首先会启用HSE并等待其稳定。

复制代码
// 使能HSE:设置RCC_CR寄存器的HSEON位为1
RCC->CR |= RCC_CR_HSEON;

// 等待HSE就绪:循环检查HSERDY位是否为1
while (!(RCC->CR & RCC_CR_HSERDY));

原理说明

  • HSEON位是RCC_CR寄存器的第16位,置1后启动外部晶振。

  • HSERDY是状态位,晶振起振需要时间,等待它变为1表示HSE已稳定。如果不等待直接后续操作,可能导致时钟配置失败。

  • **为什么用HSE?**HSE比HSI精度更高,能保证PLL输出72MHz的稳定性。如果项目对时序要求高(如通信外设),必须使用HSE。

步骤2: 配置Flash延迟

当系统时钟切换到高速时,CPU访问Flash存储器需要更长的等待时间,否则可能读取出错。因此,函数会配置Flash的等待周期。

复制代码
// 设置Flash延迟:2个等待周期(因为72MHz > 48MHz)
FLASH->ACR |= FLASH_ACR_LATENCY_2;
// 启用Flash预取缓冲区(提升读取效率)
FLASH->ACR |= FLASH_ACR_PRFTBE;

原理说明

  • 参考手册规定:当SYSCLK ≤ 24MHz时,Flash无需等待;24MHz < SYSCLK ≤ 48MHz时,需1个等待周期;SYSCLK > 48MHz时,需2个等待周期。72MHz属于高速,所以设2。

  • 预取缓冲区是Flash的缓存机制,能提前加载指令,提高CPU效率。不开启的话,系统可能运行不稳定。

  • 新手注意:这是容易忽略的一步!如果忘记配置,系统在高速下可能频繁复位或运行异常。

步骤3: 配置PLL(锁相环)

PLL是"倍频器",它将HSE的8MHz信号倍频到72MHz。函数会设置PLL的输入源和倍频系数。

复制代码
// 配置PLL:选择HSE作为PLL输入源(不分频),9倍频
RCC->CFGR |= RCC_CFGR_PLLSRC_HSE;  // PLL源 = HSE
RCC->CFGR |= RCC_CFGR_PLLMUL_9;    // 倍频因子 = 9
// 使能PLL
RCC->CR |= RCC_CR_PLLON;
// 等待PLL锁定
while (!(RCC->CR & RCC_CR_PLLRDY));

原理说明

  • PLLSRC位选择PLL的输入时钟:HSE或HSI/2。这里选HSE(8MHz),保证输入精度。

  • PLLMUL位设置倍频系数,9倍频后:8MHz × 9 = 72MHz。

  • PLL需要时间锁定频率,等待PLLRDY位为1后,表示PLL输出稳定。如果跳过等待,切换系统时钟可能失败。

  • **为什么是9倍频?**ST官方推荐72MHz为稳定最大值。如果使用HSI/2(4MHz)作为输入,即使16倍频也只能到64MHz,达不到72MHz。

步骤4: 配置总线分频器

系统时钟SYSCLK变为72MHz后,需要分频给AHB、APB等总线,确保外设时钟不超限。

复制代码
// 配置预分频器:
// AHB分频 = 1(HCLK = SYSCLK = 72MHz)
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
// APB1分频 = 2(PCLK1 = HCLK/2 = 36MHz,不超过36MHz限制)
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
// APB2分频 = 1(PCLK2 = HCLK = 72MHz)
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;

原理说明

  • AHB总线连接内核和高速外设,1分频让HCLK跑在72MHz,发挥最大性能。

  • APB1总线用于低速外设(如USART2、I2C),最高36MHz,所以2分频得到36MHz。

  • APB2总线用于高速外设(如GPIO、ADC),支持72MHz,1分频即可。

  • 分频的重要性:如果APB1设成1分频(72MHz),会超频导致外设工作异常!务必参考手册的限值。

步骤5: 切换系统时钟到PLL

最后,函数将系统时钟源从默认的HSI切换到PLL输出。

复制代码
// 切换系统时钟源为PLL
RCC->CFGR |= RCC_CFGR_SW_PLL;
// 等待切换完成(检查SWS状态位)
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);

原理说明

  • SW位用于选择SYSCLK源(HSI、HSE或PLL)。这里设为PLL。

  • SWS是状态位,硬件会自动更新它。等待SWS变为PLL,确认切换成功。如果切换中途被打断,系统可能卡死。

  • 切换后的变化:此时SYSCLK = PLLCLK = 72MHz,整个系统"加速"了!

四、完整流程总结

整个SetSysClockTo72()函数的流程可以概括为以下流程图(基于代码逻辑):

复制代码
开始
  ↓
启用HSE → 等待HSERDY
  ↓
配置Flash延迟(2等待周期)
  ↓
配置PLL(源=HSE,倍频=9) → 使能PLL → 等待PLLRDY
  ↓
配置总线分频(AHB=1, APB1=2, APB2=1)
  ↓
切换时钟源到PLL → 等待SWS确认
  ↓
结束,SYSCLK=72MHz

关键原理

  • 顺序性:步骤不能颠倒!例如,必须先启用HSE并等待就绪,才能配置PLL;否则PLL可能用错误输入源。

  • 稳定性检查:每个步骤都有"等待就绪"循环,确保硬件状态稳定后再继续。这是避免故障的核心。

  • 安全限值:总线分频严格遵循手册限值,防止外设超频。

相关推荐
范纹杉想快点毕业2 小时前
100道关于STM32的问题解答共十万字回答,适用入门嵌入式软件初级工程师,筑牢基础,技术积累,校招面试。
驱动开发·单片机·嵌入式硬件·fpga开发·硬件工程
仙人掌_lz2 小时前
Kimi Linear 论文阅读笔记:第一次“线性注意力”全面胜过全注意力
论文阅读·笔记
wdfk_prog2 小时前
[Linux]学习笔记系列 -- [kernel]cpu
linux·笔记·学习
国科安芯3 小时前
多输出电压条件下同步整流效率测试与优化
网络·单片机·嵌入式硬件·安全
li星野4 小时前
打工人日报#20251109
笔记
dxnb224 小时前
【Datawhale25年11月组队学习:hello-agents+Task1学习笔记】
人工智能·学习
nenchoumi31194 小时前
ROS2 Humble 笔记(四)ROS 的最小工作单元-- Node 节点
笔记·机器人·ros2
weixin_387002154 小时前
漏洞修复学习之CVE-2024-10976漏洞复现
数据库·sql·学习·安全·postgresql
搞机械的假程序猿5 小时前
普中51单片机学习笔记-流水灯
笔记·学习·51单片机