STM32时钟系统简介

STM32种类繁多,时钟系统也不尽相同,但基本的还是大差不差,今日小飞哥就F1系列的MCU简单聊一聊STM32的时钟系统

1、时钟种类介绍:

先来看一看时钟树图,包含了整个系统的始终来源及各个外设的始终来源

STM32 有5个时钟源:HSI、HSE、LSI、LSE、PLL。

接下来,各个时钟的含义听小飞哥白话白话,自己看ST手册也可以哒

  • 1.1 HSI时钟(内部高速时钟)

HSI时钟信号由内部8MHz的RC振荡器产生,可直接作为系统时钟或在2分频后作为PLL输入。

HSI RC振荡器能够在不需要任何外部器件的条件下提供系统时钟。它的启动时间比HSE晶体振荡器短。然而,即使在校准之后它的时钟频率精度仍较差。

校准

制造工艺决定了不同芯片的RC振荡器频率会不同,这就是为什么每个芯片的HSI时钟频率在出厂前已经被ST校准到1%(25°C)的原因。系统复位时,工厂校准值被装载到时钟控制寄存器的HSICAL[7:0]位。 如果用户的应用基于不同的电压或环境温度,这将会影响RC振荡器的精度。可以通过时钟控制寄存器里的HSITRIM[4:0]位来调整HSI频率。

  • 1.2 HSE时钟(外部高速时钟)

高速外部时钟信号(HSE)由以下两种时钟源产生:

● HSE外部晶体/陶瓷谐振器

● HSE用户外部时钟

为了减少时钟输出的失真和缩短启动稳定时间,晶体/陶瓷谐振器和负载电容器必须尽可能地靠近振荡器引脚。负载电容值必须根据所选择的振荡器来调整。

  • 1.3 PLL

内部PLL可以用来倍频HSI RC的输出时钟或HSE晶体输出时钟。PLL的设置(选择HIS振荡器除2或HSE振荡器为PLL的输入时钟,和选择倍频因子)必须在其被激活前完成。一旦PLL被激活,这些参数就不能被改动。

如果PLL中断在时钟中断寄存器里被允许,当PLL准备就绪时,可产生中断申请。 如果需要在应用中使用USB接口,PLL必须被设置为输出48或72MHZ时钟,用于提供48MHz的USBCLK时钟。

  • 1.4 LSE时钟(外部低速时钟)

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

在这个模式里必须提供一个32.768kHz频率的外部时钟源。你可以通过设置在备份域控制寄存器(RCC_BDCR)里的LSEBYP和LSEON位来选择这个模式。具有50%占空比的外部时钟信号(方波、正弦波或三角波)必须连到OSC32_IN引脚,同时保证OSC32_OUT引脚悬空。

  • 1.5 LSI时钟(内部低速时钟)

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

以上介绍来自STM32手册,总共分为5大时钟源。

2、系统时钟源选择及配置

先来看看cubemx中的时钟树图,可以一目了然的看到整个时钟架构,还是非常的nice的

要注意的是,使用内部高速时钟的话,最大能配置到64MHZ,使用外部高速时钟的话,能配置到72MHZ

了解了基本的时钟架构之后,我们要怎么样配置得到自己想要的系统频率呢?

接下来分别介绍内部高速时钟和外部高速时钟的配置

  • 2.1 内部高速时钟作为时钟输入

使用内部高速时钟的话,就不需要关注外部硬件了,可以看到内部高速时钟有3个去向,其中2个去向是可以到我们的系统时钟

sysclk后面,我们可以看到还有很多的,HCLK,PCLK,等等,这些又是什么含义呢?

系统时钟SYSCLK最大频率为72MHz(64MHZ),它是供STM32中绝大部分部件工作的时钟源。系统时钟可由PLL、HSI或者HSE提供输出,并且它通过AHB分频器分频后送给各模块使用,AHB分频器可选择1、2、4、8、16、64、128、256、512分频。其中AHB分频器输出的时钟送给5大模块使用:

①、送给AHB总线、内核、内存和DMA使用的HCLK时钟。

