首页 > 其他分享 >STM32 - 时钟系统详解

STM32 - 时钟系统详解

时间:2023-01-08 16:45:30浏览次数:38  
标签:PLL CFGR FLASH STM32 详解 CR 时钟 RCC

0. 前言

0.1 什么是时钟

时钟是由电路产生的具有周期性的脉冲信号,相当于单片机的心脏,给单片机提桶一个统一的信号

要想使用单片机的外设必须开启相应的时钟,

0.2 时钟对单片机作用

驱动外设的本质是操作寄存器,而寄存器是由D触发器构成,而D触发器需要时钟才能改写值,所以要想操作寄存器必须开启对应外设的时钟。

对CPU来说,假设CPU在一个时钟周期 (时钟等于 1/f (频率的倒数) 内执行一条指令(二进制代码)
若时钟频率越高,则时钟周期更短,则在相同的时间CPU能够执行更多的指令,CPU的运行速度更快

0.3 为什么要有时钟树

STM32时钟系统主要的目的就是给相对独立的外设模块提供时钟

为了降低整个芯片的功耗,所有外设时钟默认都是关闭状态(disable);当我们使用某个外设就要开启这个外设的时钟(enable)

不同外设需要的时钟频率不同,没必要所有外设都用高速时钟造成浪费,而且有些外设也接受不了这么高的频率,这也是为什么STM32有四个时钟源(HSE、 LSE、HSI、LSI)的原因,就是为了兼容不同速度的外设

1. 时钟树

以下是 STM32F40x 的时钟系统框图
image

在STM32中,有5个时钟源:

① LSI(Low Speed External Clock, 低速内部时钟):RC振荡器,频率为32KHz。独立看门狗的时钟源只能是LSI,同时LSI还可以做RTC的时钟源。
② LSE(Low Speed Internal Clock, 低速外部时钟):接频率为32.768KHz的石英晶体,LSE主要是RTC的时钟源。
③ HSI(High Speed Internal Clock, 高速内部时钟):RC振荡器,频率为16MHz,精度不高
④ HSE(High Speed External Clock, 高速外部时钟):可接石英/陶瓷谐振器,或外接时钟源,频率范围是2MHz~16MHz。

⑤ PLL(Phase Locked Loop, 锁相环倍频输出):理论上不能算是时钟源,只是接收时钟源后对其进行分/倍频,分/倍频 倍数可软件调节

Notice: 以上频率仅针对STM32F40x,其它的以数据手册为准

1)系统时钟 (SYSCLK) 选择

在系统复位后,默认系统时钟为 HSI。在直接使用 HSI 或者通过 PLL 使用时钟源来作为系统时钟时,该时钟源无法停止。
只有在目标时钟源已就绪时(时钟在启动延迟或 PLL 锁相后稳定时),才可从一个时钟源切换到另一个。如果选择尚未就绪的时钟源,则切换在该时钟源就绪时才会进行。RCC 时钟 
控制寄存器 (RCC_CR) 中的状态位指示哪个(些)时钟已就绪,以及当前哪个时钟正充当系统时钟。

2)时钟输出功能

共有两个微控制器时钟输出 (MCO) 引脚:

● MCO1

用户可通过可配置的预分配器(从 1 到 5)向 MCO1 引脚 (PA8) 输出四个不同的时钟源:

— HSI 时钟
— LSE 时钟
— HSE 时钟
— PLL 时钟

所需的时钟源通过 RCC 时钟配置寄存器 (RCC_CFGR) 中的 MCO1PRE[2:0] 和 MCO1[1:0]位选择。

● MCO2
用户可通过可配置的预分配器(从 1 到 5)向 MCO2 引脚 (PC9) 输出四个不同的时钟源:

— HSE 时钟
— PLL 时钟
— 系统时钟 (SYSCLK)
— PLLI2S 时钟

所需的时钟源通过 RCC 时钟配置寄存器 (RCC_CFGR) 中的 MCO2PRE[2:0] 和 MCO2 位选择。
对于不同的 MCO 引脚,必须将相应的 GPIO 端口在复用功能模式下进行设置。
MCO 输出时钟不得超过 100 MHz(最大 I/O 速度)。

2. 时钟配置

2.1 配置流程

①. 在STM32单片机复位之后,首先进入 startup 程序:

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

可以看出,在进入main主程序之前,先触发了 SystemInit() 函数(打开相应的时钟晶振,分频选择),这样就可以保证不需要每次都把时钟配置程序写入main.c文件了;

同样,当你想要执行自定义时钟配置程序时也可以改动这个部分。

②. SystemInit()

该函数位于 system_stm32f4xx.c 文件中

/**
  * @brief  Setup the microcontroller system
  *         Initialize the Embedded Flash Interface, the PLL and update the 
  *         SystemFrequency variable.
  * @param  None
  * @retval None
  */
void SystemInit(void)
{
  /* FPU settings ------------------------------------------------------------*/
  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
  #endif
  /* Reset the RCC clock configuration to the default reset state ------------*/
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;

  /* Reset CFGR register */
  RCC->CFGR = 0x00000000;

  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* Reset PLLCFGR register */
  RCC->PLLCFGR = 0x24003010;

  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* Disable all interrupts */
  RCC->CIR = 0x00000000;

#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
  SystemInit_ExtMemCtl(); 
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
         
  /* Configure the System clock source, PLL Multiplier and Divider factors, 
     AHB/APBx prescalers and Flash settings ----------------------------------*/
  SetSysClock();

...

}

可以看到,SystemInit()函数的作用就是使RCC_CR寄存器Bit0 置1,即开启 16MHz HSI 振荡器,复位其他的时钟,然后转到SetSysClock()函数中。
对于系统时钟,默认情况下就是SystemInit 函数的 SetSysClock() 函数中间判断的

③. SetSysClock()

点击查看代码
static void SetSysClock(void)
{
#if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx) || defined (STM32F401xx)
/******************************************************************************/
/*            PLL (clocked by HSE) used as System clock source                */
/******************************************************************************/
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* Enable HSE */
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /* Wait till HSE is ready and if Time out is reached exit */
  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;
  }

  if (HSEStatus == (uint32_t)0x01)
  {
    /* Select regulator voltage output Scale 1 mode */
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR |= PWR_CR_VOS;

    /* HCLK = SYSCLK / 1*/
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;

#if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx)      
    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
#endif /* STM32F40_41xxx || STM32F427_437x || STM32F429_439xx */

#if defined (STM32F401xx)
    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
#endif /* STM32F401xx */
   
    /* Configure the main PLL */
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);

    /* Enable the main PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till the main PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
   
#if defined (STM32F427_437xx) || defined (STM32F429_439xx)
    /* Enable the Over-drive to extend the clock frequency to 180 Mhz */
    PWR->CR |= PWR_CR_ODEN;
    while((PWR->CSR & PWR_CSR_ODRDY) == 0)
    {
    }
    PWR->CR |= PWR_CR_ODSWEN;
    while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
    {
    }      
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
#endif /* STM32F427_437x || STM32F429_439xx  */

#if defined (STM32F40_41xxx)     
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
#endif /* STM32F40_41xxx  */

#if defined (STM32F401xx)
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;
#endif /* STM32F401xx */

    /* Select the main PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= RCC_CFGR_SW_PLL;

    /* Wait till the main PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
    {
    }
  }
  else
  { /* If HSE fails to start-up, the application will have wrong clock
         configuration. User can add here some code to deal with this error */
  }
  
#endif

不要被上面的长度吓到了,其实无非就是对RCC几个寄存器的赋值,里面注释很详细,对着寄存器功能表,可以很容易理解其意思!

主要有耐心去看,很容易就搞明白怎么回事了!

2.3 配置寄存器

STM32中,对时钟的配置,即是对 RCC(Reset Clocl Control,复位和时钟寄存器)的配置,时钟寄存器包含如下几个:

2.3.1 时钟控制寄存器(RCC_CR)

偏移地址:0x00
复位值:0x0000 XX83,其中 X 未定义。
访问:无等待周期,按字、半字和字节访问

image

image

image

image

2.3.2 RCC 时钟配置寄存器 (RCC_CFGR)

偏移地址:0x08
复位值:0x0000 0000
访问:0  等待周期  2,按字、半字和字节访问
只有在时钟源切换期间进行访问时才会插入 1 或 2 个等待周期。

image

image

image
image

image

标签:PLL,CFGR,FLASH,STM32,详解,CR,时钟,RCC
From: https://www.cnblogs.com/Jimmy1988/p/17032874.html

相关文章