【STM32】STM32时钟系统和SystemInit函数解读

发布者:科技梦行者最新更新时间:2019-03-13 来源: eefocus关键字:STM32  时钟系统  SystemInit函数 手机看文章 扫描二维码
随时随地手机看文章

时钟系统就是CPU的脉搏,像人的心跳一样,重要性不言而喻。由于STM32本身十分复杂,外设非常多,但并不是所有的外设都需要系统时钟那么高的频率,比如看门狗以及RTC只需要几十k的时钟即可。并且,同一个电路,时钟越快功耗越快,同时抗电磁干扰能力也就越弱,所以较为复杂的MCU都是采用多时钟源的方法来解决这些问题。


STM32F1xx官方资料:

《STM32中文参考手册V10》-第六章 复位和时钟控制 RCC


STM32的时钟系统

STM32的时钟系统图



上图是STM32的时钟系统图。STM32 有5个时钟源:HSI、HSE、LSI、LSE、PLL,在图中有红色方框标记的位置。从时钟频率来分可以分成高速时钟源和低速时钟源,在这5个中HSI、HSE和PLL是高速时钟源,LSI和LSE是低速时钟源。从时钟来源来分可以分成外部时钟源和内部时钟源。外部时钟源就是从外部通过接晶振的方式获取时钟源。其中,HSE和ISE是外部时钟源,其他的是内部时钟源。下面来看看STM32的5个时钟源:


HSI(High Speed Internal)是高速内部时钟,RC振荡器,频率为8MHz,精度不高;


HSE(High Speed External)是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz;


LSI(Low Speed Internal)是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。独立看门狗的时钟源只能是LSI,同时LSI还可以做PTC的时钟源;


LSE(Low Speed External)是低速外部时钟,接频率为32.768kHz的石英晶体。这个主要是RTC的时钟源。


PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。


下面分析一下,图中A-E标识的五个地方:


A:STM32可以选择一个时钟信号输出到MCO脚(PA8,时钟输出引脚)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。这个时钟可以用来给外部其他系统提供时钟源;


B:这里是RTC的时钟源,可以选择LSI、LSE以及HSE的128分频;


C:此处的USB时钟源来自于PLL时钟源。STM32有一个全速功能的USB模块,其串行接口引擎需要一个48MHz的时钟源。该时钟源只能由PLL输出端获取,若PLL输出72MHz,则1.5分频;若PLL输出48MHz,则1分频。也就是说,当需要使用USB模块的时候,PLL必须使能;


D:STM32的系统时钟SYSCLK,提供STM32的绝大多数部件工作的时钟源。它的来源可以是三个时钟源:HSI振荡器时钟、HSE振荡器时钟和PLL时钟。系统时钟的最大频率为72MHz。


E:这里指的就是其他的所有外设了,这些外设的时钟来源都是SYSCLK。SYSCLK通过AHB分频器分频后送给各模块使用。这些模块包括:


AHB总线、内核、内存和DMA使用的HCLK时钟(最大72MHz);


通过8分频后送给Cortex的系统定时器时钟,也就是systick了;


直接送给Cortex的空闲运行时钟FCLK;


送给APB1分频器。APB1分频器输出一路给APB1外设使用(PCLK1,最大频率36MHz),另一路给通用定时器使用;


送给APB2分频器。APB2分频器输出一路给APB2外设使用(PCLK2,最大频率72MHz),另一路给定时器使用;


APB1和APB2的区别


这里需要理解一下APB1和APB2的区别:



APB1上面连接的是低速外设,包括电源接口、备份接口、CAN、USB、I2C1、I2C2、USART2、USART3、UART4、UART5、SPI2、SP3等;


而APB2上面连接的是高速外设,包括UART1、SPI1、Timer1、ADC1、ADC2、ADC3、所有的普通I/O口(PA-PE)、第二功能I/O(AFIO)口等。


在上面的时钟输出中,有很多是带使能控制的,例如AHB总线时钟、内核时钟、各种APB1外设、APB2外设等等。当使用某模块时,记得一定要先使能其相应的时钟。


 


SystemInit函数