②、分频后送给STM32芯片的系统定时器时钟(Systick=Sysclk/8=9Mhz)

③、直接送给Cortex的自由运行时钟(free running clock)FCLK。【ARMJISHU注:FCLK 为处理器的自由振荡的处理器时钟,用来采样中断和为调试模块计时。在处理器休眠时,通过FCLK 保证可以采样到中断和跟踪休眠事件。 Cortex-M3内核的“自由运行时钟(free running clock)”FCLK。“自由”表现在它不来自系统时钟HCLK,因此在系统时钟停止时FCLK 也继续运行。FCLK和HCLK 互相同步。FCLK 是一个自由振荡的HCLK。FCLK 和HCLK 应该互相平衡,保证进入Cortex-M3 时的延迟相同。】

④、送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给定时器(Timer)2、3、4倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2、3、4使用。

⑤、送给APB2分频器。APB2分频器可选择1、2、4、8、16分频, 其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给定时器(Timer)1倍频器使用。该倍频器可选择1或者2倍频,时钟输出 供定时器1使用。另外,APB2分频器还有一路输出供ADC分频器使用,分频后送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。

以上提到3种时钟Fclk、Hclk和Pclk,简单解释如下:

Fclk为供给CPU内核的时钟信号,我们所说的cpu主频为XXXXMHz,就是指的这个时钟信号,相应的,1/Fclk即为cpu时钟周期;

Hclk为优秀的高性能总线(AHB bus peripherals)供给时钟信号(AHB为advanced high-performance bus) HCLK :AHB总线时钟,由系统时钟SYSCLK 分频得到,一般不分频,等于系统时钟,HCLK是高速外设时钟,是给外部设备的,比如内存,flash。

Pclk为优秀的高性能外设总线(APB bus peripherals)供给时钟信号(其中APB为advanced peripherals bus)。

以上介绍让大家对各个时钟有个清晰的认识,接下来言归正传,该如何在cubemx中配置呢?

当使用PLL锁相环的时候,最大系统时钟能倍频至最大64MHZ,倍频系数2-16,可以根据自己的需要选择,设置不同的主频

直接使用HSI的话,系统时钟设置为8MHZ

后面的这些外设时钟需要我们根据自己的需要调整分频系数

至此,使用内部时钟的配置就完了,接下来介绍使用外部时钟:

  • 2.2 外部高速时钟作为时钟输入

使用外部时钟,首先我们硬件上必须设计有外部晶振或者外部输入源,我们一般设计使用外部晶振

外部时钟源(HSE旁路)模式:

该模式下必须提供外部时钟。用户通过设置时钟控制寄存器中的HSEBYP和HSEON位来选择这一模式。外部时钟信号(50%占空比的方波、正弦波或三角波)必须连到SOC_IN引脚,此时OSC_OUT引脚对外呈高阻态。

外部晶体/陶瓷谐振器(HSE晶体)模式:

这种模式用得比较常见,HSE晶体可以为系统提供较为精确的时钟源。在时钟控制寄存器RCC_CR中的HSERDY位用来指示高速外部振荡器是否稳定。在启动时,直到这一位被硬件置’1’,时钟才被释放出来。HSE晶体可以通过设置时钟控制寄存器里RCC_CR中的HSEON位被启动和关闭。

选中之后,MCU的对应引脚会被使用,也即是我们的硬件设计对应的引脚

同样的,HSE的时钟也有不同的路线可以到系统时钟,直接通向SYSCLK的话,就是外部晶振频率,4-16MHZ,走PLL这条路线的话,选择就变得丰富起来

使用外部晶振,最大主频可以达到72MHZ

结合cubemx,对时钟进行配置,对新手了解MCU时钟结构还是非常有好的,配置也是非常的简单,省却了去了解一大堆的寄存器。

系统时钟配置源码分析

系统时钟初始化的参数主要封装在两个结构体里面:

跟晶体状态相关的:

