重温STM32基础:时钟树
文章目录时钟产生电路4个时钟源SystemInit()时钟配置SystemInit()的调用SystemInit()时钟配置流程RM中关于系统时钟的描述SystemInit()代码简析使能HSI并初始化SetSysClockTo72()设置vector table频率限制安全机制摘要本文深入解析 STM32F103 的时钟树配置机制。首先介绍 4 个时钟源HSI、HSE、LSI、LSE及其用途随后详细剖析SystemInit()的调用时机与配置流程包括使能 HSI、通过SetSysClockTo72()将系统时钟从 8MHz 倍频到 72MHz、配置 Flash 预取与等待周期、设置总线分频系数以及向量表VTOR的重定位接着讨论 72MHz 主频的物理限制与超频风险最后介绍 CSS 时钟安全机制如何在外部时钟失效时自动切回 HSI保障系统稳定运行。时钟树是在STM32中产生时钟并且发配到STM32中各个外设的系统。时钟是一切外设运行的基础。配置流程有点复杂。STM32会提供时钟树配置代码就在SystemInit()中。相关代码是在我们的main函数之前运行的。上面是datasheet提供的时钟树结构图。中间有个红色虚线左边是时钟产生电路右边是时钟分配电路。时钟产生电路4个时钟源内部8MHz 高速RC振荡器外部4~16MHz高速石英晶体振荡器就是晶振。一般接8MHz外部32.768KHz低速晶振一般给RTC提供时钟内部40KHz低速RC振荡器一般给看门狗用1和2两个高速时钟都是提供系统时钟的。AHB,APB1和APB2都是来源于这两个高速晶振。这两个晶振都可以使用但是外部石英振荡器比内部RC振荡器更加稳定所以一般选外部石英晶振作为时钟源但是如果系统很简单而且不需要精确时钟那可以选择内部RC振荡器作为这些总线的时钟源这样可以省下外部石英晶振电路了。SystemInit()时钟配置SystemInit()的调用首先可以注意一下的是SystemInit()是在Reset_Handler()中被调用的Reset_Handler PROC EXPORT Reset_Handler[WEAK]IMPORT SystemInit IMPORT __main LDR R0,SystemInit BLX R0 LDR R0,__main BX R0 ENDP这里面有几个armasm汇编器的语法PROC…ENDP表明这是一个子程序procedure块是一个结构性标记EXPORT“导出这个符号”表示这个符号全局可见相当于C语言里把一个符号设置为“全局/外部”可见。不标明EXPORT表示这个符号只有当前文件可见IMPORT表明这个符号来自于另一个文件是另一个文件的EXPORT符号让链接器去其他文件里面找。相当于C里面的extern声明。WEAK表示这是一个弱符号。如果别的地方有强符号则用别的地方的定义。一般startup代码里面已经给Reset_Handler一个默认实现了。但是用户可以自己去实现同名的Reset_Handler来覆盖整个默认实现。SystemInit()时钟配置流程老师在课程中提到“在系统复位之后首先会使用HSI8MHz作为系统时钟然后启动HSE到PLL锁相环进行倍频9倍得到72MHz等到PLL稳定之后选择PLL为系统时钟。此时就把系统时钟从8MHz切换到了72MHz”。关于这个描述从RM和代码中都证实了一下。RM中关于系统时钟的描述After a system reset, the HSI oscillator is selected as system clock. When a clock source is used directly or through the PLL as the system clock, it is not possible to stop it.意思是在系统复位之后HSI会被选为系统时钟。当一个时钟源被直接或者被PLL用作系统时钟的时候是无法停止它的。对于后半句话开始我理解成了“一个时钟源运行起来之后就无法停止”但是其实不是。只有当它作为系统时钟的时候也就是被使用的时候不能停止。我们可以把系统时钟切换成其他时钟源然后停掉这个没有被使用的时钟。对于“被PLL使用的时候无法停止它”也很好理解因为时钟源是PLL的上游下游还在被使用的时候当然不能把源头停掉。A switch from one clock source to another occurs only if the target clock source is ready (clock stable after startup delay or PLL locked). If a clock source which is not yet ready is selected, the switch will occur when the clock source will be ready. Status bits in the Clock control register (RCC_CR) indicate which clock(s) is (are) ready and which clock is currently used as system clock.这一段的描述也可以在SystemInit()的代码中得到体现如下SystemInit()代码简析使能HSI并初始化上来就是这个操作/* Set HSION bit */RCC-CR|(uint32_t)0x00000001;就是把HSI打开。然后做了一大堆的配置和reset。切换到使用HSE并且配置成72MHz的过程是在最后的一个SetSysClock()中做的。基于system clock frequence的一个宏定义调用到SetSysClockTo72()。SetSysClockTo72()总体流程使能HSE并等待它其稳定配置Flash的prefetch和latency配置AHB, APB1和APB2的时钟分频系数使能PLL并等待它锁定把SYSCLK切换到PLL时钟这里主要分析1~3,4和5就是简单的配置寄存器。/* Enable HSE */RCC-CR|((uint32_t)RCC_CR_HSEON);/* Wait till HSE is ready and if Time out is reached exit */do{HSEStatusRCC-CRRCC_CR_HSERDY;StartUpCounter;}while((HSEStatus0)(StartUpCounter!HSE_STARTUP_TIMEOUT));首先使能了HSE然后开始等待一直到RCC_CR中的某个bit显示HSE准备好了。这和RM中的描述一致。后面一段代码看得我比较懵但是这一段是从系统角度考虑的/* Enable Prefetch Buffer */FLASH-ACR|FLASH_ACR_PRFTBE;/* Flash 2 wait state */FLASH-ACR(uint32_t)((uint32_t)~FLASH_ACR_LATENCY);FLASH-ACR|(uint32_t)FLASH_ACR_LATENCY_2;/* HCLK SYSCLK */RCC-CFGR|(uint32_t)RCC_CFGR_HPRE_DIV1;/* PCLK2 HCLK */RCC-CFGR|(uint32_t)RCC_CFGR_PPRE2_DIV1;/* PCLK1 HCLK */RCC-CFGR|(uint32_t)RCC_CFGR_PPRE1_DIV2;最开始是对FLASH的配置。从代码看这应该也是一个寄存器。但是在RM中没有找到。后来发现ST专门提供了另一个文档“STM32F10x闪存编程手册”这是一个programming manual而不受reference manual。也大概了解了一下这两类文档的区别RM主要描述系统架构、一般的外设它们的硬件和配置。主要告诉如何使用。总体来说如何配置寄存器简单理解而PM如其名字所示告诉如何编程编程模型指令和操作序列。涉及怎么解锁、发指令、怎么等待等。不是配置几个寄存器就完事的。ST对Flash和Cortex内核都提供了PM。具体的Flash操作看起来有点复杂以后研究吧。这里先看一下这个ACR。这里之所以要动Flash相关内容是因为MCU从Flash取指令但是Flash本身读写速度跟不上72MHz也就是说Flash控制器不可能在1/72^6 秒内获取一条指令。一开始8MHz的时候还能跟上所以需要配置Flash控制器让它在72MHz的时候多等几个周期保证一条完整指令被拿出来。另外还配置了开启prefetch buffer也是提升指令读取速度。后面三行代码是配置AHB, APB1和APB2的总线时钟的分频系数也就是第一幅图中的三个紫色的框的配置。同时从这幅图可以看到AHB的时钟由SYSCLK72MHz分频得到APB1和APB2的时钟是由AHB的时钟分频得到。根据几个宏定义的值AHB的分频系数是1也就是72MHzAPB1的分频系数是2也就是36MHzAPB2的分频系数是1也就是72MHz注之前在说定时器的时候一直说定时器的时钟源都是72MHz。但是从框图中看有些TIM是在APB1上的而APB1的频率是36MHz。是之前说错了么并不是可以发现框图中青色的框是从APB1之后分出来的它是把之前非1的分频再2倍频了它后面是TIM234所以这一路分支是专门为了TIM2,3,4配置的频率。之前2分频这里再2倍频所以TIM2,3,4的频率还是72MHz。TIM1也是一样的待遇不过它在APB2的分支上。所以在STM32F103中默认配置下用默认的systemInit()所有定时器的基准频率都是72MHz.框图中的粉红色框是“RCC使能”也就是对应代码里的诸如“RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);” 这样的代码后面就是设置PLL到72MHz并且在它稳定之后把它设置为系统时钟。设置vector tableSCB是Cortex 内核寄存器所以如果要细看就要看Cortex PMVTOR是vector table offset register。在systemInit的最后VTOR寄存器的值更新到RAM或者Flash。#ifdefVECT_TAB_SRAMSCB-VTORSRAM_BASE|VECT_TAB_OFFSET;/* Vector Table Relocation in Internal SRAM. */#elseSCB-VTORFLASH_BASE|VECT_TAB_OFFSET;/* Vector Table Relocation in Internal FLASH. */#endif之前听说ARM Cortex-M的core上来默认从0x00000000地址读取MSP从0x00000004得到Reset_Handler并跳转去执行。运行的起始地址不是应该是固定的么为什么这里还要更新VTORSTM32有存储器映射机制它的0x00000000是另一个地址的别名。这个零地址实际表示什么取决于两个Boot引脚在复位时的状态。BOOT1BOOT00x0000 0000 实际指向x0Flash0x0800 000001系统存储器内置 Bootloader11内部 SRAM0x2000 0000正常情况我们设置的时指向Flash也就是程序是在0x08000000这里的。程序上电之后读取MSP和Reset_Handler看上去是从0x00000000和0x00000004读实际读的是0x08000000和0x08000004。这里就是把SCB-VTOR指向真正的中断向量表也就是代码开始的地方。如果我们只有一个application程序直接放在FLASH里这一句赋值没有也没关系。但是如果我们自己的程序开始之前有bootloader程序比如0x08000000是bootloader程序app程序在0x08004000这个地方那么我们就必须指定SCB-VTOR了。其实ST的代码也为我们留好了接口就是设置VECT_TAB_OFFSET。这个值默认为0。如果app程序在0x08004000这个地方那么这个宏应该设置为0x4000。频率限制在STM32F103的datasheet上上来就说“支持最大72MHz”主频。这个最大频率哪儿来的从程序看起来系统时钟72MHz是通过程序设置的从电路结构看起来它还取决于外部时钟频率。那我们可以挂一个更高频的外部晶振比如16MHz或者调大PLL倍频来一个大于72MHz的频率可以么这个操作本身当然可以甚至可以让MCU跑在144MHz超频但是这已经不是STM32的规格保证很不建议这么做。72MHz是ST官方在芯片设计、制造和测试中确定的保证稳定工作的最高频率它取决于MCU中的晶体管速、Flash访问时间和总线时序等这是一个物理限制。所以在设计板子时要基于MCU的主频来选择外部晶振并且如果有必要需要修改PLL倍频系数。而不是反过来用PLL和外部晶振决定MCU主频另外PLL本身也是对输入的时钟频率有限制的。这是硬件相关的特性相关信息可以在DS中找到而不是RM。从这个表格可以看出PLL的输入范围是1~25MHz。之前想过为什么不直接用一个72MHz的外部晶振。。。因为这里PLL顶不住啊。要么就是先降频再倍频的操作不过这样就多此一举了。所以外部晶振的选择要迁就MCU的主频以及它内部的电路限制而不是外部晶振决定MCU的频率上限。安全机制CSS模块可以监测外部时钟的状态如果外部时钟失效CSS会把系统时钟源切换回HSI保证系统时钟的运行防止卡死造成事故。包括再高级定时器的刹车系统处也有CSS的身影。目的就是为了在外部时钟异常的时候让输出控制的电机立刻停止。课程中老师也提了一个他曾经踩过的坑如果HSE坏了我们可能看到的现象是定时器报定时中断频率比我们设置的要慢将近10倍。因为STM32会切换到默认的HSI时钟HSI的频率是8MHz只有期望值的1/9。所以如果感觉系统时钟慢了可以怀疑一些HSE是不是正常被使用或者是否正常工作。

