Loading

MCU时钟系统

文章结构如下

  • 1 时钟树

  • 2 时钟配置

  • 3 自定义系统时钟

时钟树

把 MCU 比作人体,那么时钟就相当于人的心脏,其重要不言而喻,它为单片机工作提供一个稳定的机器周期,从而使系统能够正常运行。

以 STM32F10x 系列为例,时钟树如下图所示

在 STM32 时钟系统中,有 5 个重要的时钟源,分别是 LSI, LSE, HSI, HSE, PLL。

按照时钟频率可分为高速时钟源和低速时钟源,HSI, HSE, PLL 是高速时钟源,LSI, LSE 是低速时钟源。

按照时钟来源可分为外部时钟源和内部时钟源,HSE, LSE 是外部时钟源,HSI, LSI, PLL 是内部时钟源。

(1)图标1 HSI 是内部高速时钟,RC振荡器,频率为8MHz,可作为系统时钟或PLL锁相环的输入

(2)图标2 HSE 是外部高速时钟,芯片的 23 和 24 引脚即为外部高速晶振管脚。可以外接一个频率范围为 4-16MHz 的时钟或者晶振,HSE 可以作为系统时钟和 PLL 锁相环输入,还可以经过 128 分频后输入给 RTC

(3)图标3 LSI 是内部低速时钟,RC 振荡器,频率大约为 40K,可工独立看门狗和 RTC 使用,并且独立看门狗只能使用 LSI 时钟

(4)图标4 LSE 是外部低速时钟,通常此管脚上外接一个 32.768KHz 的晶振

(5)图标5 PLL 是锁相环,用于倍频输出,因为开发板外部高速晶振也只有 8M,而此款芯片的最大时钟频率是 72M,就是通过锁相环来倍频的。图中 PLL 时钟输入源可选择为 HSI/2、HSE 或者 HSE/2,时钟源经过 2-16 倍频后输入给 PLLCLK,系统时钟可选择由 PLLCLK 提供

既然有时钟源,那必然也会有需要下游设备,那他们是怎么给其他外设和系统提供时钟的呢?

(A)MCO 是 STM32 的一个时钟输出 IO(PA8),可以选择一个时钟信号输出,可以选择 PLL 输出的 2 分频、HSI、HSE 或者系统时钟,用来给外部其他系统提供时钟源

(B)RTC 时钟,RTC 的时钟来源可以是内部低速的 LSI 时钟,外部低速 LSE 时钟,还可以是 HSE 128 分频后得到

(C)USB 时钟,串口需要一个频率为 48MHz 的时钟源,改时钟源只能从 PLL 输出端获取,可以选择 1.5 分频或者 1 分频,当需要使用 USB模块时,必须使能 PLL,并且 PLLCLK 时钟频率配置为 48MHz 或 72MHz

(D)SYSCLK 系统时钟,是绝大部分不见工作的时钟源,可由 HSI、HSE、PLLCLK 提供

(E)其他所有外设,从图中可以看出,所有外设的最终时钟来源都是 SYSCLK,SYSCLK 通过 AHB 分频器分频后送给各模块使用

APB1 上面链接的是低速外设,APB2 上面连接的是高速外设,大多数有关时钟输出部分都有一个使能控制,比如 AHB 总线、APB1 外设、APB2 外设、内核时钟等。当需要使用某个时钟的时候需要开启它的使能,否则将不工作。

时钟配置

时钟配置初始化

STM32 系统复位后首先进入 SystemInit() 函数进行时钟设置,然后再进入主函数,在system_stm32f10x.c 文件中可以找到 SystemInit(),函数的功能如下

使能 HSI 时钟,选择 HSI 作为系统时钟,设置完相关寄存器后换成 HSE作为系统时钟,接着调用 SetSysClock() 函数,设置系统时钟频率。

static void SetSysClock(void)
{
    #ifdef SYSCLK_FREQ_HSE
    	SetSysClockToHSE();
    #elif defined SYSCLK_FREQ_24MHz
    	SetSysClockTo24();
    #elif defined SYSCLK_FREQ_36MHz
    	SetSysClockTo36();
    #elif defined SYSCLK_FREQ_48MHz
    	SetSysClockTo48();
    #elif defined SYSCLK_FREQ_56MHz
    	SetSysClockTo56();
    #elif defined SYSCLK_FREQ_72MHz
    	SetSysClockTo72();
    #endif
}

在 system_stm32f10x.c 文件的开头就有对此宏定义,系统默认的宏定义是 72MHz,如下:

#define SYSCLK_FREQ_72MHz	72000000

修改此宏定义的值可以改变系统时钟频率。

SetSysClockTo72() 此函数的功能是将系统时钟 SYSCLK 设置为 72M,AHB 总线时钟设置为 72M,APB2 总线时钟设置为72M,APB1 总线时钟设置为 36M,PLL 时钟设置为 72M

时钟使能配置函数

要使用一个外设,必须先使能它的时钟,可以通过库函数进行使能,固件库把时钟相关的寄存器的使能配置都封装好,放在 xxx_rcc.c 和 xxx_rcc.h 中,可以将这些函数功能分为一下几类:

  • 外设时钟使能函数
  • 时钟源和和倍频因子配置函数
  • 外设复位函数
  • 获取时钟源配置函数

外设时钟使能函数

包含外设时钟使能和时钟源使能

void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);

上述 3 个函数分别使能 STM32 的 3 条总线,STM32 的外设都是挂接在 AHB 和 APB 总线上,所以使用外设时,需要使能对应外设所挂接的总线时钟,比如 GPIO 外设挂接在 APB2 总线上,如果需要使用 GPIO 外设,就需要先调用 RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState),具体哪个外设挂接在哪个总线上,需要查找对应芯片的手册

void RCC_HSICmd(FunctionalState NewState);
void RCC_LSICmd(FuncitonalState NewState);
void RCC_PLLCmd(FunctionalState NewState);
void RCC_RTCCLKCmd(FunctionalState NewState);

如果我们要使能 PLL 时钟,那么久需要调用 RCC_PLLCmd 函数,函数只有一个形参,ENABLE 表示使能,DISABLE 表示失能

时钟源和和倍频因子配置函数

用于选择相应的时钟源和配置时钟倍频因子,比如系统时钟,可以由 HSE、HSI、或则 PLLCLK 作为它的时钟源,具体选择哪个,就是通过时钟配置函数实现。如果设置 HSE 作为系统时钟源,那么可以调用如下函数:

RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);	//配置时钟源为 HSE

APB1 的时钟频率是 HCLK 的 2 分频,可以调用如下函数:

RCC_PCLK1Config(RCC_HCLK_Div2);	// 设置低速 APB1 时钟

外设复位函数

用于外设复位

void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

自定义系统时钟

通过修改锁相环中的倍系数值(2-16)可以改变系统的时钟频率,库函数中也有对时钟倍频因子配置的函数,如下:

void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul);

第一个参数是 PLL 时钟源选择,通常采用 HSE 作为 PLL 的时钟源,可以设置为 RCC_PLLSource_HSE_Div1/RCC_PLLSource_HSE_Div2,第二个参数就是倍频因子值(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);		// 设置低速 APB1 时钟(PCLK1)
        RCC_PCLK2Config(RCC_HCLK_Div1);		// 设置高速 APB2 时钟(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)
        while(RCC_GetSYSCLKSource()!=0x08);			// 返回用作系统时钟的时钟源,0x08:PLL 作为系统时钟
    }
}

div,pllm 两个参数分别对应锁相环的时钟源和倍频系数,只需要修改这两个参数即可修改系统时钟,无需修改函数内部程序。在未修改系统时钟时,系统初始化后的时钟时 72M,对应着此函数参数设置如下

RCC_HSE_Config(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);

如果需要设置系统时钟位 36M,可以使用如下参数

RCC_HSE_Config(RCC_PLLSource_HSE_Div2, RCC_PLLMul_9);

可以通过一个 LED 指示灯闪烁速度来反映系统时钟修改后的效果,主函数代码如下

int main()
{
    RCC_HSE_Config(RCC_PLLSource_HSE_DIV2, RCC_PLLMul_9); // 36M
    LED_Init();
    while(1)
    {
        GPIO_ResetBits(LED_PORT, GPIO_PIN_0); // 点亮 D1
        delay(6000000);
        GPIO_SetBits(LED_PORT, GPIO_Pin_0);
        delay(6000000);
    }
}
posted @ 2024-07-01 21:51  紫曜花  阅读(4)  评论(0编辑  收藏  举报