STM32基础知识(三):深入理解系统时钟RCC

文章目录

  • 前言
  • 一、基础知识
  • (1)RCC — 复位和时钟控制器
  • (2)HSE — 高速外部时钟信号
  • (3)LSE — 低速外部时钟信号
  • (4)HSI — 高速内部时钟信号
  • (5)LSI — 高速内部时钟信号
  • (6)RTC — 实时时钟
  • (7)PLL — 锁相环
  • (8)PLLCLK — PLL时钟
  • (9)SYSCLK — 系统时钟
  • (10)AHB — 高级高性能总线
  • (11)APB
  • (12)STM32 的多个时钟源
  • (13)外部晶振与内部晶振的区别
  • 二、时钟树详解
  • 系统时钟库函数
  • 总结

  • 前言

    本文主要探讨STM32中比较重要的一个基础知识,RCC系统时钟,系统时钟也就是CPU的脉搏,决定CPU的速率,被誉为芯片的心跳,之后再文章中将会结合《STM32F10X-中文参考手册》进行介绍时钟树以及相关知识。

    参考手册获取方式在上一篇博客中介绍


    一、基础知识

    (1)RCC – 复位和时钟控制器

    RCC :reset clock control 复位和时钟控制器。 时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令。单片机有了时钟,才能够运行执行指令。

    (2)HSE – 高速外部时钟信号

    HSE: High Speed External Clock signal,高速外部时钟信号,HSE 是高速的外部时钟信号,可以由有源晶振或者无源晶振提供,频率从4-16MHZ 不等。当使用有源晶振时,时钟从OSC_IN 引脚进入,OSC_OUT 引脚悬空,当选用无源晶振时,时钟从OSC_IN 和OSC_OUT 进入,并且要配谐振电容。HSE 最常使用的就是8M 的无源晶振

    (3)LSE – 低速外部时钟信号

    LSE: Low Speed External Clock signal,低速外部时钟信号,即是OSC32_IN和OSC32_OUT接口。外部用于RTC的32.768KHz晶振

    (4)HSI – 高速内部时钟信号

    HSI: High Speed Internal Clock signal,高速内部时钟信号,出厂校准的8MHz内部RC振荡器。

    (5)LSI – 高速内部时钟信号

    LSI: Low Speed Internal Clock signal,高速内部时钟信号,带有校准功能的40KHz的内部RC振荡器。

    (6)RTC – 实时时钟

    RTC: Real Time Clock实时时钟,用于带有年、月、日、小时、分钟、秒钟的计时器。时间显示时候使用

    (7)PLL – 锁相环

    PLL:锁相环倍频输出,PLL 时钟来源可以有两个,一个来自HSE,另外一个是HSI/2,倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。HSI 是内部高速的时钟信号,频率为8M,根据温度和环境的情况频率会有漂移,一般不作为PLL 的时钟来源。这里我们选HSE 作为PLL 的时钟来源

    (8)PLLCLK – PLL时钟

    通过设置PLL 的倍频因子, 可以对PLL 的时钟来源进行倍频, 倍频因子可以是:[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], 具体设置成多少, 由时钟配置寄存器设置。
    例如:设置PLL 的时钟来源为HSE=8M,PLL设置为9 倍频,所以经过PLL 倍频之后的PLL时钟:PLLCLK = 8M *9 = 72M。
    72M 是ST 官方推荐的稳定运行时钟,如果你想超频的话,增大倍频因子即可,最高为128M。

    (9)SYSCLK – 系统时钟

    SYSCLK:系统时钟,系统时钟来源可以是:HSI、PLLCLK、HSE,具体的时钟配置寄存器设置。最高72MHz

    (10)AHB – 高级高性能总线

    AHB:高级高性能总线,这是一种“系统总线”AHB主要用于高性能模块(如CPU、DMA和DSP等)之间的连接。AHB 系统由主模块、从模块和基础结构(Infrastructure)3部分组成,整个AHB总线上的传输都由主模块发出,由从模块负责回应。

    (11)APB

    APB:是一种外围总线。APB主要用于低带宽的周边外设之间的连接,例如UART等,它的总线架构不像 AHB支持多个主模块,在APB里面唯一的主模块就是APB 桥。

    (12)STM32 的多个时钟源

    STM32本身十分复杂,外设非常多,但我们实际使用的时候只会用到有限的几个外设,使用任何外设都需要时钟才能启动,但并不是所有的外设都需要系统时钟那么高的频率,为了兼容不同速度的设备,有些高速,有些低速,如果都用高速时钟,势必造成浪费 。
    而且同一个电路,时钟越快功耗越快,同时抗电磁干扰能力也就越弱,所以较为复杂的MCU都是采用多时钟源的方法来解决这些问题。所以便有了STM32的时钟系统和时钟树

    (13)外部晶振与内部晶振的区别

    外部晶振比较稳定而内部晶振的误差比较大,但如果对频率要求不高,如不涉及到串口通信和精确定时等情况时,则可以使用内部晶振。所以如果对频率要求不高,则一般是优先使用内部晶振。如果要省电,用到了SLEEP,则不能使用内部晶振,因为内部振荡会停止。

    二、时钟树详解

    首先简单介绍一下整个时钟树
    时钟树
    在上图中标注了时钟树每一部分的组成,东西很多,看着也很乱,为了方便理解接下来选择其中的一条线进行介绍。
    时钟树解释
    在上面这个图中,选取了一条时钟线,开始在(1)的位置 选择了外部高速时钟,一般选择8M,然后通过(2)的线路进入(3),在(3)也就是锁相环中,倍频选择x9,8M * 9 = 72M,经过前面的倍频,频率达到72M,也就是最大频率,之后进入APB预分频器(4)中,根据不同外设要求,根据不同外设需要挂载的总线,选择(5)或者(6),经过整个流程达到更改时钟频率的效果。
    小结一下:
    系统时钟SYSCLK 的左边,系统时钟有很多种选择,也就是系统时钟可以从不同种类初始频率得到,就是设置系统时钟使用哪个时钟源;
    系统时钟SYSCLK 的右边,则是系统时钟通过AHB预分频器,给相对应的外设设置相对应的时钟频率。
    从左到右可以简单理解为 ,不同时钟源—>系统时钟来源的设置—>系统时钟—>AHB分频器—>各个外设分频倍频器 —> 外设时钟的设置

    接下来分部分介绍:

    系统时钟来源
    系统时钟来源
    由上图可知,系统时钟来源于三个部分:HSI 振荡器时钟、HSE 振荡器时钟、主 PLL (PLL) 时钟
    初次之外还有两个次级时钟源
    次级时钟源

    40 kHz 低速内部 RC (LSI RC),该 RC 用于驱动独立看门狗,也可选择提供给 RTC 用于停机/待机模式下的自动唤醒。
    32.768 kHz 低速外部晶振(LSE 晶振),用于驱动 RTC 时钟 (RTCCLK)

    USB时钟源
    USB时钟源
    全速功能的USB模块,其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能从PLL输出端获取(唯一的),可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL必须使能,并且时钟频率配置为48MHz或72MHz

    时钟输出
    时钟输出
    可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。可以把时钟信号输出供外部使用。

    时钟监视系统(CSS)

    时钟监视系统
    STM32还提供了一个时钟监视系统(CSS),用于监视高速外部时钟(HSE)的工作状态。倘若HSE失效,会自动切换(高速内部时钟)HSI作为系统时钟的输入,保证系统的正常运行。

    AHB分频器给外设提供时钟
    AHB分频器
    AHB分频器可选择1、2、4、8、16、64、128、256、512分频;
    系统时钟经过AHB分频器分到五个部分:
    (1)内核总线:送给AHB总线、内核、内存和DMA使用的HCLK时钟。
    (2)Tick定时器:通过8分频后送给Cortex的系统定时器时钟。
    (3)I2S总线:直接送给Cortex的空闲运行时钟FCLK(Free running Clock)。
    (4)APB1外设:送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给通用定时器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2-7使用。
    (5)APB2外设:送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给高级定时器。该倍频器可选择1或者2倍频,时钟输出供定时器1和定时器8使用。

    另外,APB2分频器还有一路输出供ADC分频器使用,分频后送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。

    APB1(低速外设) 上的设备有:电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3、SPI2、窗口看门狗、Timer2、Timer3、Timer4。注意USB模块虽然需要一个单独的48MHz时钟信号,但它应该不是供USB模块工作的时钟,而只是提供给串行接口引擎(SIE)使用的时钟。USB模块工作的时钟应该是由APB1提供的。即: APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM等设备。
    APB2(高速外设) 上的设备有:UART1、SPI1、Timer1、ADC1、ADC2、所有普通IO口(PA~PE)、第二功能IO口。即 APB2负责AD,I/O,高级TIM,串口1等设备。
    对于不同芯片APB挂载的设备,可以在STM32相对于的芯片参考手册中查看
    以上的时钟输出中,有很多是 带使能控制 的,例如AHB总线时钟、内核时钟、各种APB1外设、APB2外设等等。 当需要使用某模块时,一定要先使能对应的时钟。


    系统时钟库函数

    该函数截取自固件库文件system_stm32f103xe.c
    为了方便阅读,我把英文注释翻译成了中文
    该函数是直接操作寄存器的,有关寄存器部分请参考数据手册的RCC 的寄存器描述部分

    void HSE_SetSysClock(void)
    {
    	RCC_ClkInitTypeDef clkinitstruct = {0};
    	RCC_OscInitTypeDef oscinitstruct = {0};
    	/* Enable HSE Oscillator and activate PLL with HSE as source */
    	/* 使能HSE,并以HSE 作为PLL 时钟源*/
    	oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    	oscinitstruct.HSEState = RCC_HSE_ON;
    	oscinitstruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    	oscinitstruct.PLL.PLLState = RCC_PLL_ON;
    	oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    	oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9;
    	if (HAL_RCC_OscConfig(&oscinitstruct)!= HAL_OK) 
    	{
    	/* Initialization Error */
    	/* 初始化错误*/
    	while (1);
    	}
    /* Select PLL as system clock source and configure the HCLK, PCLK1
    and PCLK2 clocks dividers */
    /* 选择PLL 作为系统时钟源并配置HCLK,PCLK1 和PCLK2 分频系数*/
    	clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK |
    	RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1
    	| RCC_CLOCKTYPE_PCLK2);
    	clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    	clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    	clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;
    	clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;
    	if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)
    	{
    	/* Initialization Error */
    	/* 初始化错误*/
    	while (1);
       }
    }
    void HSI_SetSysClock(void)
    {
    	RCC_ClkInitTypeDef clkinitstruct = {0};
    	RCC_OscInitTypeDef oscinitstruct = {0};
    	/* Enable HSE Oscillator and activate PLL with HSE as source */
    	/* 使能HSI, 并以HSI 作为PLL 时钟源*/
    	oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    	oscinitstruct.HSEState = RCC_HSE_ON;
    	oscinitstruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    	oscinitstruct.PLL.PLLState = RCC_PLL_ON;
    	oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    	oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9;
    	if (HAL_RCC_OscConfig(&oscinitstruct)!= HAL_OK) 
    	{
    	/* Initialization Error */
    	/* 初始化错误*/
    	while (1);
    	}
    /* Select PLL as system clock source and configure the HCLK, PCLK1
    and PCLK2 clocks dividers */
    /* 选择PLL 作为系统时钟源并配置HCLK,PCLK1 和PCLK2 分频系数*/
    	clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK |
    	RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1
    	| RCC_CLOCKTYPE_PCLK2);
    	clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    	clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    	clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;
    	clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;
    	if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)
    	{
    	/* Initialization Error */
    	while (1);
    	}
    }
    

    总结

    本文详细的介绍了STM32RCC时钟的相关知识,分析了整个时钟树,最后附上了相关的库函数。

    博客制作过程中难免有一些疏漏和不严谨之处,有问题欢迎评论区批评指正。

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32基础知识(三):深入理解系统时钟RCC

    发表评论