相关新闻

披着 Gemini 3.8 Flash 的外衣?海外测试平台惊现神秘超强大模型,疑似未官宣的 Gemini 4 Pro:深度实测与全景技术扒皮

披着 Gemini 3.8 Flash 的外衣?海外测试平台惊现神秘超强大模型,疑似未官宣的 Gemini 4 Pro:深度实测与全景技术扒皮

先说核心震撼结论:你以为你调用的是几毛钱百万 Token 的轻量级“打工人”模型,背后给你算题的却可能是 Google 尚未面世的超级旗舰! 竞技场异动惊雷:在 2026 年 9 月中的 LMSYS Chatbot Arena 盲测对决与特定 API 灰度请求中&…

2026/10/3 2:38:38 阅读更多 →
从零开始的故事:一个番茄钟是如何诞生的

从零开始的故事:一个番茄钟是如何诞生的

这是一个关于时间、代码和 Electron 的故事。我们将跟随一个番茄钟应用(Tomato Clock)从一行命令到桌面窗口的完整旅程,逐层剖析它的源码架构。 解读源码链接 序:一切从一个命令开始 深夜,你敲下: npm ru…

2026/10/4 2:47:18 阅读更多 →
第060篇 Java 阶段面试通关地图——六十个考点串联复盘

第060篇 Java 阶段面试通关地图——六十个考点串联复盘

摘要:本篇是《Android软件开发面试从入门到精通》第 60 篇,主题为「Java 阶段面试通关地图——六十个考点串联复盘」。这一篇我们把「Java 阶段面试通关地图——六十个考点串联复盘」一次讲透:先立概念,再拆机制,最后落到工程与面试表达,一条线不绕弯。 关键词:Android …

2026/10/4 2:44:27 阅读更多 →

最新新闻

《三国演义》人物出场统计:Python中文文本挖掘实战

《三国演义》人物出场统计:Python中文文本挖掘实战

最近在整理中文文本挖掘的入门案例时,手边一直放着一个名为threekingdoms.txt的文件——《三国演义》的中文纯文本。很多人都拿它当过练手语料,最经典的需求就是“人物出场统计”:把三国人物按出现次数排个序,看看谁才是全书真正的…

2026/10/4 7:06:46 阅读更多 →
国际学术会议投稿避坑:EI/Scopus检索与IEEE/ACM出版识别指南

国际学术会议投稿避坑:EI/Scopus检索与IEEE/ACM出版识别指南

每年到了年底,实验室的群里和邮箱里就会扎堆出现各种会议通知。2026年2月的国际学术会议清单也已经挂出来了,理工社科都有,像艾思科蓝这类信息平台整理得尤其整齐:EI会议、IEEE/ACM/JPCS权威出版、双一流主办、院士主讲、15城联动…