STM32时钟系统的配置除了初始化的时候在system_stm32f10x.c中的SystemInit函数中外,其他的配置主要在stm32f10x_rcc.c文件中,需要对这个文件好好研究一下。本文主要看一下初始化时的SystemInit函数:


void SystemInit (void)

{

  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */

  /* Set HSION bit */

  RCC->CR |= (uint32_t)0x00000001;

 

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */

#ifndef STM32F10X_CL

  RCC->CFGR &= (uint32_t)0xF8FF0000;

#else

  RCC->CFGR &= (uint32_t)0xF0FF0000;

#endif /* STM32F10X_CL */   

  

  /* 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;

 

#ifdef STM32F10X_CL

  /* Reset PLL2ON and PLL3ON bits */

  RCC->CR &= (uint32_t)0xEBFFFFFF;

 

  /* Disable all interrupts and clear pending bits  */

  RCC->CIR = 0x00FF0000;

 

  /* Reset CFGR2 register */

  RCC->CFGR2 = 0x00000000;

#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)

  /* Disable all interrupts and clear pending bits  */

  RCC->CIR = 0x009F0000;

 

  /* Reset CFGR2 register */

  RCC->CFGR2 = 0x00000000;      

#else

  /* Disable all interrupts and clear pending bits  */

  RCC->CIR = 0x009F0000;

#endif /* STM32F10X_CL */

    

#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)

  #ifdef DATA_IN_ExtSRAM

    SystemInit_ExtMemCtl(); 

  #endif /* DATA_IN_ExtSRAM */

#endif 

 

  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */

  /* Configure the Flash Latency cycles and enable prefetch buffer */

  SetSysClock();

 

#ifdef VECT_TAB_SRAM

  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */

#else

  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */

#endif 

}

SystemInit函数主要就是完成系统初始化时的默认状态的设置,同时系统时钟默认是在SetSysClock()函数中来进行判断的,而判断的依据则是通过宏定义设置的。先看看SetSysClocks()函数:


static void SetSysClock(void)

{

#ifdef SYSCLK_FREQ_HSE

  SetSysClockToHSE();

#elif defined SYSCLK_FREQ_24MHz

  SetSysClockTo24();

#elif defined SYSCLK_FREQ_36MHz

  SetSysClockTo36();

#elif defined SYSCLK_FREQ_48MHz

  SetSysClockTo48();

#elif defined SYSCLK_FREQ_56MHz

  SetSysClockTo56();  

#elif defined SYSCLK_FREQ_72MHz

  SetSysClockTo72();

#endif

 

 /* If none of the define above is enabled, the HSI is used as System clock

    source (default after reset) */ 

}

这段代码很简单,就是判断系统宏定义的时钟是多少,然后设置相应值。系统默认的宏定义是72HMz:


#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)

/* #define SYSCLK_FREQ_HSE    HSE_VALUE */

 #define SYSCLK_FREQ_24MHz  24000000

#else

/* #define SYSCLK_FREQ_HSE    HSE_VALUE */

/* #define SYSCLK_FREQ_24MHz  24000000 */ 

/* #define SYSCLK_FREQ_36MHz  36000000 */

/* #define SYSCLK_FREQ_48MHz  48000000 */

/* #define SYSCLK_FREQ_56MHz  56000000 */

#define SYSCLK_FREQ_72MHz  72000000

#endif

接下来就选择进入SetSysClockTo72()函数,我们看一下这个函数的内容:


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/2 */

    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

 

    /*  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);

 

    /* 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 */

  }

}

这里主要的内容就是下面这一段:


    /* 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;

 

    /*  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);

设置SYSCLK为72MHz,HCLK=SYSCLK,PCLK1=SYSCLK/2,PCLK2=SYSCLK。总而言之,即通过HSE(8MHz)倍频9倍成SYSCLK(72MHz),然后直接输出为HCLK(72MHz)、PCLK2(72HMz)、PCLK1(36HMz)。


相对应的,如果说宏定义不是SYSCLK_FREQ_72MHz ,而是其他的宏定义。那么就会进入各自的SetSysClockToxx()函数,比如说SetSysClockTo54()、SetSysClockTo36()等等。然后在相应的程序中,会对SYSCLK等进行赋值。


同时,在设置好系统时钟之后,可以通过变量SystemCoreClock来获取系统时钟值。这也是在stm32f10x.c文件中设置的:


#ifdef SYSCLK_FREQ_HSE

  uint32_t SystemCoreClock         = SYSCLK_FREQ_HSE;        /*!< System Clock Frequency (Core Clock) */

