从零开始学嵌入式之STM32——9.STM32的时钟系统

一、STM32的复杂的时钟系统

1.时钟源

在STM32芯片中,设计了复杂的时钟系统,我们称之为时钟树。多样时钟源的设计核心是节能:高速外设配高速时钟,低速外设配低速时钟,最大化降低芯片功耗。

(1)在STM32中,系统时钟可以选择3种时钟源作为驱动:

HSI (High Speed Internal oscillator,高速内部时钟)

HSE(High Speed External ,高速外部时钟)

PLL(Phase Locked Loop,锁相环/倍频器)

(2)另外还有两种二级时钟:

LSI时钟(Low Speed Internal,低速内部时钟)

LSE时钟(Low Speed External,低速外部时钟)

RC振荡器不太稳定,优先选择外部时钟HSE。

以下是对STM32的时钟源进行的汇总

|------------|--------|-------------------------|------------------------|----|-----------------------------------------------------------------------------------------------------------------|
| 时钟源 | 类型 | 频率范围 | 稳定性 | 功耗 | 主要用途 |
| HSI高速内部时钟 | 内部RC电路 | 8MHz(默认) | 差(±1%~±3%) | 低 | 1. 芯片上电默认时钟(无外部晶振时); 2. 应急备份、极简电路; 3. 调试阶段临时使用 |
| HSE高速外部时钟 | 外部晶振 | 4~16MHz (常用 MHz/12MHz) | 优(晶振精度 ±10ppm) | 略高 | 1. 系统主时钟首选; 2. 串口 / 定时器 / USB 等对时钟精度要求高的外设; 3. PLL 倍频的输入源 |
| PLL锁相环/倍频器 | 倍频器 | 最大 72MHz(F103系列) | 依赖输入源(HSE 输入则稳,HSI 则差) | 中 | 1. 系统核心时钟(SYSCLK)的最终来源;2. 倍频规则: - HSI 输入:PLL = HSI/2 × 倍频(如 8/2×16=64MHz); - HSE 输入:PLL = HSE × 倍频(如 8×9=72MHz) |
| LSI低速内部时钟 | 内部RC电路 | 40kHz 左右(典型) | 差 | 极低 | 1. 独立看门狗(IWDG)专用; 2. RTC 时钟备份(无 LSE 时) |
| LSE低速外部时钟 | 外部晶振 | 32.768kHz(固定) | 尤 | 低 | 1. RTC 实时时钟(精准计时); 2. 低功耗模式下的时钟源 |

LSE为什么是32768Hz的原因:因为它是2的15次方,很容易得到一秒钟的时长。

LSI的用法:

也可以给RTC使用--作为备用

给看门狗使用--看门狗一般采用LSI

1.1.外部时钟源

1.1.1.OSC_OUT/OSC_IN:振荡器引脚,外接高速晶振

HSE:高速外部时钟源,4-16MHz可选。常用8MHz

1.1.2.OSC32_IN/OSC32_OUT:低速晶振引脚,外接低速晶振

LSE:低速外部时钟源,常用32.768KHz

1.1.3.MCO:主时钟输出引脚,为片外外设提供时钟

1.2.内部时钟源

HSI:高速内部时钟源,固定为8MHz。使用RC振荡电路,精度较差。

LSI:低速内部时钟源,固定为40KHz。使用RC振荡电路,精度较差。

PLL:锁相环倍频器

SYSCLK:系统时钟

二、时钟的作用

时钟的作用主要有两方面

  1. 生成系统时钟(SYSCLK):作为芯片内部核心工作节拍,要求高频、高精度;
  2. 生成实时时钟(RTCCLK):用于外部时间感知,频率低、精度要求适中。

三、芯片启动时,系统时钟的时钟源切换:

芯片启动时,由于外部晶振时钟需要启动时间较长,因此芯片会先使用内部时钟,待外部时钟稳定后再切换到外部时钟。如下图:

先使用HSI后切换到HSE的过程,在芯片的启动代码中的SystemInit函数中有体现:

cpp 复制代码
//startup_stm32f10x_hd.s汇编文件中的启动入口函数
; Reset handler
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP

SystemInit函数在startup_stm32f10x.c文件中实现

