时钟树:概念与编程详解 (铁头山羊)
目录
时钟树
时钟和时钟树概念
分频器 ,锁相环和复用器
树根
树干
树枝
练习题
时钟树编程
时钟树初始状态
时钟树编程接口
时钟树编程实现
配置Flash指令预取
片上外设开关与复位
时钟树
时钟和时钟树概念
-
时钟概念阐释
-
时钟本质:时钟是高低变化的方波信号,类似人的心跳,为单片机片上外设运行提供信号。
-
代码示例体现:如 LED 闪灯代码需开启 GPIO时钟,串口使用时设置波特率也涉及时钟信号,时钟频率快慢决定片上外设工作速度。
-
使用UART模块时,必须对UART模块进行波特率的设置,波特率是通过时钟信号,经过分频器分频之后,再/16,再次进行分频之后得到波特率,并且时钟信号为72MHZ,那么为什么为72MHZ,后面会讲到。
-
时钟树概念介绍
-
形象比喻:把单片机时钟系统比作人的血液循环系统,时钟树类似树的分叉结构,有一棵大树和一棵小树,主要研究大树。
-
结构对应:大树树根是时钟信号产生处类似人的心脏,树干类似主动脉,树枝类似血管分支,树叶对应单片机片上外设。
-
分频器 ,锁相环和复用器
-
时钟树零部件
-
分频器作用:对输入信号频率做除法,如固定二分频器,输入 8 兆赫兹,输出 4 兆赫兹;AHB 分频器分频系数可选,最大输入 72 兆赫兹,选不同系数有不同输出频率。
-
锁相环功能:对输入信号频率做乘法,倍频系数可选,如输入 8 兆赫兹,设置倍频系数乘以 9 可产生 72 兆赫兹频率。
-
复用器职责:从多路输入信号中选择一路作为输出,如锁相环输入信号前的复用器可从三路信号中选一路作为锁相环输入。
-
树根
-
时钟树树根解析
-
时钟源种类:单片机两棵时钟树有四个时钟源,分别是 LSI、LSE、HSI、HSE。
-
高低速划分:LS 代表低速,对应小树;HS 代表高速,对应大树。高速树时钟频率几十兆赫兹,低速树时钟频率几十 k 赫兹。
-
内外部分类:i 代表内部,LSI 是内部低速时钟源,HSI 是内部高速时钟源;e 代表外部,LSE 需通过 PC14 和 PC15 外接低速晶振,HSE 需通过 PD0 和 PD1 外接高速晶振。
-
外接原因:内部时钟源使用方便但精度不高,对精度有要求时需外接时钟源。
内部晶振精度不高的原因:
-
-
树干
-
时钟树树干分析
-
树干任务:从树根获取时钟,加工产生 SYSCLK(系统时钟)。
-
SYSCLK来源及特点:有三种来源,直接来自 HSI,精度低、频率固定为 8 兆赫兹;直接来自 HSE,精度高、频率与 HSE 相等;来自锁相环,方式灵活,可从 HSI 或 HSE 获取时钟,通过改变锁相环倍频系数产生不同频率信号,如 8 兆、20 兆、72 兆赫兹等。
-
-
树枝
-
时钟树树枝讲解
-
总线对应:单片机内部有三条总线,AHB(高级高速总线)、APB1(高速外设总线1)、APB2(高速外设总线 2),分别对应时钟树三根树枝,不同总线挂载不同片上外设。
-
开启时钟依据:开启片上外设时钟根据其挂载总线选择对应编程接口,如开启 USART1 或者 GPIO 用 RCC AHB1 总线,开启 I2C 用 RCC APB2 总线。
-
-
右面粗的树干代表的是AHB总线,左右两面两个比较细的树干代表APB1 和 APB2总线,绿色代表总线上挂载的片上外设。
树枝分频关系:每根树枝有分频器,AHB 分频器产生 HCLK,HCLK 经 APB2 分频器得到 PCLK2,经 APB1 分频器得到 PCLK1,分别供给对应片上外设。
练习题
-
树枝相关练习题
-
练习示例:假设 sysclk 频率,配置三个分频器使 HCLK、PCLK1、PCLK2 达到指定频率。
-
sysclk 为 8 兆赫兹,三个分频器系数都设为除以 1;
-
sysclk 为 72 兆赫兹,AHB 分频器设为除以 2,APB1 分频器设为除以 4,APB2 分频器设为除以 1;
-
sysclk 为 72 兆赫兹且要求达到最大频率,AHB 分频器设为除以 1,APB1 分频器设为除以 2,APB2 分频器设为除以 1。
-
-
-
时钟树编程
时钟树初始状态
-
状态定义:单片机上电或复位后未执行代码时,时钟树的状态为初始状态。
-
频率情况:初始状态下,sysclk 来自单片机内部高速时钟 HSI,频率为 8 兆赫兹;AHB、APB2、APB1 分频器系数都为 1,HCLK = CCLK = PCLK2 = PCLK1 = 8 兆赫兹。
-
实验验证:利用内核从 HCLK 获取时钟,频率决定代码执行速度,自制延迟函数编写闪灯程序,因系统初始化使时钟树频率改变导致闪灯速度异常。
-
这里使用for循环来代替延时函数,这里每个循环消耗10个时钟周期是大概值,6个时钟周期比较准确。
实验发现,板载LED的闪烁频率很快,明显不是1s一个周期,那是为什么呢?
原来是因为单片机复位之后会执行先systeminit,这个初始化会将芯片的性能发挥到最大,所以会帮用户选择最高频率。
所以我们再看main函数中的代码,初始的频率就不是8Mhz了,所以LED灯的闪烁频率会很快。
-
标准库启动代码
-
启动文件位置:在工程的 default 文件夹的 STARTUP 文件夹中,名为.s 的文件是启动文件。
-
执行流程:单板机从 reset handler 开始执行,先执行 system init 函数,再执行用户编写的 main 方法。
-
system init 作用:帮助用户配置时钟树,使其达到最高频率 72 兆赫兹,发挥芯片最佳性能。
-
代码修改:注释掉 reset handler 中执行 system init 的两行代码(注意用英文分号注释),重新编译下载代码后,闪灯速度改变,但仍不准确。
-
延迟调整:因 for 循环消耗 CPU 周期数不确定,将延迟次数改为 666,666 ,实现严格的 500 毫秒延迟,闪灯速度为一秒一次。
-
-
systemInit会将初始的时钟树(左面)配置为右面的时钟树。
时钟树编程接口
-
数量与对应关系:时钟树有 10 个编程接口,与时钟树各环节一一对应,理解相对容易。
-
命名含义:编程接口以 RCC 下划线开头,RCC 是 Reset And Clock Controller 的缩写,代表芯片内部模块,有控制时钟树和复位时钟树两个功能。
-
各接口作用:
-
RCC_HSE_Config 配置 HSE(高速外部时钟)开关;
-
RCC_HSICmd 控制 HSI(高速内部时钟)开关;
-
RCC_PLL_Config 配置锁相环参数;
-
RCC_PLL_Cmd 控制锁相环开关;
-
RCC_sysclk_config 选择系统时钟来源;
-
hclk_config、PCLK1_config 和 PCLK2_config 配置分频器分频系数;
-
RCC_get_flag_status 获取 RCC 标志位;
-
-
RCC_get_sysclk_source 获取 sysclk 当前来源。(因为切换来源需要时间,所以这个函数来获取是否切换成功)
-
-
时钟树编程实现
-
初始状态:启动文件中注释掉 system_init,main 方法执行时,sysclk 从 HSI 获取时钟,频率为 8 兆赫兹。
-
目标状态:从 HSE 获取时钟,经锁相环倍频,在 SYSCLK 上产生 72 兆赫兹时钟频率,HCLK、PCLK2 和 PCLK1 分别达到 72 兆赫兹、72 兆赫兹和 36 兆赫兹。
-
-
编写思路与步骤:分四步,先开启 HSE,再配置并启动锁相环,接着配置三个分频器分频系数,最后切换系统时钟来源。
-
具体代码编写:用 system_clock_init 函数封装初始化代码,按四步思路编写代码,各步骤使用相应编程接口实现功能并查询状态。
第一步:开启HSE
第二步:配置并开启锁相环(hase - locked loop,常缩写为PLL )
第三步:配置分频器系数
第四步:选择SYSCLk来源
配置Flash指令预取
缓冲区的定义与作用
Flash 模块(存储程序代码的非易失性存储器)的读取速度较慢,为解决内核(CPU)与 Flash 模块之间的速度不匹配问题,芯片设计了指令预取缓冲区:
-
位置:位于内核与 Flash 模块之间。
-
机制:提前将下一条待执行的指令从 Flash 中读取并缓存到缓冲区。
-
目的:减少内核等待 Flash 读取的时间,加快指令执行速度。
缓冲区的配置方法
-
开启接口:通过
Flash_prefetch_buffer_CMD
函数控制,参数为enable
时开启预取缓冲区。 -
配置限制:该接口只能在系统时钟(sysclk)频率小于等于 8 兆赫兹时调用(因高频时钟下 Flash 访问可能已通过其他机制优化,或需配合延迟设置)。所以要开启缓冲区要在配置时钟树之前进行开启,并且配置合适的访问延迟。
关键注意点
指令预取缓冲区是 Flash 模块优化指令读取效率的核心机制,其正确配置需结合系统时钟频率:
-
若 sysclk 超过 8 兆赫兹,需先通过
Flash_set_latency
配置 Flash 访问延迟(如 72 兆赫兹时需设置 2 个等待周期),确保 Flash 与内核的时序匹配。
总结:缓冲区(指令预取缓冲区)是 Flash 模块的性能优化组件,通过预取指令减少内核等待时间,其开启需在低系统时钟频率下完成,并需配合访问延迟设置以适应高频场景。
片上外设开关与复位
-
时钟开关:
-
通过 RCC_ahb_clock_CMD 开启或关闭 ahb 总线上片上外设时钟;
-
通过 RCC_APB2_periph_clock_CMD、RCC_APB1_periph_clock_CMD 开关相应总线上片上外设时钟,
-
举例说明了开启不同外设时钟的代码写法。
-
-
-
复位方法:对 APP2 总线上片上外设复位调用 RCC_APB2_periph_RESET_CMD;对 APP1 总线上片上外设复位调用 RCC_APB1_periph_RESET_CMD,以复位 iPhone C1 模块为例说明了代码写法及含义 。
-
//配置时钟树
void rcc_init()
{//还要开启缓冲区 因为 systeminit会开启缓冲区,把他注释掉了所以要自己开启FLASH_PrefetchBufferCmd(ENABLE);FLASH_SetLatency( FLASH_Latency_2 );//1.配置开启HSERCC_HSEConfig(RCC_HSE_ON);//等待HSE就绪while(RCC_GetFlagStatus( RCC_FLAG_HSERDY) == RESET);//2.配置并开启锁相环RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);//开启PLLRCC_PLLCmd(ENABLE);//等待PLL开启while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);//3.配置分频器系数RCC_HCLKConfig(RCC_SYSCLK_Div1);RCC_PCLK1Config(RCC_SYSCLK_Div2);RCC_PCLK2Config(RCC_SYSCLK_Div1);//4.选择SYSCLK来源RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );while( RCC_GetSYSCLKSource() != 0x08);
}