关于 RCC 配置系统时钟和外部输出 MCO

这张图 STM32F103 时钟树原理图,接下来我会在这张图里一小段一小段截取来分开讲解。

在这里插入图片描述

HSE 和 HSI 系统时钟输出

PLL 时钟源可以来源于两个,一个是 HSE ,另一个是 HSI 。
HSE 是如何作为时钟源呢?

在这里插入图片描述

1、这个是 HSE 高速外部时钟1的输入, 可以很清楚的看到外部接了 OUT 和 IN 两个引脚,外部有源或者无源晶振通过这两个引脚提供时钟信号(当是有源晶振时,时钟从 IN 进入, OUT 悬空, 当是无源晶振时,时钟从 IN 和 OUT 进入,并且要配谐振电容。)。
2、当确定 PLL 时钟来源时,可以选择不分频或者 2 分频,这个可以由 RCC 的 CFGR 寄存器的 PLLXTPRE 配置。
在这里插入图片描述

在这里插入图片描述

HSI 又是怎么作为时钟源呢?

在这里插入图片描述

HSI 是内部高速时钟信号,只能 2 分频来作为 PLL 时钟源,但是根据温度和环境情况频率会有漂移,所以一般不作为 PLL 时钟源。

在这里插入图片描述

1、通过上面所讲的配置 HSE 和 HSI 两个时钟作为 PLL 时钟的来源,可以通过 CFGR (寄存器上面已经讲到)的 PLLSRC 来配置来源。

在这里插入图片描述

/2、一般来说常用 8 M 频率,所以接下来就用 HSE 和 HSI 为 8M 来作为例子,在
这儿可以对时钟来源进行倍频,可以通过 CFGR 的 PLLMUL 来配置。

在这里插入图片描述

因为我们使用的是 8M 时钟,所以这儿经过倍频了之后最高可以达到 8M * 16 = 128M 的频率,但是官方推荐的是 72M 稳定时钟。

在这里插入图片描述

这儿我们可以看到,系统时钟的来源一共有 3 个,除了我们上述配置的 PLL 时钟,还可以直接使用 HSE 和 HSI 直接作为时钟来源,可以通过 CFGR 的 SW 来配置。

在这里插入图片描述

在这里插入图片描述

这儿可以看到,系统时钟会经过 AHB 预分频器,得到的叫做 APB 总线时钟(即 HCLK )。

在这里插入图片描述

我们可以通过设置 CFGR 的 HPRE 来设置分频器,大部分外设的时钟都是通过这个 HCLK 分频的得到,至于 AHB 总线上的时钟为多少,得等到我们使用该外设时才能用到,所以我们这儿只需要粗线条的配置 APB 的时钟就可以了。

在这里插入图片描述

这要注意,如果 AHB 的预分频器大于2,必须开启预缓冲器,我在闪存取读中并没有找到(不代表没有,只是博主没有看到,因为博主英文差,所以读英文版有些困难,如果有哪位大佬找到愿意分享,博主还望大佬不吝赐教,谢谢!(后面我会讲解用固件库开启这个。))

在这里插入图片描述

我们常用的 APB2 总线时钟(即 HCLK2 )经过高速到 APB2 预分频器获得,配置可以用 CFGR 的 PPRE2 。

在这里插入图片描述

HCLK2 是属于高速的总线时钟,高速的外设就挂载到这条总线上。

在这里插入图片描述

APB1 总线时钟(即 PCLK1 )经过低速到 APB 预分频器获得,配置可以用 CFGR 的 PPRE1 。

在这里插入图片描述

属于低速,最高只能达到 36M 低速外设就挂载在这个总线上。

以上就是关于 HSE 和 HSI 时钟的全部配置理论过程,接下来我会使用 STM32 的固件库来配置上面,配置原理与上面相同,理论是不是很枯燥,但是坚持一定很美丽,现在直接上代码讲解

1、以 HSE 作为时钟来源,经过PLL倍频来作为系统时钟。