cpp 复制代码
//下面的代码是基于STM32F103ZET6的初始化操作,可以看出,系统初始化时,第一步先使能了内置时钟HSI
//在执行完初始化操作的最后,通过SetSysClock();函数将时钟切换到外部时钟。
void SystemInit (void)
{
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;
  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
  RCC->CFGR &= (uint32_t)0xF8FF0000;
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;
  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;
  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC->CFGR &= (uint32_t)0xFF80FFFF;
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;
  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();
}

SetSysClock();函数也是在startup_stm32f10x.c文件中实现

cpp 复制代码
//代码节选
static void SetSysClock(void)
{
  SetSysClockTo72();
}

SetSysClockTo72();函数也是在startup_stm32f10x.c文件中实现

cpp 复制代码
static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    
  /* 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)
  {
    /* Enable Prefetch Buffer */
    FLASH->ACR |= FLASH_ACR_PRFTBE;

    /* Flash 2 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    

 
    /* HCLK = SYSCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
      
    /* PCLK2 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

#ifdef STM32F10X_CL
    //条件编译不生效部分,略去
#else    
    /*  PLL configuration: 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);
#endif /* STM32F10X_CL */

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

    /* Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
    
    /* Select PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    

    /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
  }
  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 */
  }
}

以上代码就是芯片启动时,系统时钟的启动过程。

三、总结

1.HSE时钟

高速外部时钟,由外部时钟源提供,目前几乎所有的STM32单片机的设计都是在外部接一个8MHz的晶振,经过PLL倍频(9倍频)后得到一个72MHz的系统时钟。系统默认设置就是这个时钟,在启动文件中可以看到相应的配置。

2.HSI时钟

HSI时钟信号由内部8MHz的RC振荡器产生,可以直接作为系统时钟或在2分频后作为PLL输入。HSI RC振荡器能够在不需要任何外部器件的条件下提供系统时钟。它的启动时间比HSE晶体振荡器短。然而,即使在校准之后他的时钟频率精度仍然较差。

3.PLL时钟

内部PLL用来倍频HSI RC和HSE晶体输出的时钟。PLL的设置必须在其被激活前完成。一旦PLL被激活,这些参数就不能被改动。如果PLL中断在时钟中断寄存器中被允许,当PLL准备就绪时,可产生中断申请。

PLL时钟一般都是对外部的8MHz的时钟信号经过9倍频后,得到72MHz的时钟频率,这是STM32F1系列允许的最高时钟频率。

4.LSE时钟

LSE晶体是一个32.768KHz的低速外部晶体或陶瓷谐振器。它为实时时钟或其他定时功能提供一个低功耗且精确的时钟源。

LSE是不能驱动系统时钟的。

5.LSI时钟

LSI RC担当一个低功耗时钟源的角色,它可以在停机和待机模式下保持运行,为独立看门狗和自动唤醒单元提供时钟。LSI时钟频率大约40KHz(在30~60KHz之间)。

LSI也不能驱动系统时钟。

相关推荐
飞睿科技2 小时前
乐鑫智能开关方案解析:基于ESP32-C系列的低功耗、高集成设计
嵌入式硬件·物联网·esp32·智能家居·乐鑫科技
来自晴朗的明天3 小时前
13、NMOS 电源防反接电路
单片机·嵌入式硬件·硬件工程
17(无规则自律)4 小时前
深入浅出 Linux 内核模块,写一个内核版的 Hello World
linux·arm开发·嵌入式硬件
芯岭技术4 小时前
PY32MD310单片机:高性能、低功耗的32位电机控制微控制器
单片机·嵌入式硬件
wotaifuzao5 小时前
STM32 + FreeRTOS 的订阅通知组件架构
stm32·嵌入式硬件·架构·freertos·事件驱动·嵌入式架构
小龙报5 小时前
【51单片机】深度解析 51 串口 UART:原理、配置、收发实现与工程化应用全总结
c语言·开发语言·c++·stm32·单片机·嵌入式硬件·51单片机
Lester_110112 小时前
STM32 高级定时器PWM互补输出模式--如果没有死区,突然关闭PWM有产生瞬间导通的可能吗
stm32·单片机·嵌入式硬件·嵌入式软件
小李独爱秋14 小时前
“bootmgr is compressed”错误:根源、笔记本与台式机差异化解决方案深度指南
运维·stm32·单片机·嵌入式硬件·文件系统·电脑故障
梁洪飞16 小时前
内核的schedule和SMP多核处理器启动协议
linux·arm开发·嵌入式硬件·arm