stm32入门篇(6)

启动文件的源码解读不是很难,我们来接着看stm32的时钟配置

关键就是这个图,这个图理解了基本对时钟模块就有了比较好的了解了。

序号 模块名称(概念) 主要功能 / 作用 关键输入 关键输出与范围 相关寄存器位(典型) 备注 / 常见用法
① 外部高速振荡器 HSE 及前分频 接入 4--16 MHz 外部晶振或时钟信号,并可先 /2 作为 PLL 输入 OSC_IN/OSC_OUT 外部晶振;或外部时钟 HSECLK(原始外部时钟);HSECLK/2(经 PLLXTPRE) RCC_CR.HSEON、HSEBYP 使能/旁路;RCC_CFGR.PLLXTPRE 选择是否 /2 常见:外部 8 MHz 晶振,给 PLL 提供 8 MHz 或 4 MHz 输入
② PLL 源选择开关 PLLSRC 在 HSI/2 与 HSE(或 HSE/2) 之间选择 PLL 的输入源 HSI/2(内部 8 MHz/2)、HSE 或 HSE/2(来自①) PLL 输入时钟 PLLCLK_IN RCC_CFGR.PLLSRC 一般为了精确频率,用 HSE;若不接晶振,可用 HSI/2
③ PLL 倍频器 PLLMUL 将 PLL 输入时钟做 ×2~×16 倍频,生成高频系统基准 PLLCLK_IN(来自②) PLLCLK_OUT = PLLCLK_IN × N;最大 72 MHz RCC_CFGR.PLLMULL[3:0] 典型:HSE=8 MHz,选择 ×9 → PLL 输出 72 MHz
④ 系统时钟源选择 SW(SYSCLK 选择器) 在 HSI、HSE、PLLCLK 之间选择系统时钟 SYSCLK HSI、HSE、PLLCLK(来自③) SYSCLK(系统时钟,最大 72 MHz) RCC_CFGR.SW[1:0] 选择;SWS[1:0] 状态 复位默认 HSI;稳定后常切到 PLL 72 MHz 作为 SYSCLK
⑤ AHB 预分频器(HCLK 分频) 对 SYSCLK 做 /1、/2、...、/512 分频,得到 AHB 总线时钟 HCLK SYSCLK(来自④) HCLK = SYSCLK / (1,2,...,512) RCC_CFGR.HPRE[3:0] HCLK 给 CPU、DMA、AHB 外设;通常设为 /1,让 HCLK = 72 MHz
⑥ APB1 预分频器 将 HCLK 再分频,得到低速外设总线 APB1 的时钟 PCLK1(≤36 MHz) HCLK(来自⑤) PCLK1 = HCLK / (1,2,4,8,16),最大 36 MHz;若分频≠1,则某些定时器时钟再×2 RCC_CFGR.PPRE1[2:0] 典型:HCLK=72 MHz,PPRE1= /2 → PCLK1=36 MHz;TIM2--TIM7 定时器时钟会再 ×2 变回 72 MHz
⑦ APB2 预分频器 将 HCLK 分频,得到高速外设总线 APB2 的时钟 PCLK2(≤72 MHz) HCLK(来自⑤) PCLK2 = HCLK / (1,2,4,8,16),最大 72 MHz;分频≠1 时,对应定时器时钟再×2 RCC_CFGR.PPRE2[2:0] 常见:PPRE2 = /1,使 PCLK2 = 72 MHz,供 GPIO、ADC1/2 等高速外设使用

外部时钟源和内部时钟源:

图中黄色 ① 块(HSE OSC)对应的,就是外部高速时钟源:

  • STM32 芯片上只提供振荡放大器 + 接口电路(在芯片里)。

  • 真正的"时钟产生器件"在芯片外面,典型有两种接法:

    1. 外部晶振 + 两个电容

      • 一颗 8 MHz 晶振焊在板子上,两端接到 STM32 的 OSC_IN / OSC_OUT。

      • 晶振两端再各接一个小电容(10~30pF 左右)到地。

      • 这样由 STM32 内部的振荡放大器 + 板上的晶振 + 电容一起组成振荡器。

    2. 外部有源时钟模块

      • 比如一个"8 MHz 方波时钟模块",直接把输出接到 OSC_IN,

      • 这时一般不再用 OSC_OUT,也不需要外接晶振。

不管哪种,产生基准频率的是板子上的器件,所以叫"外部时钟源"。

  • HSI / LSI:在芯片"肚子里"的 RC,什么都不用接 → 内部时钟。

  • HSE / LSE:要在板子上焊"石英石头"(晶振)或有源模块 → 外部时钟。

  • 图上 HSE 块只是"接口 + 控制电路",真正的振荡元件在芯片外。

其他时钟源:

标号 模块名称(概念) 主要功能 / 作用 关键输入 关键输出与范围 相关寄存器位(典型) 备注 / 常见用法
A USB 时钟预分频器 给 USB 外设产生 48 MHz 的 USBCLK PLLCLK(来自 PLL 输出,一般 72 MHz) USBCLK = PLLCLK /1 或 /1.5(目标 48 MHz) RCC_CFGR.USBPRE 典型:PLL=72 MHz 时,USBPRE=0 → /1.5 得到 48 MHz;如果 PLL 配成 48 MHz,则可选 /1 直接送 USB
B Cortex 内核 / AHB 核心时钟域 以 HCLK 作为 Cortex-M3 内核、总线矩阵、存储器接口等的主时钟;并给 FCLK(内核运行时钟) HCLK(来自 AHB 预分频器⑤) FCLK(Cortex 核心时钟)、系统总线时钟(AHB) 与 RCC_CFGR.HPRE、FLASH_ACR 等相关 一般把 HCLK 直接设为 72 MHz,这样 CPU、DMA、SRAM、Flash 等都以 72 MHz 跑;要注意 Flash 等待周期要配置正确
C ADC 时钟预分频器 将 APB2 总线时钟 PCLK2 再分频,给 ADC 产生合适的时钟 PCLK2(来自 APB2 预分频器⑦) ADCCLK = PCLK2 /2、/4、/6、/8,最大 14 MHz RCC_CFGR.ADCPRE[1:0] 典型:PCLK2=72 MHz 时,常选 /6 → 12 MHz,或 /8 → 9 MHz,保证 ≤14 MHz;ADCCLK 过高会影响转换精度/稳定性
D 低速时钟 / RTC / 独立看门狗时钟块 管理 LSE(外部 32.768kHz)、LSI(内部 40kHz)以及 HSE/128 等低速时钟,用于 RTC 和 IWDG LSE 晶振(板上 32.768 kHz)、LSI(40 kHz RC)、HSE 分频 RTCCLK(RTC 时钟)、IWDGCLK(独立看门狗时钟) RCC_BDCR.RTCSEL 选择 RTC 源;LSEON/LSERDY;RCC_CSR.LSION 等 常见:RTC 用 LSE 外部 32.768k(时间更准,可掉电保持);如果板上没焊 LSE,就用 LSI 或 HSE/128;IWDG 一般固定用 LSI
E MCO(主时钟输出)模块 把某个内部时钟输出到 MCO 引脚(常是 PA8),方便测量或给外部芯片当时钟 可选源:HSI、HSE、SYSCLK、PLLCLK/2 等(不同系列略有差异) MCO 引脚上的时钟信号 RCC_CFGR.MCO[2:0] 调试时用得多:例如把 SYSCLK 输出到 MCO,用示波器测当前系统频率是否 72 MHz;也可用作给外部器件供时

容易混淆概念:

  • SYSCLK 是芯片系统总时钟;SysTick 是一个定时器,用 SYSCLK(或其分频)作为计时源,经常被 RTOS 用来当"系统滴答"。

  • RCC 是"时钟和复位管理模块";RTC 是"独立的日历/时间计时模块",通常用低速时钟、可掉电保持。

配置时钟实战:

cpp 复制代码
void HSE_SetSysClock(uint32_t pllmul)
2 {
3 __IO uint32_t StartUpCounter = 0, HSEStartUpStatus = 0;
4 
5 // 把 RCC 外设初始化成复位状态
6 RCC_DeInit();
7 
8 //使能 HSE,开启外部晶振,野火 STM32F103 系列开发板用的是 8M
9 RCC_HSEConfig(RCC_HSE_ON);
10 
11 // 等待 HSE 启动稳定
12 HSEStartUpStatus = RCC_WaitForHSEStartUp();
13 
14 // 只有 HSE 稳定之后则继续往下执行
15 if (HSEStartUpStatus == SUCCESS) {
16 //-----------------------------------------------------------------//
17 // 这两句是操作 FLASH 闪存用到的,如果不操作 FLASH,这两个注释掉也没影响
18 // 使能 FLASH 预存取缓冲区
19 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
20 
21 // SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成 2
22 // 设置成 2 的时候,SYSCLK 低于 48M 也可以工作,如果设置成 0 或者 1 的时候,
23 // 如果配置的 SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了
24 // 0:0 < SYSCLK <= 24M
25 // 1:24< SYSCLK <= 48M
26 // 2:48< SYSCLK <= 72M
27 FLASH_SetLatency(FLASH_Latency_2);
28 //-----------------------------------------------------------------//
29 
30 // AHB 预分频因子设置为 1 分频,HCLK = SYSCLK
31 RCC_HCLKConfig(RCC_SYSCLK_Div1);
32 
33 // APB2 预分频因子设置为 1 分频,PCLK2 = HCLK
34 RCC_PCLK2Config(RCC_HCLK_Div1);
35 
36 // APB1 预分频因子设置为 1 分频,PCLK1 = HCLK/2
37 RCC_PCLK1Config(RCC_HCLK_Div2);
38 
39 //-----------------设置各种频率主要就是在这里设置-------------------//
40 // 设置 PLL 时钟来源为 HSE,设置 PLL 倍频因子
41 // PLLCLK = 8MHz * pllmul
42 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);
43 //-------------------------------------------------------------//
44 
45 // 开启 PLL
46 RCC_PLLCmd(ENABLE);
47 
48 // 等待 PLL 稳定
49 while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
50 }
51 
52 // 当 PLL 稳定之后,把 PLL 时钟切换为系统时钟 SYSCLK
53 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
54 
55 // 读取时钟切换状态位,确保 PLLCLK 被选为系统时钟
56 while (RCC_GetSYSCLKSource() != 0x08) {
57 }
58 } else {
59 // 如果 HSE 开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理
60 // 当 HSE 开启失败或者故障的时候,单片机会自动把 HSI 设置为系统时钟,
61 // HSI 是内部的高速时钟,8MHZ
62 while (1) {
63 }
64 }
65 }