//以HSE作为时钟来源,经过PLL倍频来作为系统时钟,这个是比较常用的。
void HSE_SetSysClock(uint32_t pllmul)    //定义一个函数,来代表这一个过程。
{
    __IO uint32_t HSEStartUpStatus = 0;    //__IO指的是静态变量,原型为volatile,代表允许除了程序员之外(比如硬件)来修改它的内容。
    
    RCC_DeInit();    //在stm32f10x_rcc.h里,是外设时钟初始化。
    RCC_HSEConfig();    //使能HSE,开启外部晶振,我的开发板使用的是8M。
    HSEStartUpStatus = RCC_WaitForHSEStartUp();    //等待HSE稳定,若是稳定了会返回SUCCES这个值。
    if (HSEStartUpStatus = SUCCESS)    //判断是否成功启动并且稳定。
    {
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);    //使能FLASH缓冲区。
//SYSCLK周期与闪存访问时间的比例设置,设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了。
// 0:0 < SYSCLK <= 24M
// 1:24< SYSCLK <= 48M
// 2:48< SYSCLK <= 72M
        FLASH_SetLatency(FLASH_Latency_2);
        RCC_HCLKConfig(RCC_SYSCLK_Div1);    //AHB预分频因子设置为1分频。
        RCC_PCLK2Config(RCC_HCLK_Div1);    //APB2预分频因子设置为1分频。
        RCC_PCLK1Config(RCC_HCLK_Div2);    //APB1预分频因子设置为1分频。
        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);    //设置时钟来源为HSE,倍频为8M*pllmul。
        RCC_PLLCmd(ENABLE);    //开启PLL。
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);    //等待PLL稳定,代码中相关寄存器作用会在HSE和HSI讲解完后详细讲解。
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);    //把PLL时钟切换成系统时钟。
        while (RCC_GetSYSCLKSource() != 0x08);    //读取时钟切换状态,确保PLLCLK被选为系统时钟。
    }
    else
    {
        //这儿是HSE开启失败,程序就会来这儿,博主是只做了一个死循环处理(因为博主只是学习阶段,并不是做项目阶段,所以并没有太在意这儿,但是个别情况下还是添加一下失败处理,例如重启/复位什么的。)
        while (1);
    }
}
//至此,HSE就配置好了。我们只需要调用HSE_SetSysClock函数就可以了。
//例如:
    HSE_SetSysClock(13);    //代表8*13=104M的频率,调整参数可以改变。

HSI 的配置与上面大同小异,所以我只在不同的地方设置。

void HSI_SetSysClock(uint32_t pllmul)
{
    __IO uint32_t HSIStartUpStatus = 0;

    RCC_DeInit();
    RCC_HSICmd(ENABLE);
    HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;    //等待HSI,上面使用的是函数,这儿直接使用的是寄存器,本质一样。
    if (HSIStartUpStatus  == RCC_CR_HSIRDY)    //因为使用的寄存器,所以值就不是RESET,RESET是函数的返回值。
    {
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
        FLASH_SetLatency(FLASH_Latency_2);
        RCC_HCLKConfig(RCC_SYSCLK_Div1); 
        RCC_PCLK2Config(RCC_HCLK_Div1); 
        RCC_PCLK1Config(RCC_HCLK_Div2);
        RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);    //设置时钟来源为HSI。
        RCC_PLLCmd(ENABLE);
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
        while (RCC_GetSYSCLKSource() != 0x08);
    }
    else
    {
        while (1);
    }
}

在这儿, HSE 和 HSI系统时钟配置就弄好了。

接下来,我们来看看关于 RCC 的寄存器的作用吧。

CR 寄存器,时钟控制寄存器。

在这里插入图片描述

在这里插入图片描述

CFGR 寄存器,时钟配置寄存器

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

CIR 寄存器,时钟中断寄存器

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

APB2RSTR 寄存器, APB2 外设复位寄存器

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

APB1RSTR 寄存器, APB1 外设复位寄存器

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

AHBEND 寄存器, AHB 外设使能寄存器

在这里插入图片描述

在这里插入图片描述

APB2END 寄存器, APB2 外设使能寄存器

在这里插入图片描述

在这里插入图片描述

APB1END 寄存器, APB1 外设使能寄存器

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

BDCR 寄存器,备份域控制寄存器

在这里插入图片描述

CSR 寄存器,控制 / 状态寄存器

在这里插入图片描述

在这里插入图片描述

以上,就是全部关于 RCC 的寄存器了。

接下来,就是关于 MCO 的讲解以及代码讲解。

MCO

什么是 MCO ?
为什么要配置 MCO ?

首先, MCO 是什么? MCO 就是由 STM32 内部向外部发出时钟信号。
为什么要配置 MCO ?这是因为在使用其他芯片的时候可能需要一个晶振来提供一个时钟信号,如果使用 MCO 来输出时钟信号,就可以在成本上减少一个晶振的使用。
其实 MCO 的配置也是使用 CFGR 的 MCO 配置,可以通过示波器检测是否配置正确。
在这里插入图片描述

这儿就可以很清楚的看到 MOC 的输出源。

在这里插入图片描述

这儿就可以配置输出源。

下面放代码实例:

//其实要把MCO作为外部输出只需要调用stm32f10x_rcc.h这个库函数就可以了。
    RCC_MCOConfig(RCC_MCO_SYSCLK);
//配置的参数可以参考下图。

在这里插入图片描述

到此为止,关于系统时钟配置和外部输出时钟已经弄好了,谢谢大家耐心观看,觉得博主写的不错的点赞关注一下再走吧,嘻嘻~