/*** @brief  RCC Internal/External Oscillator (HSE, HSI, LSE and LSI) configuration structure definition*/
typedef struct
{uint32_t OscillatorType;       /*!< The oscillators to be configured.This parameter can be a value of @ref RCC_Oscillator_Type */#if defined(STM32F105xC) || defined(STM32F107xC)uint32_t Prediv1Source;       /*!<  The Prediv1 source value.This parameter can be a value of @ref RCCEx_Prediv1_Source */
#endif /* STM32F105xC || STM32F107xC */uint32_t HSEState;              /*!< The new state of the HSE.This parameter can be a value of @ref RCC_HSE_Config */uint32_t HSEPredivValue;       /*!<  The Prediv1 factor value (named PREDIV1 or PLLXTPRE in RM)This parameter can be a value of @ref RCCEx_Prediv1_Factor */uint32_t LSEState;              /*!<  The new state of the LSE.This parameter can be a value of @ref RCC_LSE_Config */uint32_t HSIState;              /*!< The new state of the HSI.This parameter can be a value of @ref RCC_HSI_Config */uint32_t HSICalibrationValue;   /*!< The HSI calibration trimming value (default is RCC_HSICALIBRATION_DEFAULT).This parameter must be a number between Min_Data = 0x00 and Max_Data = 0x1F */uint32_t LSIState;              /*!<  The new state of the LSI.This parameter can be a value of @ref RCC_LSI_Config */RCC_PLLInitTypeDef PLL;         /*!< PLL structure parameters */#if defined(STM32F105xC) || defined(STM32F107xC)RCC_PLL2InitTypeDef PLL2;         /*!< PLL2 structure parameters */
#endif /* STM32F105xC || STM32F107xC */
} RCC_OscInitTypeDef;

跟时钟源、分频相关的

/*** @brief  RCC System, AHB and APB busses clock configuration structure definition*/
typedef struct
{uint32_t ClockType;             /*!< The clock to be configured.This parameter can be a value of @ref RCC_System_Clock_Type */uint32_t SYSCLKSource;          /*!< The clock source (SYSCLKS) used as system clock.This parameter can be a value of @ref RCC_System_Clock_Source */uint32_t AHBCLKDivider;         /*!< The AHB clock (HCLK) divider. This clock is derived from the system clock (SYSCLK).This parameter can be a value of @ref RCC_AHB_Clock_Source */uint32_t APB1CLKDivider;        /*!< The APB1 clock (PCLK1) divider. This clock is derived from the AHB clock (HCLK).This parameter can be a value of @ref RCC_APB1_APB2_Clock_Source */uint32_t APB2CLKDivider;        /*!< The APB2 clock (PCLK2) divider. This clock is derived from the AHB clock (HCLK).This parameter can be a value of @ref RCC_APB1_APB2_Clock_Source */
} RCC_ClkInitTypeDef;

配置代码如下:

/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;//时钟源为HSERCC_OscInitStruct.HSEState = RCC_HSE_ON; //打开HSERCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV2;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;//打开PLLRCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;//PLL时钟源选择HSERCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}

配置参数可以对照cubemx中时钟树图分析分析

跟时钟相关的宏定义基本都在STM32XXX_RCC.h中,就不再多列举了

在HAL_RCC_OscConfig,函数中对HSE的配置如下,贴出一部分代码,其他时钟源配置类似,贴了一些中文注释:

/* Check the parameters */assert_param(IS_RCC_OSCILLATORTYPE(RCC_OscInitStruct->OscillatorType));/*------------------------------- HSE Configuration ------------------------*/ if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSE) == RCC_OSCILLATORTYPE_HSE){/* Check the parameters */assert_param(IS_RCC_HSE(RCC_OscInitStruct->HSEState));/* When the HSE is used as system clock or clock source for PLL in these cases HSE will not disabled *///判断HSE是否作为系统时钟,或者作为PLL时钟的来源// RCC_CFGR_SWS_HSE=0x00000004//__HAL_RCC_GET_SYSCLK_SOURCE函数,获取CFGR位 3:2 SWS: 系统时钟切换状态 (System clock switch status)//判断3:2位是否为01,含义:01: HSE 振荡器用作系统时钟,这两个位为只读//也就是判断此时系统时钟或者主PLL时钟是否已经设置为HSE//RCC_CFGR_SWS_PLL=0x00000008 ,判断CFGR的第3为是否为1if((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_HSE)                                                                     ||\((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_PLL) && ((RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) == RCC_PLLCFGR_PLLSRC_HSE))){   //PLL作为系统时钟,  RCC_PLLCFGR_PLLSRC=0x00400000,第22位,是否选择:1:选择 HSE 振荡器时钟作为 PLL 和 PLLI2S 时钟输入//如果HSE作为系统时钟来源,或者作为PLL时钟来源的话if((__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET) && (RCC_OscInitStruct->HSEState == RCC_HSE_OFF)){//此时HSE已经打开了,或HSE没有使能,这里的任何一种情况都会导致失败return HAL_ERROR;}}else  //否则的话,系统的时钟还没有进行初始化{/* Reset HSEON and HSEBYP bits before configuring the HSE --------------*///对RCC->CFGR 寄存器的23:16 清零,也就是复位HSEON(关闭振荡器) 和重置就绪位__HAL_RCC_HSE_CONFIG(RCC_HSE_OFF);//获取当前系统时间戳,用于判断关闭HSE是否超时/* Get Start Tick*/tickstart = HAL_GetTick();/* Wait till HSE is disabled */  while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET)  //等待HSE关闭{if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE){return HAL_TIMEOUT;  //超过最大时间为关闭HSE则,出错}       }/* Set the new HSE configuration ---------------------------------------*///重新设置HSE,这个值来自于结构体,我们使用就需要使能,通过设置就可以对RCC->CFGR 寄存器的23:16,写此值__HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState);/* Check the HSE State *///再一次进行确认,我们是否设置了打开HSEif((RCC_OscInitStruct->HSEState) != RCC_HSE_OFF){//进入说明,我们的确是打开了/* Get Start Tick*///获取当前系统时间戳tickstart = HAL_GetTick();/* Wait till HSE is ready */ //获取HSE就绪标志位,未就绪就等待while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET){if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE){return HAL_TIMEOUT;} }}else //否则没有打开HSE的开关咯{/* Get Start Tick*/tickstart = HAL_GetTick();/* Wait till HSE is bypassed or disabled */while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET){if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE) //HSE_TIMEOUT_VALUE=5000{return HAL_TIMEOUT;} }}}}

摘出来这段,就是等待时钟相关标志位置位,保证系统处于稳定的状态

/* Set the new HSE configuration ---------------------------------------*/__HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState);/* Check the HSE State */if (RCC_OscInitStruct->HSEState != RCC_HSE_OFF){/* Get Start Tick */tickstart = HAL_GetTick();/* Wait till HSE is ready */while (__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET){if ((HAL_GetTick() - tickstart) > HSE_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}else{/* Get Start Tick */tickstart = HAL_GetTick();/* Wait till HSE is disabled */while (__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET){if ((HAL_GetTick() - tickstart) > HSE_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}

关于时钟的配置设计的寄存器是非常多的,小飞哥刚开始学习的时候,有寄存器版本和库函数版本,当时一看寄存器真精简啊,后来发现库函数用着好简单...后来就放弃了寄存器版本...

今天的分享就要OVER了,仅仅进行了比较粗略的介绍,希望对各位小伙伴有一些帮助,有疑问的可以+小飞哥好友,一起交流学习

 专辑|RT-Thread实战笔记

 专辑|cubemx与HAL库系列教程

☞ 如何制定通讯协议及如何解析协议数据

☞ 10分钟教你玩转freemodbus

Cubemx与HAL库系列教程|系统时钟配置详解及源码分析相关推荐

  1. UCOS你问我答系列之系统时钟节拍详解

    前言 系统时钟节拍是多任务得以正常运行的基石,UCOS的系统时钟节拍一般依赖于MCU的硬件定时器.硬件定时器产生固定时间间隔的中断,中断中调用UCOS的系统函数,完成多任务操作系统的基本调度功能. 本 ...

  2. nightwatch系列教程05——Nightwatch配置详解

    本章内容翻译自http://nightwatchjs.org/gettingstarted#settings-file. 测试运行接收一个配置文件作为参数,默认是当前目录下的 nightwatch.j ...

  3. java log 配置,java日志系统--log4j配置解析过程,源码分析

    log4j 可以看成是非常类似jdk logger 结构 ,有个logger 与logManger 都是在logManger的静态块中初始化类,加载配置文件 Logger.getLogger(Test ...

  4. Spring Security系列(32)- Spring Security Oauth2之authorities授权使用详解及源码分析

    前言 在oauth_client_details表中,有一个authorities字段,从字面上来看是授权的意思,在之前我们分析了可以通过resourceId和scope进行授权,那么这个author ...

  5. 【STM32学习】时钟配置详解

    [STM32学习]时钟配置详解 看懂时钟图 结合代码 外部高速时钟修改 看懂时钟图 在刚开始学习32的时候,并不会在意这些,或者即使看了也看的不是很明白.随着学习的深入,我们发现看门狗.定时器.ADC ...

  6. 深入浅出Mybatis系列(四)---配置详解之typeAliases别名(mybatis源码篇)

    上篇文章<深入浅出Mybatis系列(三)---配置详解之properties与environments(mybatis源码篇)> 介绍了properties与environments, ...

  7. 【STM32】系统时钟RCC详解(超详细,超全面)

    转载:https://blog.csdn.net/as480133937/article/details/98845509 1什么是时钟 时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的 ...

  8. Maven系列一pom.xml 配置详解

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  9. 关于STM32F105/107时钟配置详解

    本文用的是标准库,先给出时钟配置代码,代码在system_stm32f10x.c里面. 首先定义最终系统频率72MHz: 如果是其他频率把72的注释掉,打开你想配置的频率.这个频率并不会影响实际的设置 ...

最新文章

  1. TypeError: string argument without an encoding
  2. 【必读】2019年深度学习自然语言处理最新十大发展趋势, 附报告下载
  3. 多层陶瓷电容器用处_陶瓷电容的作用及特点
  4. python智能办公系统_用 Python 自动化办公能做到哪些有趣或有用的事情?
  5. gradle全局使用阿里云镜像
  6. VC6.0 控件Radio Button的使用
  7. 中文导致Mybatis无效的列索引
  8. 自然语言处理中的语言模型与预训练技术的总结
  9. linux pxe安装视频,Linux—图解PXE实现全自动安装系统(1)
  10. android如何让gps服务停止,android – 启动/停止GPS(或位置服务)时接收通知(通过BroadcastReceiver)...
  11. C语言之文件读写探究(三):fputs、fgets、feof(一次读写一行字符(文本操作))
  12. 人类无法抗拒的10种心理
  13. 路径规划之基于优化的规划算法
  14. Markdown语法(一)标题段落分割线
  15. caffemodel中的参数及特征的抽取
  16. Atitit. .net c# web 跟客户端winform 的ui控件结构比较
  17. anylogic和java,基于Anylogic的Java代码入门教程
  18. 查看linux系统版本命令大全
  19. 局域网共享文件搭建方法
  20. python实现离散沃尔什变换_快速沃尔什变换(示例代码)

热门文章

  1. 适配器模式【Adapter Pattern】
  2. Session与Cookie的区别与联系
  3. n个元素的所有子集(递归+非递归 +不去重)
  4. 【xlwings api语言参考】Range.FormulaR1C1 属性
  5. Viper快速配置与上手
  6. 边缘提取之Roberts算子
  7. 苹果手机充值显示服务器异常,球球大作战iOS会员充值异常解决办法
  8. 进制转换(非常详细+算法代码)
  9. 导入式样式表CSS与链接式样式表CSS的区别
  10. 火狐怎么打开html页面,电脑如何设置火狐浏览器主页|电脑设置火狐启动页面的方法...