代码里有关闪存的处理:

  • 这两行的意思是:打开 Flash 预取缓冲,并把 Flash 的访问等待周期设为 2,以配合 48--72MHz 的 SYSCLK,防止访问速度过快导致错误。

  • Flash 不像 GPIO 那样在 RCC 里有一个开关时钟位。

    它的"时钟"可以理解为紧跟系统总线时钟,一直在工作;你做的只是给它配置"访问时间参数"(Latency)和"预取功能"。

这里就得说一下smt32的内存分布了:

项目 FLASH SRAM
类型 非易失性存储器(断电不丢) 易失性存储器(断电全没)
在 MCU 里的作用 相当于"硬盘 / 固态盘":放程序、常量、出厂参数等 相当于"运行内存(RAM)":放变量、栈、运行时数据
典型地址(STM32F1) 从 0x0800 0000 开始 从 0x2000 0000 开始
上电后的用途 CPU 从 FLASH 里取指令执行(代码就在里面) 启动代码会把需要的全局/静态变量初始化到 SRAM,然后程序运行过程中读写这里
是否可写 可以,但要按"页擦除+写入",操作复杂且慢,次数有限 随时读写,字节/字对齐即可,速度快
读写速度 读比 SRAM 慢,需要设置 等待周期(Latency),频率越高 Latency 越多 读写都很快,和 CPU 主频接近
断电是否保存数据 ✅ 是,内容保存 ❌ 否,断电全部丢失
容量特点 容量通常比 SRAM 大(比如 64K、128K、256K...) 容量通常比 FLASH 小(比如 20K、32K...)
适合放什么 程序代码(.text)、只读常量(const)、初始化数据、少量配置参数 全局/局部变量、堆栈、运行时缓冲区(如串口缓存、数组、算法数据)
擦写寿命 有限(大约 1万~10万 次擦写级别) 理论上无限次读写(受电路寿命限制,但远大于 Flash)
写入代价 写/擦要走专门的 Flash 编程流程,期间可能会阻塞或不能从同一块执行代码 普通内存访问指令即可,无需特殊流程
典型用法类比 MCU 的"硬盘" MCU 的"内存条"

具体数据展示:

启动代码做的三件关键事:

  1. 设置主栈指针 MSP(指向 SRAM 顶部)

  2. 把 FLASH 里的 .data 初始值复制到 SRAM 的 .data 区

  3. 把 .bss 区全部清零

相关推荐
Zhao1368245539136 分钟前
DP4330A 与DP4363 测试对比
嵌入式硬件
辛迪聊物业数字化41 分钟前
智慧社区小程序落地避坑指南:3类物业方案选型+成本测算,线上服务零翻车
大数据·嵌入式硬件·百度·系统架构
电子科技圈1 小时前
Arm电机控制开发工具哪家强?
arm开发·单片机·嵌入式硬件·设计模式·软件构建
JasmineX-11 小时前
下位机直接控制57步进电机匀速转动
stm32·单片机·嵌入式硬件
聆思科技AI芯片2 小时前
【AI硬件开发上手教程】怎么使用AI编程实现小聆AI语音控制舵机
人工智能·嵌入式硬件·开源·ai编程·智能硬件
abc light2 小时前
ARM 裸机开发(6)——时钟树与定时器
arm开发·单片机·fpga开发
吠品12 小时前
智谱开源 ZCode 之后,真正的考试才开始
单片机·嵌入式硬件·1024程序员节
Chery114013 小时前
高速ADC与精密DAC怎么选?DP9230D与DP8532D应用解析
嵌入式硬件·物联网·射频工程
小李不想当小白16 小时前
SPI通信外设与W25Q64 (硬件SPI)(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
LCG米16 小时前
STM32驱动W5500以太网模块实测:TCP吞吐瓶颈定位、TX指针回绕陷阱与断线重连保活落地
stm32·单片机·嵌入式硬件·tcp/ip