STM32时钟树分析

对于广大初次接触 STM32 的读者朋友(甚至是初次接触 ARM 器件的读者朋友)来说,在熟悉了开发环境的使用之后,往往“栽倒”在同一个问题上。这问题有个关键字叫:时钟树。

众所周知,微控制器(处理器)的运行必须要依赖周期性的时钟脉冲来驱动——往往由
一个外部晶体振荡器提供时钟输入为始,最终转换为多个外部设备的周期性运作为末,这种
时钟“能量”扩散流动的路径,犹如大树的养分通过主干流向各个分支,因此常称之为“时钟树”。在一些传统的低端 8 位单片机诸如 51, AVR, PIC 等单片机,其也具备自身的一个时钟树系统,但其中的绝大部分是不受用户控制的,亦即在单片机上电后,时钟树就固定在某种不可更改的状态(假设单片机处于正常工作的状态)。比如 51 单片机使用典型的 12MHz晶振作为时钟源,则外设如 IO口、定时器、串口等设备的驱动时钟速率便已经是固定的,用户无法将此时钟速率更改,除非更换晶振。

而 STM32 微控制器的时钟树则是可配置的,其时钟输入源与最终达到外设处的时钟速
率不再有固定的关系,本文将来详细解析 STM32 微控制器的时钟树。图 1 是 STM32 微控制
器的时钟树,表 1 是图中各个标号所表示的部件。

image

image
image

在认识这颗时钟树之前,首先要明确“主干”和最终的“分支”。假设使用外部 8MHz
晶振作为 STM32 的时钟输入源(这也是最常见的一种做法),则这个 8MHz 便是“主干”,
而“分支”很显然是最终的外部设备比如通用输入输出设备( GPIO)。这样可以轻易找出第
一条时钟的“脉络”:

③ —— ⑤ —— ⑦ —— ㉑ —— ⑧ —— ⑨ —— ⑪ —— ⑬

对此条时钟路径做如下解析:
- 对于 ③ ,首先是外部的 3-25MHz(前文已假设为 8MHz)输入;
- 对于 ⑤ ,通过 PLL 选择位预先选择后续 PLL 分支的输入时钟( 假设选择外部晶振);
- 对于 ⑦ ,设置外部晶振的分频数(假设 1 分频);
- 对于 ㉑ , 选择 PLL 倍频的时钟源(假设选择经过分频后的外部晶振时钟);
- 对于 ⑧ , 设置 PLL 倍频数(假设 9 倍频);
- 对于 ⑨ , 选择系统时钟源(假设选择经过 PLL 倍频所输出的时钟);
- 对于 ⑪ , 设置 AHB 总线分频数(假设 1 分频);
- 对于 ⑬ , 时钟到达 AHB 总线;

在上一章节中所介绍的 GPIO 外设属于 APB2 设备, 即 GPIO 的时钟来源于 APB2 总线,
同样在图 1 中也可以寻获 GPIO 外设的时钟轨迹:

③ —— ⑤ —— ⑦ —— ㉑ —— ⑧ —— ⑨ —— ⑪ —— ⑮ —— ⑯

  • 对于 ③ ,首先是外部的 3-25MHz(前文已假设为 8MHz)输入;
  • 对于 ⑤ , 通过 PLL 选择位预先选择后续 PLL 分支的输入时钟( 假设选择外部晶振);
  • 对于 ⑦ ,设置外部晶振的分频数(假设 1 分频);
  • 对于 ㉑ , 选择 PLL 倍频的时钟源(假设选择经过分频后的外部晶振时钟);
  • 对于 ⑧ , 设置 PLL 倍频数(假设 9 倍频);
  • 对于 ⑨ , 选择系统时钟源(假设选择经过 PLL 倍频所输出的时钟);
  • 对于 ⑪ ,设置 AHB 总线分频数(假设 1 分频);
  • 对于 ⑮ ,设置 APB2 总线分频数(假设 1 分频);
  • 对于 ⑯ , 时钟到达 APB2 总线;

现在来计算一下 GPIO 设备的最大驱动时钟速率( 各个条件已在上述要点中假设):

1) 由 ③ 所知晶振输入为 8MHz,由 ⑤ —— ㉑ 知 PLL 的时钟源为经过分频后的外部晶振时钟,并且此分频数为 1 分频,因此首先得出 PLL 的时钟源为: 8MHz / 1 = 8MHz。
2) 由 ⑧ 、 ⑨ 知 PLL 倍频数为 9,且将 PLL 倍频后的时钟输出选择为系统时钟,则得出系统时钟为 8MHz * 9 = 72MHz。
3) 时钟到达 AHB 预分频器,由 ⑪ 知时钟经过 AHB 预分频器之后的速率仍为 72MHz。
4) 时钟到达 APB2 预分频器,由 ⑮ 经过 APB2 预分频器后速率仍为 72MHz。
5) 时钟到达 APB2 总线外设。

因此 STM32 的 APB2 总线外设,所能达到的最大速率为 72MHz。依据以上方法读者可以搜寻出 APB1 总线外设时钟、 RTC 外设时钟、独立看门狗等外设时钟的来龙去脉。接下来
从程序的角度分析时钟树的设置,程序清单如下:

void RCC_Configuration(void)
{
    ErrorStatus HSEStartUpStatus; ( 1)
    RCC_DeInit(); ( 2)
    RCC_HSEConfig(RCC_HSE_ON); ( 3)
    HSEStartUpStatus = RCC_WaitForHSEStartUp(); ( 4if(HSEStartUpStatus == SUCCESS) ( 5)
    {
        RCC_HCLKConfig(RCC_SYSCLK_Div1); ( 6)
        RCC_PCLK2Config(RCC_HCLK_Div1); ( 7)
        RCC_PCLK1Config(RCC_HCLK_Div2); ( 8)
        FLASH_SetLatency(FLASH_Latency_2); ( 9)
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); ( 10)
        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); ( 11)
        RCC_PLLCmd(ENABLE); ( 12while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); ( 13)
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); ( 14while(RCC_GetSYSCLKSource() != 0x08); ( 15)
    }
}

以上是 ST 官方所提供的 STM32 时钟树配置函数,读者首先要知道 3 点
- ST 所提供的库函数在函数和变量命名上有非常良好的规范性和易读性(虽然有点
冗长),即便没有注释,也可从函数名和变量名来大致判断该函数或变量所包含的
意义。
- 其次,读者应从上图区分出各个总线和对应的时钟:其中 PLLCLK 表示 PLL 锁相环
的输出时钟, SYSCLK 表示系统时钟, HCLK 表示 AHB 总线的时钟, PCLK1 表示 APB1
总线的时钟, PCLK2 则表示 APB2 总线的时钟。
- (9)(10)两句代码的作用是设置 STM32 内部 FLASH 的等待周期。 做如下解释: STM32
的内部用户 FLASH 用以存储代码指令供 CPU 存取以执行, STM32 的 CPU 的最大速
率已知为 72MHz,但 FLASH 无法达到这么高的速度,因此要在 CPU 存取 FLASH 的
过程中插入所谓的“等待周期”,显然 CPU 速度越快,所要插入的等待周期个数越
多,原则是

  • 当 CPU 速率为 0 ~ 24MHz 时,不需要插入等待周期,即等到周期个数为 0;
  • 当 CPU 速率为 24 ~ 48MHz 时,插入 1 个等待周期;
  • 当 CPU 速率为 48MHz ~ 72MHz 时,插入 2 个等待周期;

有以上三点准备之后, 开始解析这段程序:
1. 定义一个 ErrorStatus 类型的变量 HSEStartUpStatus;
2. 将时钟树复位至默认设置;
3. 开启 HSE 晶振;
4. 等待 HSE 晶振起振稳定,并将起振结果保存至 HSEStartUpStatus 变量中;
5. 判断 HSE 晶振是否起振成功(假设成功了,进入 if 内部);
6. 设置 HCLK 时钟为 SYSCLK 的 1 分频;
7. 设置 PLCK2 时钟为 SYSCLK 的 1 分频;
8. 设置 PLCK1 时钟为 SYSCLK 的 2 分频;
9. 选择 PLL 输入源为 HSE 时钟经过 1 分频,并进行 9 倍频;
12. 使能 PLL 输出;
13. 等待 PLL 输出稳定;
14. 选择系统时钟源为 PLL 输出;
15. 等待系统时钟稳定;

上述代码中对时钟树的配置顺序为( 对应图中标号) :

③ —— ⑪ —— ⑭ —— ⑮ —— ⑦ —— ㉑ —— ⑧ —— ⑨

通过对比发现,程序中对时钟树的配置顺序并不是依次从图中由左到右、由上到下配置的,
这是为什么呢?事实上这个问题相信大部分读者都可以自己解释:电子设计世界的思维和操
作方式, 其顺序往往和日常生活是不一样的,比如人们经常先给电视机连接电源,再打开电
视机开关;先把大水管的总闸打开,再打开小水龙头; 总而言之是一种由“主” 到“次” 的
顺序。 转移到最常见的 51 单片机的开发平台, 开发人员往往先把定时器的分频数,重载值
等参数配置好,最后才启动定时器计数;先把各个外设的中断打开,最后再打开总中断;这和人们的生活习惯其实恰好相反,是一种先“ 次” 后“ 主” 的顺序。

至此,理解 STM32 的时钟树就是轻而易举的事情了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值