2026/10/4 7:06:46 阅读更多 →
消防指挥论文别硬扛:从火场调研到定稿,我会这样搭 AI 工具箱 [特殊字符]

消防指挥论文别硬扛:从火场调研到定稿,我会这样搭 AI 工具箱 [特殊字符]

先把场景说具体:假如你是消防指挥专业学生,正在做本科毕业论文,题目类似 《城市商业综合体地下停车场火灾应急指挥流程优化研究——以某市某商圈为例》。 这题难就难在,它不是单纯背理论就能写完:你要梳理接警响应、力…

2026/10/4 7:06:46 阅读更多 →
从一组定位轨迹到一篇合格毕设:导航专业的 AI 工具搭子清单 [特殊字符]

从一组定位轨迹到一篇合格毕设:导航专业的 AI 工具搭子清单 [特殊字符]

先把场景说具体:资源环境与安全大类 / 测绘地理信息类 / 导航与位置服务专业的同学,很可能会遇到这样一个毕业设计—— “校园或园区室内外无缝导航定位系统设计与精度评估” 要完成 GNSS 室外定位、UWB/蓝牙 Beacon 等室内定位补充、电子地图展示、路径…

2026/10/4 7:06:46 阅读更多 →
经济学论文用AI生成论文软件怎么写才不踩雷?三步稳妥用法

经济学论文用AI生成论文软件怎么写才不踩雷?三步稳妥用法

经济学专业的同学对论文又爱又怕:爱的是选题丰富,怕的是计量模型、回归结果、稳健性检验一套流程走下来,任何一个环节出错都得推倒重来。于是不少人把目光投向 AI 生成论文软件,结果生成的文字被导师一眼看穿——"这段边际分…

2026/10/4 7:06:46 阅读更多 →
Linux环境下华为Hilink SDK接入实战:从环境搭建到设备量产

Linux环境下华为Hilink SDK接入实战:从环境搭建到设备量产

标题里写着“目录”,其实就是整个系列的第一篇——把项目背景、技术选型、目录结构、开发规划一次讲清楚。这篇文章我按照自己在Linux环境下跑通华为Hilink SDK的完整经历来写,把踩过的坑、想明白的逻辑、后续开发路线都交代一遍,给准备入坑的…

2026/10/4 7:05:46 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →