stm32 RCC时钟
时钟介绍
众所周知,要让单片机的各个设备(如ADC、定时器TIMx……)正常工作,必须要为其提供一定的时钟频率,因此,为了能进行更多、更灵活的时钟配置,stm32单片机提供了了多个时钟源,通过对时钟源进行简单配置(如倍频、分频),然后提供给各个需要使用的设备,使其正常工作。
时钟树
图中的梯形符号可以理解为一个多入单出的开关,即从多个输入中选择一个输出
时钟源
从上图可以看出,stm32有5个时钟源:HSI、HSE、LSI、LSE、PLL(其实只有四个,因为PLL时钟并不能算“源”,因为它也是来源于HSI或者HSE,从时钟树中也能看出来)
说明:时钟源的字母"H"代表高速时钟,"L"代表低速时钟,"I"代表内部时钟,"E"代表外部时钟,外部时钟都有2个引脚引出去,用来接外部晶振。
高速内部时钟(HSI):由内部RC振荡器产生,频率为8MHz,但不稳定。
高速外部时钟(HSE):以外部晶振作时钟源,晶振频率可取范围为4~16MHz,一般采用8MHz的晶振。
低速内部时钟(LSI):由内部RC振荡器产生,也主要提供给实时时钟模块,频率大约为40KHz。
低速外部时钟(LSE):以外部晶振作时钟源,主要提供给实时时钟模块,所以一般采用32.768KHz。
高速、低速时钟
高速时钟(HSE和HSI)提供给芯片主体的主时钟
低速时钟(LSE和LSI)只是提供给芯片中的RTC(实时时钟)及独立看门狗使用
从图中可以看出高速时钟也可以提供给RTC。
外部、内部时钟
内部时钟是在芯片内部RC振荡器产生的,起振较快,所以时钟在芯片刚上电的时候,默认使用内部高速时钟。
外部时钟信号是由外部的晶振输入的,在精度和稳定性上都有很大优势,所以上电之后我们再通过软件配置,转而采用外部时钟信号。
外部高速时钟HSE
- 从外部晶振(8MHz)接到OSC_OUT和OSC_IN引脚开始
- 经过第一个分频器PLLXTPRE(不分频或者分频——频率除以2)
- 遇到开关PLLSRC(PLL entry clock source),我们可以选择其输出,输出为外部高速时钟(HSE)或是内部高速时钟(HSI)。这里选择输出为HSE
- 接着遇到锁相环PLL,具有倍频作用,在这里我们可以输入倍频因子PLLMUL(可以是
2,3,4,...,16
),建议不要超过72MHz,但要是想超频,就得在这个寄存器上做手脚啦。
PLLCLK与SYSCLK时钟输出到外设
系统时钟SYSCLK经过一系列配置后提供给相应外设。
-
经过PLL的时钟称为PLLCLK。分频因子选1,即不分频;倍频因子我们设定为9倍频。也就是说,经过PLL之后,我们的时钟从原来HSE的8MHz变为72MHz的PLLCLK(8M/1 * 9=72M)
-
紧接着又遇到了一个开关SW,通过这个开关,可以切换SYSCLK的时钟源,可以选择为HSI、PLLCLK、HSE;经过这个开关之后就是STM32的系统时钟SYSCLK了。
-
我们选择PLLCLK时钟,所以得到SYSCLK为72MHz。PLLCLK在输入到SW前,还流向了USB预分频器,这个分频器输出为USB外设的时钟(USBCLK)
-
SYSCLK经过AHB预分频器,分频后再通过一些其它配置(如APB1,APB2分频等)输出到其它外设,给这些外设提供时钟
经过AHB预分频的时钟可以成为
HCLK时钟
,经过APB1预分频的时钟称为PCLK1时钟
,经过APB2预分频的时钟称为PCLK2时钟
-
GPIO外设挂载在APB2总线上,所以把APB2预分频器设置为不分频,就可以得到GPIO外设的时钟也等于HCLK,为72MHz了
总结:
SYSCLK:系统时钟,STM32大部分器件的时钟来源。主要由AHB预分频器分配到各个部件,一般最大配置为72MHz(虽然可以超频,但不建议,因为会不稳定)
HCLK:由AHB预分频器直接输出得到,它是高速总线AHB的时钟信号,提供给存储器,DMA及cortex内核,是cortex内核运行的时钟,cpu主频就是这个信号,它的大小与STM32运算速度,数据存取速度密切相关。
FCLK:同样由AHB预分频器输出得到,是内核的“自由运行时钟”。“自由”表现在它不来自时钟 HCLK,因此在HCLK时钟停止时 FCLK 也继续运行。它的存在,可以保证在处理器休眠时,也能够采样和到中断和跟踪休眠事件 ,它与HCLK互相同步。
PCLK1:外设时钟,由APB1预分频器输出得到,最大频率为36MHz,提供给挂载在APB1总线上的外设
PCLK2:外设时钟,由APB2预分频器输出得到,最大72MHz,提供给挂载在APB2总线上的外设如GPIO
folding blue,查看APBx总线挂载的外设
【在头文件 stm32f10x_rcc.h
头文件中指出了各个外设挂载在哪个总线上】
/** @defgroup APB2_peripheral
* @{
*/
#define RCC_APB2Periph_AFIO ((uint32_t)0x00000001)
#define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008)
#define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010)
#define RCC_APB2Periph_GPIOD ((uint32_t)0x00000020)
#define RCC_APB2Periph_GPIOE ((uint32_t)0x00000040)
#define RCC_APB2Periph_GPIOF ((uint32_t)0x00000080)
#define RCC_APB2Periph_GPIOG ((uint32_t)0x00000100)
#define RCC_APB2Periph_ADC1 ((uint32_t)0x00000200)
#define RCC_APB2Periph_ADC2 ((uint32_t)0x00000400)
#define RCC_APB2Periph_TIM1 ((uint32_t)0x00000800)
#define RCC_APB2Periph_SPI1 ((uint32_t)0x00001000)
#define RCC_APB2Periph_TIM8 ((uint32_t)0x00002000)
#define RCC_APB2Periph_USART1 ((uint32_t)0x00004000)
#define RCC_APB2Periph_ADC3 ((uint32_t)0x00008000)
#define RCC_APB2Periph_TIM15 ((uint32_t)0x00010000)
#define RCC_APB2Periph_TIM16 ((uint32_t)0x00020000)
#define RCC_APB2Periph_TIM17 ((uint32_t)0x00040000)
#define RCC_APB2Periph_TIM9 ((uint32_t)0x00080000)
#define RCC_APB2Periph_TIM10 ((uint32_t)0x00100000)
#define RCC_APB2Periph_TIM11 ((uint32_t)0x00200000)
#define IS_RCC_APB2_PERIPH(PERIPH) ((((PERIPH) & 0xFFC00002) == 0x00) && ((PERIPH) != 0x00))
/**
* @}
*/
/** @defgroup APB1_peripheral
* @{
*/
#define RCC_APB1Periph_TIM2 ((uint32_t)0x00000001)
#define RCC_APB1Periph_TIM3 ((uint32_t)0x00000002)
#define RCC_APB1Periph_TIM4 ((uint32_t)0x00000004)
#define RCC_APB1Periph_TIM5 ((uint32_t)0x00000008)
#define RCC_APB1Periph_TIM6 ((uint32_t)0x00000010)
#define RCC_APB1Periph_TIM7 ((uint32_t)0x00000020)
#define RCC_APB1Periph_TIM12 ((uint32_t)0x00000040)
#define RCC_APB1Periph_TIM13 ((uint32_t)0x00000080)
#define RCC_APB1Periph_TIM14 ((uint32_t)0x00000100)
#define RCC_APB1Periph_WWDG ((uint32_t)0x00000800)
#define RCC_APB1Periph_SPI2 ((uint32_t)0x00004000)
#define RCC_APB1Periph_SPI3 ((uint32_t)0x00008000)
#define RCC_APB1Periph_USART2 ((uint32_t)0x00020000)
#define RCC_APB1Periph_USART3 ((uint32_t)0x00040000)
#define RCC_APB1Periph_UART4 ((uint32_t)0x00080000)
#define RCC_APB1Periph_UART5 ((uint32_t)0x00100000)
#define RCC_APB1Periph_I2C1 ((uint32_t)0x00200000)
#define RCC_APB1Periph_I2C2 ((uint32_t)0x00400000)
#define RCC_APB1Periph_USB ((uint32_t)0x00800000)
#define RCC_APB1Periph_CAN1 ((uint32_t)0x02000000)
#define RCC_APB1Periph_CAN2 ((uint32_t)0x04000000)
#define RCC_APB1Periph_BKP ((uint32_t)0x08000000)
#define RCC_APB1Periph_PWR ((uint32_t)0x10000000)
#define RCC_APB1Periph_DAC ((uint32_t)0x20000000)
#define RCC_APB1Periph_CEC ((uint32_t)0x40000000)
时钟配置
系统初始化函数
系统在复位后,会首先调用系统初始化函数SystemInit()
进行初始化配置,包括一些时钟初始化配置等,之后才会调用 main 函数进行执行。
在执行 SystemInit()
函数后,配置的时钟如下所述:
SYSCLK(系统时钟) = 72MHZ
AHB总线时钟HCLK = 72MHZ
APB1总线时钟PCLK1 = 36MHZ
APB2总线时钟PCLK2 = 72MHZ
PLL主时钟PLLCLK = 72MHZ
(PLL2时钟 = 40MHZ)
系统时钟配置
配置顺序 HSE->PLL->SYSCLK
void RCC_HSE_Config(u32 div,u32 pllm) //自定义系统时间(可以修改时钟)
{
RCC_DeInit(); //将外设RCC寄存器重设为缺省值
RCC_HSEConfig(RCC_HSE_ON);//设置外部高速晶振(HSE)
if(RCC_WaitForHSEStartUp()==SUCCESS) //等待HSE起振
{
RCC_HCLKConfig(RCC_SYSCLK_Div1);//设置AHB时钟预分频为1(HCLK)
RCC_PCLK1Config(RCC_HCLK_Div2);//设置低速AHB时钟(PCLK1)
RCC_PCLK2Config(RCC_HCLK_Div1);//设置高速AHB时钟(PCLK2)
RCC_PLLConfig(div,pllm);//设置PLL时钟源及倍频系数
RCC_PLLCmd(ENABLE); //使能PLL时钟源
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);//检查指定的RCC标志位设置与否,PLL就绪
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//设置系统时钟(SYSCLK)的时钟源为为PLL时钟
while(RCC_GetSYSCLKSource()!=0x08);//返回用作系统时钟的时钟源,0x08表示时钟源为PLL时钟
}
}
RCC_PLLConfig(div,pllm)
就是配置从 HSE->PLL的过程中分频、倍频因子的值,如下图:
示例程序:
#include "stm32f10x.h"
#include "led.h"
void delay(u32 i){
while(i--);
}
/*******************************************************************************
* 函 数 名 : RCC_HSE_Config
* 函数功能 : 自定义系统时钟,可以通过修改PLL时钟源和倍频系数实现时钟调整
* 输 入 : div:RCC_PLLSource_HSE_Div1/RCC_PLLSource_HSE_Div2
pllm:RCC_PLLMul_2-RCC_PLLMul_16
* 输 出 : 无
*******************************************************************************/
void RCC_HSE_Config(u32 div,u32 pllm) //自定义系统时间(可以修改时钟)
{
RCC_DeInit(); //将外设RCC寄存器重设为缺省值
RCC_HSEConfig(RCC_HSE_ON);//设置外部高速晶振(HSE)
if(RCC_WaitForHSEStartUp()==SUCCESS) //等待HSE起振
{
RCC_HCLKConfig(RCC_SYSCLK_Div1);//设置AHB时钟(HCLK)
RCC_PCLK1Config(RCC_HCLK_Div2);//设置低速AHB时钟(PCLK1)
RCC_PCLK2Config(RCC_HCLK_Div1);//设置高速AHB时钟(PCLK2)
RCC_PLLConfig(div,pllm);//设置PLL时钟源及倍频系数
RCC_PLLCmd(ENABLE); //使能PLL时钟源
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);//检查指定的RCC标志位设置与否,PLL就绪
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//设置系统时钟(SYSCLK)的时钟源为为PLL时钟
while(RCC_GetSYSCLKSource()!=0x08);//返回用作系统时钟的时钟源,0x08表示时钟源为PLL时钟
}
}
int main()
{
RCC_HSE_Config(RCC_PLLSource_HSE_Div2,RCC_PLLMul_9); // 8M / 2 * 9 = 36M
LED_Init();
while(1)
{
GPIO_ResetBits(LED1_PORT,LED1_PIN);//点亮LED1
delay(6000000);
GPIO_SetBits(LED1_PORT,LED1_PIN);
delay(6000000);
}
}
本文来自博客园,作者:aJream,转载请记得标明出处:https://www.cnblogs.com/ajream/articles/16079861.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人