RCC时钟详解

发布时间 2023-09-19 19:01:22作者: 一步一磕头的菜鸡

一. STM32时钟树

1.这里主要熟悉SYSCLK的时钟流程即可. (主要是配置好锁相环)

二. 核心寄存器分析

1.RCC_CR时钟控制寄存器

  • RCC_CR寄存器主要是控制时钟状态/使能标志位. 值得注意的是PLLON使能标志位, 后面配置锁相环部分需要先失能.

2.RCC_CFGR时钟配置寄存器

  • RCC_CFGR寄存器主要是负责配置时钟树种, 各个线路上时钟源的选择以及倍频/分频系数.

3.其他寄存器

  • RCC还有时钟中断寄存器, 复位/使能(APB1/APB2)寄存器, 备份区域控制和控制状态寄存器. 这些等后面接触到再做了解.

三. RCC外设驱动

1. 操作寄存器方式驱动

  • 这里以官方的代码作为参考, 从这里可以看出官方将SYSCLK配置为72M, 并将主要的总线上的时钟频率也配置为官方推荐的最大值. 所以外后外设使用无特殊要求, 不需要修改时钟.
static void SetSysClockTo72(void)
{
    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

    // ① 使能HSE,并等待HSE稳定
    RCC->CR |= ((uint32_t)RCC_CR_HSEON);

    // 等待HSE启动稳定,并做超时处理
    do {
        HSEStatus = RCC->CR & RCC_CR_HSERDY;
        StartUpCounter++;
    } while ((HSEStatus == 0)
        &&(StartUpCounter !=HSE_STARTUP_TIMEOUT));

    if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
        HSEStatus = (uint32_t)0x01;
    } else {
        HSEStatus = (uint32_t)0x00;
    }
    // HSE启动成功,则继续往下处理
    if (HSEStatus == (uint32_t)0x01) {

        //-----------------------------------------------------------
        // 使能FLASH 预存取缓冲区 */
        FLASH->ACR |= FLASH_ACR_PRFTBE;

        // SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2
        // 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,
        // 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了
        // 0:0 < SYSCLK <= 24M
        // 1:24< SYSCLK <= 48M
        // 2:48< SYSCLK <= 72M */
        FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
        FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
        //------------------------------------------------------------

        // ② 设置AHB、APB2、APB1预分频因子
        // HCLK = SYSCLK
        RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
        //PCLK2 = HCLK
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
        //PCLK1 = HCLK/2
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

        // ③ 设置PLL时钟来源,设置PLL倍频因子,PLLCLK = HSE * 9 = 72 MHz
        RCC->CFGR &= (uint32_t)((uint32_t)
                                ~(RCC_CFGR_PLLSRC
                                | RCC_CFGR_PLLXTPRE
                                | RCC_CFGR_PLLMULL));
        RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE
                                | RCC_CFGR_PLLMULL9);

        // ④ 使能 PLL
        RCC->CR |= RCC_CR_PLLON;

        // ⑤ 等待PLL稳定
        while ((RCC->CR & RCC_CR_PLLRDY) == 0) {
        }

        // ⑥ 选择PLL作为系统时钟来源
        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
        RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;

        // ⑦ 读取时钟切换状态位,确保PLLCLK被选为系统时钟
        while ((RCC->CFGR&(uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08){
        }
    } else {// 如果HSE启动失败,用户可以在这里添加错误代码出来
    }
}

1. 固件库方式驱动

  • 参考官方寄存器的驱动方式, 用固件库仿写一个可以传参修改SYSCLK时钟的函数. SYSCK要想修改最少要保障一下几点(如第一段代码所示), 详细规整代码如第二段所示.

第一段代码

#include "stm32f10x.h"

int main()
{
    ErrorStatus state;
    
    GPIO_InitTypeDef GPIO_InitStructures;
    
    //要想修改SYSCLK的代码, 必须要重置RCC寄存器, 失能PLL然后设置PLL锁相环时钟源和参数. 最后使能PLL并设置PLLCLK为SYSCLK时钟
    RCC_DeInit();
    RCC_HSEConfig(RCC_HSE_ON);
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_7);
    RCC_PLLCmd(ENABLE);
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStructures.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructures.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructures.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(GPIOA, &GPIO_InitStructures);
    
    
    RCC_MCOConfig(RCC_MCO_SYSCLK);
}

第二段规整版代码

void HSE_SetSysClock(uint32_t pllmul)
{
    __IO uint32_t StartUpCounter = 0, HSEStartUpStatus = 0;

    // 把RCC外设初始化成复位状态
    RCC_DeInit();

    //使能HSE,开启外部晶振,野火STM32F103系列开发板用的是8M
    RCC_HSEConfig(RCC_HSE_ON);

    // 等待 HSE 启动稳定
    HSEStartUpStatus = RCC_WaitForHSEStartUp();

    // 只有 HSE 稳定之后则继续往下执行
    if (HSEStartUpStatus == SUCCESS) {
//-----------------------------------------------------------------//
        // 这两句是操作FLASH闪存用到的,如果不操作FLASH,这两个注释掉也没影响
        // 使能FLASH 预存取缓冲区
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

        // SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2
        // 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,
        // 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了
        // 0:0 < SYSCLK <= 24M
        // 1:24< SYSCLK <= 48M
        // 2:48< SYSCLK <= 72M
        FLASH_SetLatency(FLASH_Latency_2);
//-----------------------------------------------------------------//

        // AHB预分频因子设置为1分频,HCLK = SYSCLK
        RCC_HCLKConfig(RCC_SYSCLK_Div1);

        // APB2预分频因子设置为1分频,PCLK2 = HCLK
        RCC_PCLK2Config(RCC_HCLK_Div1);

        // APB1预分频因子设置为1分频,PCLK1 = HCLK/2
        RCC_PCLK1Config(RCC_HCLK_Div2);

//-----------------设置各种频率主要就是在这里设置-------------------//
        // 设置PLL时钟来源为HSE,设置PLL倍频因子
        // PLLCLK = 8MHz * pllmul
        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);
//-------------------------------------------------------------//

        // 开启PLL
        RCC_PLLCmd(ENABLE);

        // 等待 PLL稳定
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
        }

        // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

        // 读取时钟切换状态位,确保PLLCLK被选为系统时钟
        while (RCC_GetSYSCLKSource() != 0x08) {
        }
    } else {
        // 如果HSE开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理
        // 当HSE开启失败或者故障的时候,单片机会自动把HSI设置为系统时钟,
        // HSI是内部的高速时钟,8MHZ
        while (1) {
        }
    }
}

四. RCC外设驱动总结

  • 无特殊需求, 直接使用使用配置好的时钟频率即可.如果需要修改部分外设, 例如APB1/APB2上面的外设时钟频率只需要调用固件库中的函数, 修改倍频/分频系数即可.(SYSCLK一般不做修改)