IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> Cubemx与HAL库系列教程|系统时钟配置详解及源码分析 -> 正文阅读

[嵌入式]Cubemx与HAL库系列教程|系统时钟配置详解及源码分析

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;//时钟源为HSE
??RCC_OscInitStruct.HSEState?=?RCC_HSE_ON;?//打开HSE
??RCC_OscInitStruct.HSEPredivValue?=?RCC_HSE_PREDIV_DIV2;
??RCC_OscInitStruct.HSIState?=?RCC_HSI_ON;
??RCC_OscInitStruct.PLL.PLLState?=?RCC_PLL_ON;//打开PLL
??RCC_OscInitStruct.PLL.PLLSource?=?RCC_PLLSOURCE_HSE;//PLL时钟源选择HSE
??RCC_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为是否为1
????if((__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?*/
???//再一次进行确认,我们是否设置了打开HSE
??????if((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

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-02-16 13:16:43  更:2022-02-16 13:16:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 8:40:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码