1、为什么要有时钟
时钟对单片机来说就像人和心脏的关系一样,单片机有了时钟,才能够执行指令。所以使用任何一个外设都必须打开相应的时钟。
简单来说时钟是单片机的脉搏,为单片机执行每条指令提供驱动。
2、STM32系统时钟框图
将这个框图分成两部分分析:
系统时钟SYSCLK 的左边就是设置系统时钟使用哪个时钟源;
系统时钟SYSCLK 的右边是系统时钟通过AHB预分频器,给总线上挂在的外设设置对应的时钟频率
3、时钟源
在 STM32 中,一共有 5 个时钟种类,分别是 HSI 、 HSE 、 LSI 、 LSE 、 PLL 。
①HSI 是高速内部时钟, RC 振荡器,频率为 8MHz ;
②HSE 是高速外部时钟,可接石英 / 陶瓷谐振器,或者接外部时钟源,频率范围是 4MHz – 16MHz ;
③LSI 是低速内部时钟, RC 振荡器,频率为 40KHz ;
④LSE 是低速外部时钟,接频率为 32.768KHz 的石英晶体;
⑤PLL 为锁相环倍频输出,严格的来说并不算一个独立的时钟源(因为PLL并不是自己产生的时钟源,而是通过其他三个时钟源倍频得到的时钟), PLL 的输入可以接 HSI/2 、 HSE 或者 HSE/2 。PLL倍频可选择为 2 – 16 倍,但是其输出频率最大不得超过 72MHz 。
4、系统时钟确定
三种不同的时钟源可被用来驱动系统时钟(SYSCLK):
● HSI振荡器时钟
● HSE振荡器时钟
● PLL时钟
默认时钟使用HSE,经过PLL倍频,得到72M系统时钟。
5、与时钟有关的总线
(1)AHB总线
系统时钟(SYSCLK)最大频率为 72MHz ,它通过 AHB 分频器分频后送给5 大模块使用:
①送给 AHB 总线、内核、内存和 DMA 使用的 HCLK 时钟;
②通过 8 分频后送给 Cortex 的系统定时器时钟STCLK;
③直接送给 Cortex 的空闲运行时钟 FCLK ;
④送给 APB1 分频器。 APB1分频器可以选择1、2、4、8、16分频,其输出一路供 APB1 外设使用( PCLK1 ,最大频率36MHz),另一路送给定时器 (Timer)2 、 3 、 4 倍频器使用。该倍频器根据PCLK1的分频值自动选择 1 或者 2 倍频,时钟输出供定时器 2 、 3 、 4 使用。
⑤送给 APB2 分频器。 APB2分频器可以选择1、2、4、8、16分频,其输出一路供 APB2 外设使用( PCLK2 ,最大频率 72MHz ),另外一路送给定时器 (Timer)1 倍频使用。该倍频器根据PCLK2的分频值自动选择1 或 2 倍频,时钟输出供定时器 1 使用。另外 APB2 分频器还有一路输出供 ADC 分频器使用,分频后送给 ADC 模块使用。 ADC分频器可选择为2、4、6、8分频。
(2)APB1,APB2总线
APB1从AHB上2分频,频率是36MHz,
APB2从AHB上1分频,频率是72MHz。
APB1和APB2总线上连接的模块:
6、MCO时钟输出
STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟SYSCLK。
这样可以把时钟信号输出供外部使用,也可以接在示波器上查看当前时钟频率是否与设定值吻合。
有些外部IC没有时钟不工作,但是加一个晶振需要成本,就可以考虑这个
7、程序
(1)默认时钟(使用HSE,经过PLL倍频,得到72M系统时钟)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
(2)HSE时钟
void HSE_SetSysClk( uint32_t RCC_PLLMul_x )
{
ErrorStatus HSEStatus;
// 把RCC 寄存器复位成复位值
RCC_DeInit();
// 使能 HSE
RCC_HSEConfig(RCC_HSE_ON);
HSEStatus = RCC_WaitForHSEStartUp();
if( HSEStatus == SUCCESS ) {
// 使能预取指
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency(FLASH_Latency_2);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
// 配置 PLLCLK = HSE * RCC_PLLMul_x
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);
// 使能PLL
RCC_PLLCmd(ENABLE);
// 等待PLL稳定
while( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET );
// 选择系统时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while( RCC_GetSYSCLKSource() != 0x08 );
} else {
/* 如果HSE 启动失败,用户可以在这里添加处理错误的代码 */
}
}
(3)HSI时钟
void HSI_SetSysClk( uint32_t RCC_PLLMul_x )
{
__IO uint32_t HSIStatus = 0;
// 把RCC 寄存器复位成复位值
RCC_DeInit();
// 使能 HSI
RCC_HSICmd(ENABLE);
HSIStatus = RCC->CR & RCC_CR_HSIRDY;
if( HSIStatus == RCC_CR_HSIRDY ) {
// 使能预取指
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency(FLASH_Latency_2);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
// 配置 PLLCLK = HSE * RCC_PLLMul_x
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_x);
// 使能PLL
RCC_PLLCmd(ENABLE);
// 等待PLL稳定
while( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET );
// 选择系统时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while( RCC_GetSYSCLKSource() != 0x08 );
} else {
/* 如果HSI 启动失败,用户可以在这里添加处理错误的代码 */
}
}
(4)MCO
/*
* 复用推挽模式开启MCO对应引脚PA8
**/
void MCO_GPIO_Config()
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
}
RCC_MCOConfig(RCC_MCO_SYSCLK); /* 参数为要输出的时钟 */
配置好之后示波器探头接到PA8 上即可看到时钟波形
8、程序分享
链接:https://pan.baidu.com/s/18mjutYzEUkMvhEcDCtFnsg
提取码:u68h