STM32——STM32F103时钟解析(正点原子资料+HAL库代码分析)

news2025/5/24 5:53:56

文章目录

    • 前言
    • 时钟树详解
    • 系统时钟配置
    • 系统时钟使能配置

前言

上次写系统时钟解析的时候说出一篇103的时钟解析,我就整理HAL库开发的正点的资料,给小白梳理,我也是小白,不做权威使用。

时钟树详解

在这里插入图片描述
在 STM32 中,有五个时钟源,为 HSI、HSE、LSI、LSE、PLL。从时钟频率来分可以分为高速时钟源和低速时钟源,在这 5 个中 HIS,HSE 以及 PLL 是高速时钟,LSI 和 LSE是低速时钟。从来源可分为外部时钟源和内部时钟源,外部时钟源就是从外部通过接晶振的方式获取时钟源,其中 HSE 和 LSE 是外部时钟源,其他的是内部时钟源。下面我们看看 STM32 的5 个时钟源,我们讲解顺序是按图中红圈标示的顺序:

①、HSI 是高速内部时钟,RC 振荡器,频率为 8MHz。
②、HSE 是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为 4MHz~16MHz。
③、LSI 是低速内部时钟,RC 振荡器,频率为 40kHz。独立看门狗的时钟源只能是 LSI,同时 LSI 还可以作为 RTC 的时钟源。
④、LSE 是低速外部时钟,接频率为 32.768kHz 的石英晶体。这个主要是 RTC 的时钟源。
⑤、PLL 为锁相环倍频输出,其时钟输入源可选择为 HSI/2、HSE 或者 HSE/2。倍频可选择为 2~16 倍,但是其输出频率最大不得超过 72MHz。

上面我们简要概括了 STM32 的时钟源,那么这 5 个时钟源是怎么给各个外设以及系统提供时钟的呢?这里我们将一一讲解。我们还是从图的下方讲解起吧,因为下方比较简单。图中我们用 A~E 标示我们要讲解的地方。

A. MCO 是 STM32 的一个时钟输出 IO(PA8),它可以选择一个时钟信号输出,可以选择为 PLL 输出的 2 分频、HSI、HSE、或者系统时钟。这个时钟可以用来给外部其他系统提供时钟源。
B. 这里是 RTC 时钟源,从图上可以看出,RTC 的时钟源可以选择 LSI,LSE,以及HSE 的 128 分频。
C. 从图中可以看出 C 处 USB 的时钟是来自 PLL 时钟源。STM32 中有一个全速功能的 USB 模块,其串行接口引擎需要一个频率为 48MHz 的时钟源。该时钟源只能从 PLL 输出端获取,可以选择为 1.5 分频或者 1 分频,也就是,当需要使用 USB模块时,PLL 必须使能,并且时钟频率配置为 48MHz 或 72MHz。
D.处就是 STM32 的系统时钟 SYSCLK,它是供 STM32 中绝大部分部件工作的时钟源。系统时钟可选择为 PLL 输出、HSI 或者 HSE。系统时钟最大频率为 72MHz,当然你也可以超频,不过一般情况为了系统稳定性是没有必要冒风险去超频的。
E. 这里的 E 处是指其他所有外设了。从时钟图上可以看出,其他所有外设的时钟最终来源都是 SYSCLK。SYSCLK 通过 AHB 分频器分频后送给各模块使用。这些模块包括:
①、AHB 总线、内核、内存和 DMA 使用的 HCLK 时钟。
②、通过 8 分频后送给 Cortex 的系统定时器时钟,也就是 systick 了。
③、直接送给 Cortex 的空闲运行时钟 FCLK。
④、送给 APB1 分频器。APB1 分频器输出一路供 APB1 外设使用(PCLK1,最大频率 36MHz),另一路送给定时器(Timer)2、3、4 倍频器使用。
⑤、送给 APB2 分频器。APB2 分频器分频输出一路供 APB2 外设使用(PCLK2,最大频率 72MHz),另一路送给定时器(Timer)1 倍频器使用。