#elif defined SYSCLK_FREQ_24MHz

  uint32_t SystemCoreClock         = SYSCLK_FREQ_24MHz;        /*!< System Clock Frequency (Core Clock) */

#elif defined SYSCLK_FREQ_36MHz

  uint32_t SystemCoreClock         = SYSCLK_FREQ_36MHz;        /*!< System Clock Frequency (Core Clock) */

#elif defined SYSCLK_FREQ_48MHz

  uint32_t SystemCoreClock         = SYSCLK_FREQ_48MHz;        /*!< System Clock Frequency (Core Clock) */

#elif defined SYSCLK_FREQ_56MHz

  uint32_t SystemCoreClock         = SYSCLK_FREQ_56MHz;        /*!< System Clock Frequency (Core Clock) */

#elif defined SYSCLK_FREQ_72MHz

  uint32_t SystemCoreClock         = SYSCLK_FREQ_72MHz;        /*!< System Clock Frequency (Core Clock) */

#else /*!< HSI Selected as System Clock source */

  uint32_t SystemCoreClock         = HSI_VALUE;        /*!< System Clock Frequency (Core Clock) */

#endif

 


总结与分析

这里总结一下SystemInit函数默认设置的系统时钟的大小:


时钟名称 时钟大小

SYSCLK(系统时钟) 72MHz

HCLK(AHB总线时钟) 72MHz

PCLK1(APB1总线时钟) 36MHz

PCLK2(APB2总线时钟) 72MHz

PLL时钟 72MHz

关键字:STM32  时钟系统  SystemInit函数 引用地址:【STM32】STM32时钟系统和SystemInit函数解读

上一篇:【STM32】STM32端口复用和重映射(AFIO辅助功能时钟)
下一篇:【STM32】MDK中寄存器地址名称映射分析

推荐阅读最新更新时间:2024-03-16 16:25

