STM32F4 时钟树概述

STM32F4 相对于 STM32F1 来说,时钟部分复杂了很多, STM32F4 的时钟配置,我们提供两个函数: Sys_Clock_Set 和 Stm32_Clock_Init。其中 Sys_Clock_Set 是核心的系统时钟配置函
数,由 Stm32_Clock_Init 调用,实现对系统时钟的配置。外部程序,一般调用 Stm32_Clock_Init函数来配置时钟。 sys文件夹中

STM32F4 的时钟树图(非常重要)

 

STM32F4 时钟树概述

 

上图从左往右看,就是整个 STM32F4 的时钟走向。这里,我们挑选出 9 个重要的地方进行介绍(图 5.2.2.1 中标出的①~⑨)。
① 这是进入 PLL 之前的一个时钟分频系数(M),取值范围是: 2~63,一般取 8。注意,这个分频系数,对主 PLL 和 PLLI2S 都有效。
② 这是 STM32F4 的主 PLL,该部分控制 STM32F4 的主频率(PLLCLK)和 USB/SDIO/随机数发生器等外设的频率(PLL48CK)。其中, N 是主 PLL vco 的倍频系数,其取值
范围是: 64~432; P 是系统时钟的主 PLL 分频系数,其取值范围是: 2、 4、 6 和 8(仅 限这四个值); Q 是 USB/SDIO/随机数产生器等的主 PLL 分频系数,其取值范围是:
2~15; R 没用到。
③ 这是 STM32F4 I2S 部分的 PLL,该部分主要用于设置 STM32F4 I2S 内部输入时钟频率。其中, N 是用于 PLLI2S vco 的倍频系数,其取值范围是: 192~432; R 是 I2S 时钟的分
频系数,其取值范围是: 2~7; P 和 Q 没用到。
④ 这是 PLL 之后的系统主时钟(PLLCLK), STM32F4 的主频最高是 168Mhz,所以我们一般设置 PLLCLK 为 168Mhz(M=8,N=336,P=2),通过 SW 选择 SYSCLK=PLLCLK
即可得到 168Mhz 的系统运行频率。
⑤ 这是 PLL 之后的 USB/SDIO/随机数发生器时钟频率,由于 USB 必须是 48Mhz 才可以正常工作,所以这个频率一般设置为 48Mhz(M=8,N=336,Q=7)。
⑥ 是 I2S 的时钟,通过 I2SSRC 选择内部 PLLI2SCLK 还是外部 I2SCKIN 作为时钟。 探索者 STM32F4 开发板使用的是内部 PLLI2SCLK。
⑦ 这是 Cortex 系统定时器,也就是 SYSTICK 的时钟。上图清楚的表明 SYSTICK 的来源是 AHB 分频后再 8 分频(这个 8 分频是可以设置的,即 8 分频,或者不分频,一般使
用 8 分频), 我们一般设置 AHB 不分频,则 SYSTICK 的频率为: 168M/8=21Mhz。前面介绍的延时函数,就是基于 SYSTICK 来实现的。
⑧ 这里是 STM32F4 很多外设的时钟来源,即两个总线桥: APB1 和 APB2,其中 APB1是低速总线(最高 42Mhz), APB2 是高速总线(最高 84Mhz)。另外定时器部分,如
果所在总线(APB1/APB2)的分频系数为 1,那么就不倍频,如果不为 1(比如 2/4/8/16),那么就会 2 倍频(Fabpx*2)后,作为定时器时钟输入。
⑨ 这是 STM32F4 内部以太网 MAC 时钟的来源。对于 MII 接口来说,必须向外部 PHY芯片提供 25Mhz 的时钟,这个时钟,可以由 PHY 芯片外接晶振,或者使用 STM32F4
的 MCO 输出来提供。然后, PHY 芯片再给 STM32F4 提供 ETH_MII_TX_CLK 和ETH_MII_RX_CLK 时钟。对于 RMII 接口来说,外部必须提供 50Mhz 的时钟驱动 PHY
和 STM32F4 的 ETH_RMII_REF_CLK,这个 50Mhz 时钟可以来自 PHY、有源晶振或者 STM32F4 的 MCO。 我们的开发板使用的是 RMII 接口,使用 PHY 芯片提供 50Mhz
时钟驱动 STM32F4 的 ETH_RMII_REF_CLK。
关于时钟的详细介绍,在《STM32F4xx 中文参考手册》第 6.2 节(106~113 页)有详细介绍。有不明白的地方,可以对照手册仔细研究。 最后,提醒下大家, STM32F4 默认的情况下
(比如串口 IAP 时或未初始化时钟时),使用的是内部 16M 的 HSI 作为时钟的,所以不需要外部晶振也可以下载和运行代码的。
从上图可以看出 STM32F4 的时钟设计的比较复杂,各个时钟基本都是可控的,任何外设都有对应的时钟控制开关,这样的设计,对降低功耗是非常有用的,不用的外设不开启时钟,
就可以大大降低其功耗。
下面开始 Sys_Clock_Set 函数的介绍, 该函数用于配置 STM32F4 的时钟,包括系统主时钟、USB/SDIO/随机数发生器时钟、 APB1 和 APB2 时钟等。 该函数代码如下:

//时钟设置函数
//Fvco=Fs*(plln/pllm);
//Fsys=Fvco/pllp=Fs*(plln/(pllm*pllp));
//Fusb=Fvco/pllq=Fs*(plln/(pllm*pllq));
//Fvco:VCO 频率
//Fsys:系统时钟频率
//Fusb:USB,SDIO,RNG 等的时钟频率
//Fs:PLL 输入时钟频率,可以是 HSI,HSE 等.
//plln:主 PLL 倍频系数(PLL 倍频),取值范围:64~432.
//pllm:主 PLL 和音频 PLL 分频系数(PLL 之前的分频),取值范围:2~63.
//pllp:系统时钟的主 PLL 分频系数(PLL 之后的分频),取值范围:2,4,6,8.(仅限这 4 个值!)
//pllq:USB/SDIO/随机数产生器等的主 PLL 分频系数(PLL 之后的分频),取值范围:2~15.
//外部晶振为 8M 的时候,推荐值:plln=336,pllm=8,pllp=2,pllq=7.
//得到:Fvco=8*(336/8)=336Mhz
// Fsys=336/2=168Mhz
// Fusb=336/7=48Mhz
//返回值:0,成功;1,失败。
u8 Sys_Clock_Set(u32 plln,u32 pllm,u32 pllp,u32 pllq)
{
u16 retry=0;
u8 status=0;
RCC->CR|=1<<16; //HSE 开启
while(((RCC->CR&(1<<17))==0)&&(retry<0X1FFF))retry++;//等待 HSE RDY
if(retry==0X1FFF)status=1; //HSE 无法就绪
else
{
RCC->APB1ENR|=1<<28; //电源接口时钟使能
PWR->CR|=3<<14; //高性能模式,时钟可到 168Mhz
RCC->CFGR|=(0<<4)|(5<<10)|(4<<13);//HCLK 不分频;APB1 4 分频;APB2 2 分频.
RCC->CR&=~(1<<24); //关闭主 PLL
RCC->PLLCFGR=pllm|(plln<<6)|(((pllp>>1)-1)<<16)|(pllq<<24)|(1<<22);
//配置主 PLL,PLL 时钟源来自 HSE
RCC->CR|=1<<24; //打开主 PLL
while((RCC->CR&(1<<25))==0);//等待 PLL 准备好
FLASH->ACR|=1<<8; //指令预取使能.
FLASH->ACR|=1<<9; //指令 cache 使能.
FLASH->ACR|=1<<10; //数据 cache 使能.
FLASH->ACR|=5<<0; //5 个 CPU 等待周期.
RCC->CFGR&=~(3<<0); //清零
RCC->CFGR|=2<<0; //选择主 PLL 作为系统时钟
while((RCC->CFGR&(3<<2))!=(2<<2));//等待主 PLL 作为系统时钟成功.
}
return status;
}

在 Sys_Clock_Set 函数中,我们设置了 APB1 为 4 分频, APB2 为 2 分频, HCLK 不分频,同时选择 PLLCLK 作为系统时钟。该函数有 4 个参数,具体意义和计算方法,见函数前面的说
明。 一般我们推荐设置为: Sys_Clock_Set(336,8,2,7),即可设置 STM32F4 运行在 168Mhz 的频率下, APB1 为 42Mhz, APB2 为 84Mhz, USB/SDIO/随机数发生器时钟为 48Mhz。

 

 以上代码中, RCC 和 FLASH 都是 MDK 定义的一个结构体,包含 RCC/FLASH 相关的寄存器组。其寄存器名与《STM32F4xx 中文参考手册》 里面定义的寄存器名字是一摸一样的,所
以在你不明白某个寄存器干什么用的时候,可以到《STM32F4xx 中文参考手册》里面查找一下,你就可以迅速查到这个寄存器的作用以及每个位所代表的意思。 特别注意,由于 FLASH 速度
远远跟不上 CPU 的运行频率,所以这里我们设置了 FLASH 的等待周期为 5, 很明显, FLASH会大大拖慢程序的运行,不过 STM32F4 有自适实时存储器加速器(ART),通过这个加速器,可
以让 STM32F4 获得相当于 0 FLASH 等待周期的运行效果。关于 STM32F4 的 FLASH 以及 ART等的介绍,请大家参考《STM32F4xx 中文参考手册》第 3.3 节(59 页开始)。
接下来,我们再看下 Stm32_Clock_Init 函数,该函数代码如下:

//系统时钟初始化函数
//plln:主 PLL 倍频系数(PLL 倍频),取值范围:64~432.
//pllm:主 PLL 和音频 PLL 分频系数(PLL 之前的分频),取值范围:2~63.
//pllp:系统时钟的主 PLL 分频系数(PLL 之后的分频),取值范围:2,4,6,8.(仅限这 4 个值!)
//pllq:USB/SDIO/随机数产生器等的主 PLL 分频系数(PLL 之后的分频),取值范围:2~15.
void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq)
{
RCC->CR|=0x00000001; //设置 HISON,开启内部高速 RC 振荡
RCC->CFGR=0x00000000; //CFGR 清零
RCC->CR&=0xFEF6FFFF; //HSEON,CSSON,PLLON 清零
RCC->PLLCFGR=0x24003010; //PLLCFGR 恢复复位值
RCC->CR&=~(1<<18); //HSEBYP 清零,外部晶振不旁路
RCC->CIR=0x00000000; //禁止 RCC 时钟中断
Sys_Clock_Set(plln,pllm,pllp,pllq);//设置时钟
//配置向量表
#ifdef VECT_TAB_RAM
MY_NVIC_SetVectorTable(1<<29,0x0);
#else
MY_NVIC_SetVectorTable(0,0x0);
#endif
}

该函数主要进行了时钟配置前的一些设置工作,然后通过调用 Sys_Clock_Set 函数,实现对 STM32F4 的时钟配置。最后,根据代码运行的位置( FLASH or SRAM), 调用函数
MY_NVIC_SetVectorTable 进行中断向量表偏移设置。MY_NVIC_SetVectorTable 函数的代码如下:

//设置向量表偏移地址
//NVIC_VectTab:基址
//Offset:偏移量
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab,u32 Offset)
{
SCB->VTOR=NVIC_VectTab|(Offset&(u32)0xFFFFFE00);
//设置 NVIC 的向量表偏移寄存器,VTOR 低 9 位保留,即[8:0]保留。
}

该函数是用来配置中断向量表基址和偏移量,决定是在那个区域。当在 RAM 中调试代码 的时候,需要把中断向量表放到 RAM 里面, 这就需要通过这个函数来配置。关于向量表的详
细介绍请参考《Cortex M3 与 M4 权威指南》第 4.5.3 节(117 页)或者《Cortex M3 权威指南》第七章,第 113 页的向量表一章。 关于 SCB->VTOR 寄存器,请参考《STM32F3 与 F4 系列 Cortex
M4 内核编程手册》第 4.4.4 节(212 页),有详细描述。

 

 

 

上一篇:文件系统应用笔记之一:FatFS在STM32F4上的移植


下一篇:西门子 S7-200 以太网模块连接 WINCC步骤