目录
1.时钟源
(1)可以使用三种不同的时钟源来驱动系统时钟 (SYSCLK),CPU 运行的频率为 168MHz:
● HSI 振荡器时钟(16MHz),也就是高速内部时钟,一般来说很少用,因为精度没有外部高速时钟那么高。
● HSE 振荡器时钟,也就是高速外部时钟, 本开发板 8MHz。
● 主 PLL (PLL) 时钟
(2)器件具有以下两个次级时钟源:
● 32 kHz 低速内部 RC (LSI RC),该 RC 用于驱动独立看门狗,也可选择提供给 RTC 用于停机/待机模式下的自动唤醒。
● 32.768 kHz 低速外部晶振(LSE 晶振),用于驱动 RTC 时钟 (RTCCLK),对于每个时钟源来说,在未使用时都可单独打开或者关闭,以降低功耗。
2.附图说明
STM32f407的时钟体系十分的复杂,时钟树也非常庞大,对于初学者来说容易看的眼花缭乱,所以,用图形的形式能更好地帮助理解,弄清楚时钟体系的结构,时钟体系也是后面内容的基础。
3.倍频公式
如果不修改PLLM与HSE的值的话,代入倍频公式后会发现PLL输出频率不是168MHz。所以,一开始就要把PLLM与HSE的值都修改为8MHz,这样才不会影响后面定时器等内容的学习(比如定时器定时时间不准)。
注意:不同的芯片倍频公式是不一样的,需要查询对应的官方手册才能确定!
4.切换时钟源
在特殊的应用场景,为了达到最高的能效比,没有必要使用到PLL,可将HSE、HSI作为系统时钟源。例如,在智能手表锁屏的情况下,如果使用PLL配置过后输出的频率会造成过多的功耗,降低自身的续航能力;同时要维持计步与测量心率功能。因此,PLL在锁屏下的应用场景并不合适,在保证功能实现的前提下,尽可能降低功耗,可以切换频率更低的时钟源提供给系统时钟。
1.选择PLL作为系统时钟源
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); RCC->CFGR |= RCC_CFGR_SW_PLL;
2.选择HSI作为系统时钟源
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); RCC->CFGR |= RCC_CFGR_SW_HSI;
3.选择HSE作为系统时钟源
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); RCC->CFGR |= RCC_CFGR_SW_HSE;
5.代码验证
以上一篇文章的思考题流水灯的代码为例,加入切换时钟源的代码,观察LED灯闪烁的频率变化。
#include "stm32f4xx.h"
static GPIO_InitTypeDef GPIO_InitStruct;
void delay(void)//粗延时函数,后面学到定时器后,会用定时器进行精确延时
{
uint32_t i=0xA00000;
while(i--);
}
int main()
{
uint32_t b=0;
//打开端口F的硬件时钟,就是为端口F供电
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
//配置引脚
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Speed=GPIO_High_Speed;
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_Init(GPIOF,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_14;
GPIO_Init(GPIOE,&GPIO_InitStruct);
//一开始所有灯全灭
GPIO_WriteBit(GPIOF,GPIO_Pin_9,Bit_SET);
GPIO_WriteBit(GPIOF,GPIO_Pin_10,Bit_SET);
GPIO_WriteBit(GPIOE,GPIO_Pin_13,Bit_SET);
GPIO_WriteBit(GPIOE,GPIO_Pin_14,Bit_SET);
while(1)
{
b=!b;
if(b)
{
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
}
else
{
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_HSI;
}
//点亮D1
GPIO_WriteBit(GPIOF,GPIO_Pin_9,Bit_RESET);
delay();
//点亮D2
GPIO_WriteBit(GPIOF,GPIO_Pin_10,Bit_RESET);
delay();
//点亮D3
GPIO_WriteBit(GPIOE,GPIO_Pin_13,Bit_RESET);
delay();
//点亮D4
GPIO_WriteBit(GPIOE,GPIO_Pin_14,Bit_RESET);
delay();
//熄灭所有灯
GPIO_WriteBit(GPIOF,GPIO_Pin_9,Bit_SET);
GPIO_WriteBit(GPIOF,GPIO_Pin_10,Bit_SET);
GPIO_WriteBit(GPIOE,GPIO_Pin_13,Bit_SET);
GPIO_WriteBit(GPIOE,GPIO_Pin_14,Bit_SET);
delay();
}
}
然后,还可以直接用流水灯的代码,不切换时钟源,只改变PLLN的值,也能改变LED灯的闪烁频率。