其中需要理解的是 APB1 和 APB2 的区别,APB1 上面连接的是低速外设,包括电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3 等等,APB2 上面连接的是高速外设包 括 UART1、SPI1、Timer1、ADC1、ADC2、所有普通 IO 口(PA~PE)、第二功能 IO 口等。居宁老师的《稀里糊涂玩 STM32》资料里面教大家的记忆方法是 2>1, APB2 下面所挂的外设的时钟要比 APB1 的高。

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

系统时钟配置

在系统启动之后,程序会先执行 HAL 库定义的 SystemInit 函数,进行系统一些初始化配置。 SystemInit 程序中主要做了以下三个方面的工作:
1) 复位 RCC 时钟配置为默认复位值(默认开始了 HIS)
2) 外部存储器配置
3) 中断向量表地址配置

HAL 库的 SystemInit 函数并没有像标准库的 SystemInit 函数一样进行时钟的初始化配置。HAL 库的 SystemInit 函数除了打开 HSI 之外,没有任何时钟相关配置,所以使用 HAL 库我们必须编 写自己的时钟配置函数。首先我们打开工程模板看看我们在工程 SYSTEM 分组下面定义的 sys.c中包含,也可以使用Cubemx自动生成配置代码,具体可以参考STM32Cubemx的总结手册。

//时钟系统配置函数 
//PLL:选择的倍频数,RCC_PLL_MUL2~RCC_PLL_MUL16 
//返回值:0,成功;1,失败 
void Stm32_Clock_Init(u32 PLL) 
{ 
HAL_StatusTypeDef ret = HAL_OK; 
RCC_OscInitTypeDef RCC_OscInitStructure; 
RCC_ClkInitTypeDef RCC_ClkInitStructure; 
RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE; //时钟源为 HSE 
RCC_OscInitStructure.HSEState=RCC_HSE_ON; //打开 HSE 
RCC_OscInitStructure.HSEPredivValue=RCC_HSE_PREDIV_DIV1; //HSE 预分频 
RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON;
//打开 PLL 
RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE; 
//PLL 时钟源选择 HSE 
RCC_OscInitStructure.PLL.PLLMUL=PLL; 
//主 PLL 倍频因子 
ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化 
if(ret!=HAL_OK) while(1); 
//选中 PLL 作为系统时钟源并且配置 HCLK,PCLK1 和 PCLK2 
RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK| 
RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1| 
RCC_CLOCKTYPE_PCLK2); 
//设置系统时钟时钟源为 PLL 
RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK; 
RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1; 
//AHB 分频系数为 1 
RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV2; 
//APB1 分频系数为 2 
RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV1; 
//APB2 分频系数为 1 
ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_2); 
//同时设置 FLASH 延时周期为 2WS,也就是 3 个 CPU 周期。 
if(ret!=HAL_OK) while(1); 
}

在sys.c文件中,有一个Stm32_Clock_Init()函数,这个函数里面是时钟的配置。除了配置 PLL 相关参数确定 SYSCLK 值之外,还配置了 AHB,APB1 和 APB2 的分频系数,也就是确定了HCLK,PCLK1 和 PCLK2 的时钟值。

typedef struct 
{ 
uint32_t OscillatorType; //需要选择配置的振荡器类型 
uint32_t HSEState; //HSE 状态 
uint32_t HSEPredivValue; // Prediv1 值 
uint32_t LSEState; //LSE 状态 
uint32_t HSIState; //HIS 状态
uint32_t HSICalibrationValue; //HIS 校准值 
uint32_t LSIState; 
//LSI 状态 
RCC_PLLInitTypeDef PLL; //PLL 配置 
}RCC_OscInitTypeDef; 

typedef struct 
{ 
uint32_t PLLState; //PLL 状态 
uint32_t PLLSource; //PLL 时钟源 
uint32_t PLLMUL; //PLL VCO 输入时钟的乘法因子 
}RCC_PLLInitTypeDef;

在这个函数中初始化一个结构体,关于RCC_OscInitTypeDef外部晶振的结构体,里面包含选择配置振荡器的类型。在这个结构体中有关于PLL的配置的成员变量,RCC_PLLInitTypeDef 结构体,里面又包含了PLL状态、 PLL时钟源、PLL输入时钟的乘法因子,这三个成员变量主要是设置PLL的分频倍频参数。

RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE; //时钟源为 HSE 
RCC_OscInitStructure.HSEState=RCC_HSE_ON; //打开 HSE 
RCC_OscInitStructure.HSEPredivValue=RCC_HSE_PREDIV_DIV1; //HSE 预分频 
RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON;
//打开 PLL 
RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE; 
//PLL 时钟源选择 HSE 
RCC_OscInitStructure.PLL.PLLMUL=PLL; 
//主 PLL 倍频因子 
ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化 

在时钟配置函数中开启了 HSE 时钟源,同时选择 PLL 时钟源为 HSE,然后把Stm32_Clock_Init 的唯一的入口参数直接设置作为 PLL 的倍频因子。设置好 PLL 时钟源参数之后,也就是确定了 PLL 的时钟频率,接下来我们就需要设置系统时钟,以及 AHB,APB1 和 APB2 相关参数。

HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, 
uint32_t FLatency); 

该函数有两个入口参数,第一个入口参数 RCC_ClkInitStruct 是结构体RCC_ClkInitTypeDef 指针类型,用来设置 SYSCLK 时钟源以及 AHB,APB1 和 APB2 的分频系数。第二个入口参数 FLatency 用来设置 FLASH 延迟。

//选中 PLL 作为系统时钟源并且配置 HCLK,PCLK1 和 PCLK2 
RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK| 
RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1| 
RCC_CLOCKTYPE_PCLK2);
//设置系统时钟时钟源为 PLL 
RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK; 
RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1; //AHB 分频系数为 1 
RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV2; //APB1 分频系数为 2 
RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV1; //APB2 分频系数为 1 
ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_2); 
//同时设置 FLASH 延时周期为 2WS,也就是 3 个 CPU 周期。 

第一个参数 ClockType 配置说明我们要配置的是 SYSCLK,HCLK,PCLK1 和 PCLK2 四个时钟。
第二个参数 SYSCLKSource 配置选择系统时钟源为 PLL。
第三个参数 AHBCLKDivider 配置 AHB 分频系数为 1。
第四个参数 APB1CLKDivider 配置 APB1 分频系数为 2。
第五个参数 APB2CLKDivider 配置 APB2 分频系数为 1。
根据我们在主函数中调用 Stm32_Clock_Init(RCC_PLL_MUL9)时候设置的入口参数值,我 们可以计算出,PLL 时钟为 PLLCLK=HSE9 =8MHz9=72MHz,同时我们选择系统时钟源为 PLL , 所 以 系 统 时 钟 SYSCLK=72MHz 。 AHB 分频系数为 1 ,故其频率为 HCLK=SYSCLK/1=72MHz。APB1 分频系数为 2,故其频率为 PCLK1=HCLK/2=36MHz。APB2 分频系数为 1,故其频率为 PCLK2=HCLK/1=72/1=72MHz。最后我们总结一下通过调用函数 Stm32_Clock_Init(RCC_PLL_MUL9)之后的关键时钟频率值:
SYSCLK(系统时钟) =72MHz
PLL 主时钟 =72MHz
AHB 总线时钟(HCLK=SYSCLK/1) =72MHz
APB1 总线时钟(PCLK1=HCLK/2) =36MHz
APB2 总线时钟(PCLK2=HCLK/1) =72MHz

系统时钟使能配置

在 STM32F1 的 HAL 库中,外设时钟使能操作都是在 RCC 相关固件库文件头文件 stm32f1xx_hal_rcc.h 定义的。大家打开stm32f1xx_hal_rcc.h 头文件可以看到文件中除了少数几 个函数声明之外大部分都是宏定义标识符。外设时钟使能在 HAL 库中都是通过宏定义标识符来实现的。我们的宏是__HAL_RCC_GPIOA_CLK_ENABLE(),宏定义里面有SET_BIT()函数,SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN); 意思很清楚哈,里面是寄存器配置APB2ENR,后面是配置的位,是一个宏

#define RCC_APB2ENR_IOPAEN ((uint32_t)0x00000001) 

作用是设置寄存器 RCC->APB2ENR 寄存器的位 2 为 1。我们可以从 STM32F1 的中文参考手册中搜索 APB2ENR 寄存器定义,位 2 的作用是用来使用 GPIOA 时钟。而配置描述如下:
IOPAEN:IO 端口 A 时钟使能
由软件置 1 和清零
0:禁止 IO 端口 A 时钟
1:使能 IO 端口 A 时钟
那么我们对各个时钟的使能就可以用以下方式表述:

__HAL_RCC_GPIOA_CLK_ENABLE();//使能 GPIOA 时钟 
__HAL_RCC_DMA1_CLK_ENABLE();//使能 DMA1 时钟 
__HAL_RCC_USART2_CLK_ENABLE();//使能串口 2 时钟 
__HAL_RCC_TIM1_CLK_ENABLE();//使能 TIM1 时钟

我们使用外设的时候需要使能外设时钟,如果我们不需要使用某个外设,同样我们可以禁止某个外设时钟。禁止外设时钟使用方法和使能外设时钟非常类似,同样是头文件中定义的宏定义标识符。我们同样以 GPIOA 为例,宏定义标识符为:

#define __HAL_RCC_GPIOA_CLK_DISABLE() \ 
(RCC->APB2ENR &= ~(RCC_APB2ENR_IOPAEN)) 

禁止外设时钟的设置为:

__HAL_RCC_DMA1_CLK_DISABLE();//禁止 DMA1 时钟 
__HAL_RCC_USART2_CLK_DISABLE();//禁止串口 2 时钟 
__HAL_RCC_TIM1_CLK_DISABLE();//禁止 TIM1 时钟

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1216098.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MATLAB与Excel的数据交互

准备阶段 clear all % 添加Excel函数 try Excel=actxGetRunningServer(Excel.Application); catch Excel=actxserver(Excel.application); end % 设置Excel可见 Excel.visible=1; 插入数据 % % 激活eSheet1 % eSheet1.Activate; % 或者 % Activate(eSheet1); % % 打开…

springboot使用MongoTemplate根据正则表达式查询日期数据

一、日期正则表达式测试 匹配HH:mm:ss正则表达式写法有很多列举两个 .(点)代表任意匹配 ^开头必须是跟着的 : 精确匹配,必须是: ([0-1]?[0-9]|2[0-3]).([0-5][0-9]).([0-5][0-9]) ^([0-1]?[0-9]|2[0-3]).([0-5][0-9]).([0-5][0-9])$ ([0-1]?[0-9]|2[0-3]):([0-5][0-9]):…

【机器学习9】前馈神经网络

深度前馈网络是一类网络模型的统称,主要包括多层感知机、 自编码器、限制玻尔兹曼机, 以及卷积神经网络等。 1 激活函数 激活函数及对应导函数图其它Sigmoid 导数 在z很大或很小时都会趋近于0, 造成梯度消失的现象Tanh 其导数在z很大或很小…

Python接口自动化(什么是接口、接口优势、类型)

简介 经常听别人说接口测试,接口测试自动化,但是你对接口,有多少了解和认识,知道什么是接口吗?它是用来做什么的,测试时候要注意什么?坦白的说,笔者之前也不是很清楚。接下来先看一下…

模拟实现一个Linux中的简单版shell

exec系列接口中的环境变量 在之前我们学习了exec系类函数的功能就是将一个程序替换成另外一个程序。 然后就会出现下面的问题: 首先父进程对应的环境变量的信息是从bash中来的,因为我们自己写的父进程在运行的时候首先就要成为bash的子进程。这里我们将…

解决计算机丢失msvcr71.dll问题,总结5种解决方法分享

由于各种原因,计算机在使用的过程中可能会出现一些问题,其中之一就是丢失msvcr71.dll文件。这个问题可能会导致计算机无法正常运行某些程序或功能,给我们的生活和工作带来困扰。那么,当我们遇到这个问题时,应该如何解决…

福利来了,运营素材免费下载

各位运营的小伙伴,是不是在日常工作中常常用到这种场景:公司要做一个活动,老板让你写一个活动SOP,但是没有过往经验,一时无从下手,老板又死催。 自己想了解拉新的办法,但是一时找不到资料&…

双十一快递业务量暴增,快递驿站视频智能监控方案保障快递业务顺利开展

一、背景分析 虽然刚刚过去的双十一电商购物狂潮结束,但是快递业务量仍处在高峰期。据数据统计,今年全国邮政快递企业在11月11日当天共揽收快递包裹6.39亿件,是平日业务量的1.87倍,同比增长15.76%。随着电商购物节的不断增多&…

【Qt之QWizardPage】使用

介绍 QWizardPage类是向导页面的基类。 QWizard表示一个向导。每个页面都是一个QWizardPage。当创建自己的向导时,可以直接使用QWizardPage,也可以子类化它以获得更多控制。 页面具有以下属性,由QWizard呈现:a title,…

易点易动库存管理系统:革新企业库存管理,降本增效

在快速变化的市场环境中,企业面临着库存管理的巨大挑战。传统的库存管理方式耗时耗力,且常常因为信息滞后、数据不精确而导致库存积压或短缺。易点易动库存管理系统(以下简称“易点易动”)的出现,标志着企业库存管理进…

前端性能优化的方式

文章目录 前言DNS 预解析存储使用 HTTP / 2.0预加载预渲染懒执行与懒加载文件优化webpack优化如何根据chrome的timing优化移动端优化后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:前端系列文章 🐱‍👓博主在前端…

Linux进程之进程的状态简述

文章目录 1.百度搜索2.对进程状态的认识2.0创建状态2.1就绪状态2.2运行状态2.3阻塞状态2.4挂起状态 3.认识LinuxOS下的进程3.0进程状态的简述3.1了解R/S状态3.2D深度睡眠状态3.3信号/调试暂停状态3.4僵尸状态 1.百度搜索 2.对进程状态的认识 一个进程所具有的状态为操作系统的…

demo(三)eurekaribbonhystrix----服务降级熔断

一、介绍: 1、雪崩: 多个微服务之间调用的时候,假如微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的"扇出"。如果扇出的链路上某个微服务的调用响应的时间过长或者不可用&am…

开发者的第一台服务器 ECS云服务器低至99元:新老同享

“阿里云始终围绕‘稳定、安全、性能、成本、弹性’的目标不断创新,为客户创造业务价值。”10月31日,杭州云栖大会上,阿里云弹性计算计算产品线负责人张献涛表示,通过持续的产品和技术创新,阿里云发布了HPC优化实例等多…

Kubernetes(k8s)进阶

文章目录 Kubernetes进阶一、Namespace(名称空间)1.namespace介绍2.管理namespace查看namespace创建namespaceyaml文件配置namespace 二、Pod(最小基本部署单元)1.pod介绍2.管理pod创建并运行pod查看pod信息访问pod删除podyaml文件…

如何分析伦敦金的价格走势预测?

伦敦金作为国际黄金市场的重要指标,其价格走势一直备受投资者关注。但是,黄金市场的价格变化受到多种因素的影响,因此要准确预测伦敦金的价格走势并非易事。在本文中,将介绍一些常用的方法和工具,帮助您分析伦敦金的价…

挖掘PostgreSQL事务的“中间态”----更加严谨的数据一致性?

1.问题 今天在上班途中,中心的妹纸突然找我,非常温柔的找我帮忙看个数据库的报错。当然以我的性格,妹子找我的事情对我来说优先级肯定是最高的,所以立马放下手中的“小事”,转身向妹子走去。具体是一个什么样的问题呢…

vue部署之后提示用户更新的两种方式(http请求和worker线程请求)

const { writeFile, mkdir, existsSync } require(fs) // 动态生成版本号 const createVersion () > {// mkdir(./dist, { recursive: true }, (err) > {//检测dist目录是否存在if (existsSync(./dist)) {writeFile(./dist/version.json, {"version":"$…

【vue】0到1的常规vue3项目起步

创建项目并整理目录 npm init vuelatestjsconfig.json配置别名路径 配置别名路径可以在写代码时联想提示路径 {"compilerOptions" : {"baseUrl" : "./","paths" : {"/*":["src/*"]}} }elementPlus引入 1. 安装e…

新增文件收藏夹、回收站、终端等功能,1Panel开源面板v1.8.0发布

2023年11月13日,现代化、开源的Linux服务器运维管理面板1Panel正式发布v1.8.0版本。 在这一版本中,1Panel新增文件收藏夹、回收站、终端功能,面板设置时支持设置面板监听地址。此外,1Panel开源项目组还进行了60多项功能更新和问题…