stm32学习笔记之SysTick的思考
首先我们要明白什么是SysTick定时器? Sys 系统 ,tick 滴答声 ,系统滴答滴答很形象地表示了它是一个系统节拍器。SysTick 是一个24 位的倒计数定时器,当计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息。 为什么要设置SysTick定时器? (1)产生操作系统的时钟节拍 SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持
[单片机]
<font color='red'>stm32</font>学习笔记之SysTick的思考
关于STM32中管脚复用的使用方法
在STM32的数据手册的管脚分配图中可以看到:PC14与OSC32_IN公用一个引脚,PC15与OSC32_OUT公用一个引脚,管脚配置如图1、图2、图3所示。它们的使用方法如下: 当LSE(低速外部时钟信号)开启时,这两个公用管脚的功能是OSC32_IN和OSC32_OUT。 当LSE(低速外部时钟信号)关闭时这两个公用管脚的功能是PC14和PC15。 备用区域控制寄存器(RCC_BDCR)的LSEON用于控制LSE的开启或关闭。关于这个寄存器的用法请参看《STM3210x技术参考手册》。 图1 STM32 LQFP48封装引脚图 图2 STM32 LQFP64封装引脚图 图3 STM32 LQFP100封装
[单片机]
关于<font color='red'>STM32</font>中管脚复用的使用方法
STM32 基础系列教程 3 – 外部中断
前言 学习stm32 GPIO 的外部中断使用,用中断模式实现简单的按键输入功能,用按实现基础实验二中的LED亮灭切换功能! 示例详解 基于硬件平台: STM32F10C8T6最小系统板, MCU 的型号是 STM32F103c8t6, 使用stm32cubemx 工具自动产生的配置工程,使用KEIL5编译代码。将PA0引脚设置成外部中断输入引脚(中断模式设为下降沿中断),程序得到PA0引脚的(按键操作)输入状态,并根据其输入电平的高低来控制PC13输出,来控制LED,实现按一次LED亮,再按一下,LED灭的功能。 本示例所用的最小系统板原理图: 新建STM32 CUBEMX 工程, 双击桌面STM32Cube
[单片机]
<font color='red'>STM32</font> 基础系列教程 3 – 外部中断
STM32外部中断EXTI学习笔记
硬件情况:采用PA11管脚 需要明确的是,PxN管脚共用外部中断线EXTIN和外部中断向量EXTIN_IRQn和中断服务程序入口EXTIN_IRQHandler,但是需要注意的是 共用EXTI9_5_IRQn和EXTI9_5_IRQHandler、 共用EXTI15_10_IRQn和EXTI15_10_IRQHandler 基本过程: 1、设置时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); 注意需要打开AFIO时钟 2、配置GPIO GPIO_InitStructure.GPIO_Pin = G
[单片机]
基于stm32的通用定时器详解
TM32的定时器是个强大的模块,定时器使用的频率也是很高的,定时器可以做一些基本的定时,还可以做PWM输出或者输入捕获功能。 时钟源问题: 名为TIMx的有八个,其中TIM1和TIM8挂在APB2总线上,而TIM2-TIM7则挂在 APB1总线上。其中TIM1&TIM8称为高级控制定时器(advanced control timer).他们所在的APB2总线也比APB1总线要好。APB2可以工作在72MHz下,而APB1最大是36MHz。 定时器的时钟不是直接来自APB1或APB2,而是来自于输入为APB1或APB2的一个倍频器。 下面以定时器2~7的时钟说明这个倍频器的作用:当APB1的预分频系数为1时,这个倍频器不起作用,
[单片机]
基于<font color='red'>stm32</font>的通用定时器详解
STM32入门系列-使用库函数点亮LED软硬件分析
电路图分析 首先找来单片机的原理图,根据原理图进行相关的设计工作。 例如在上图中相同网络标号表示它们是连接在一起的,因此D1发光二极管阴极是连接在STM32的PC0管脚上,D2指示灯阴极连接在PC1管脚上,其他LED管脚以此类推。如果要使D1指示灯亮,只需要控制PC0管脚输出低电平, 如果要使D1指示灯灭,只需控制PC0输出高电平。如果你们使用的是其他板子,连接LED的管脚和极性不一样,那么只需要在程序中修改对应的GPIO管脚和输出电平状态即可,原理是一样的。 要点亮D1发光二极管,也就是让STM32的PC0管脚输出一个低电平。 库函数支持文件 如果采用的是库函数开发,需要复制创建好的库函数模板,在此模板上进行程序开发
[单片机]
<font color='red'>STM32</font>入门系列-使用库<font color='red'>函数</font>点亮LED软硬件分析
STM32 GPIO 配置
固件库 V3.5 IAR /******************************************************************************* * 函数名称: GPIO_Configuration * 函数功能: 设置各GPIO端口功能 * 参数变量: NONE * 全局变量: NONE * 调用函数: * 修改时间: * 版 本: V1.0 * 状 态:调试完成 *******************************************************************************/ void GPIO_Configuration(voi
[单片机]
STM32的ADC的采样周期确定
  采样频率的确定   1.首先确定ADC 的时钟,这里需要看你的RCC的设置。在采用固件库的基础上,设定ADC的采样频率相对来说是很容易的。   (1)由时钟控制器提供的ADCCLK 时钟和PCLK2(APB2 时钟)同步。CLK 控制器为ADC 时钟提供一个专用的可编程预分频器。   (2) 一般情况下在程序 中将 PCLK2 时钟设为 与系统时钟 相同   RCC_HCLKConfig(RCC_SYSCLK_Div1);   RCC_PCLK2Config(RCC_HCLK_Div1);   RCC_PCLK1Config(RCC_HCLK_Div2);   (3)采样时间和转换时间   ADC 使用若干个ADC_CLK 周
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved