打印
[其他ST产品]

c语言延时函数delay_<STM32>HAL_Delay功能实现

[复制链接]
275|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lvuu|  楼主 | 2023-12-26 17:00 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
前言:本文章目的是透过ST官方固件包(STM32CubeF1 firmware package)内的参考手册(Reference manual)UM1847与STM32F10xxx家族芯片的参考手册RM0008,分析HAL库是如何初始化时钟的。

分析这问题的原因有:

  • 时钟树是整个系统中十分关键的部分,没有时钟,其他模块就无法正常运作。
  • HAL_Delay(uint32_t) 延迟功能函数为什么能在不同系统时钟(SYSCLK)情况下提供相同的延时功能。
HAL库文件的组成部分

想分析HAL库从启动到初始化时钟的整个流程,就需要先了解HAL库文件的组成部分。

在STM32CubeF1的固件库文件的文档中,就提供了下图关于STM32CubeF1 firmware package structure(固件包的文件结构示意图)

从图中给出的信息中可以知道的是,一个工程文件中(一般由STM32CubeMX自动生成)"Drivers"目录内的包括了BSP(板级支持包)、CMSIS(微控制器软件接口标准)、HAL库和LL库。"Drivers"可以理解为C语言中的库文件,里面包含了ST提供的各种功能函数。而"Middlewares"中间层则是算应用层,提供给需要相关应用开发的开发者使用。




使用特权

评论回复
沙发
lvuu|  楼主 | 2023-12-26 17:00 | 只看该作者
在工程文件中,除了上述的库文件外,还有3个比较重要的文件:main.c(主程序文件)、system_stm32f1xx.c(CMSIS标准接口的入口文件)、startup_stm32f103rbtx.s(芯片的启动文件)。其中,后两个是特定芯片的(本文是基于STM32F103RBT6)文件,但对应不同芯片的功能基本是一致的,可以打开对应的文件看里面的@brief(简述)就基本了解该文件的功能了。下面简单例举该文件都做了些什么事情:

startup_stm32f103rbtx.s

使用特权

评论回复
板凳
lvuu|  楼主 | 2023-12-26 17:00 | 只看该作者
初始化设置SP(堆栈指针)
初始化设置PC(程序指针)
设置中断向量表(中断入口地址)
配置时钟系统
进入main主程序

使用特权

评论回复
地板
lvuu|  楼主 | 2023-12-26 17:01 | 只看该作者
system_stm32f1xx.c SystemInit() 初始化系统时钟函数——该功能是在"startup_stm32f1xx_xx.s"文件中调用
在还未进入main主程序前,主要涉及的的文件就是上面这两个针对特定芯片的配置文件。这里还未涉及到主要的配置时钟树的过程,这里是在为后面使用HAL库做前期的必要准备(类似于OSI模型的最底层,目的是服务于上层,对于上层HAL库来说下层是透明的,实现HAL库编写的程序代码可以简单的移植到不同芯片中)

使用特权

评论回复
5
lvuu|  楼主 | 2023-12-26 17:01 | 只看该作者
时钟树(Clock tree)——相关配置HAL库函数 接触STM32系列芯片的的不会对该时钟树图陌生,这里做了点简单的标注,辅助理解时钟是怎么经过分频、倍频、选择器最后被系统时钟SYSCLK选用的。主要突出了STM32有5个时钟源,分别是HSE、HSI、LSI、LSE和PLLCLK(PLL由HSI或HSE提供输入源)。SYSCLK(系统时钟)则是来源于HSI、PLLCLK或HSE其中一个来提供,最大支持72MHz。

使用特权

评论回复
6
lvuu|  楼主 | 2023-12-26 17:03 | 只看该作者
配置时钟的HAL库文件——stm32f1xx_hal_rcc.c、stm32f1xx_hal_rcc_ex.c 注意:使用HAL库函数前,必须在main函数中先对HAL库进行初始化(HAL_Init()函数)。另外,如果在STM32CubeMX配置中没有进行初始化配置,而自己编写代码调用HAL库来进行初始化配置时,需要在"stm32f1xx_hal_conf.h"文件中的"Module Selection"中,通过取消相关HAL库的注释开启支持。

使用特权

评论回复
7
lvuu|  楼主 | 2023-12-26 17:03 | 只看该作者
stm32f1xx_hal_rcc.c => HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct)

使用特权

评论回复
8
lvuu|  楼主 | 2023-12-26 17:03 | 只看该作者
HAL_RCC_OscConfig函数——生成SYSCLK(系统时钟源)

RCC_OscInitTypeDef结构体成员
OscillatorType: 选择配置目标Oscillators(晶振)
HSEState: 高速外部时钟开启或关闭
HSEPredivValue: HSE预分频(CLK TREE中的PLLXTPRE)
LSEState: 低速外部时钟开启或关闭
HSIState: 高速内部时钟开启或关闭
HSICalibrationValue: 一般设置默认值: RCC_HSICALIBRATION_DEFAULT
LSIState: 低速内部时钟开启或关闭
PLL:
PLLState: PLLCLK开启或关闭
PLLSource: PLLSRC选择器(HSE或HSI/2)
PLLMUL: PLLMUL倍频器倍率(2-16)

使用特权

评论回复
9
lvuu|  楼主 | 2023-12-26 17:06 | 只看该作者
注意:HSE的开启再分为External source (HSE bypass)和External crystal/ceramic resonator (HSE crystal) 具体信息见RM0008文档7.2.1HSE Clock。

使用特权

评论回复
10
lvuu|  楼主 | 2023-12-26 17:06 | 只看该作者

使用特权

评论回复
11
lvuu|  楼主 | 2023-12-26 17:06 | 只看该作者
stm32f1xx_hal_rcc.c => HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)

使用特权

评论回复
12
lvuu|  楼主 | 2023-12-26 17:15 | 只看该作者
HAL_RCC_ClockConfig函数——配置CPU、AHB(APB2)、APB(APB1)

RCC_ClkInitTypeDef结构体成员
ClockType: 选择配置时钟目标
SYSCLKSource: SYSCLK的时钟源(HSI、PLLCLK、HSE)
AHBCLKDivider: AHP Prescaler(/1, 2, ...512)分频
APB1CLKDivider: APB1 Prescaler(/1, 2, 4, 8, 16)分频
APB2CLKDivider: APB2 Prescaler(/1, 2, 4, 6, 16)分频
注意:HAL_RCC_ClockConfig函数中还有一个参数FLatency,其值取决于SYSCLK频率(stm32f1xx_hal_rcc.c中描述)

使用特权

评论回复
13
lvuu|  楼主 | 2023-12-26 17:15 | 只看该作者
只需要在主函数中通过结构体配置好相关参数,再调用HAL_RCC_OscConfig()和 HAL_RCC_ClockConfig()就完成了整体时钟树的配置。

使用特权

评论回复
14
lvuu|  楼主 | 2023-12-26 17:15 | 只看该作者
总结分析——HAL_Delay()的实现过程
通过逐步剖析函数,有个大概宏观认知HAL库是如何配置时钟树的就已经足够,因为现在已经不需要再要求用户编写初始化配置代码,交给STM32CubeMX应用程序完成即可。

回到文章开头,分析从启动到整个时钟配置过程,一方面是更具体了解STM32的Clock Tree配置过程,及整体的时钟架构,另一方面也是熟悉HAL库。还有一点就是想解决第二个问题,HAL_Delay()是怎样实现在不同SYSCLK下具实现相同功能的?好像剖析至此都没得到答案,但其实在这过程中HAL库已经在用户无感间,通过配置SysTick(AHB经过8分频后的Cortex system timer)生成1毫秒中断(HAL_Delay通过SysTick定时中断实现)。SysTick配置是在HAL_InitTick()函数中完成。

使用特权

评论回复
15
lvuu|  楼主 | 2023-12-26 17:15 | 只看该作者
在HAL_InitTick的@note中明确写出了该函数会在HAL_Init()和通过HAL_RCC_ClockConfig()配置时钟时调用,再结合HAL_Init()的解释。

使用特权

评论回复
16
lvuu|  楼主 | 2023-12-26 17:16 | 只看该作者
"Configures the SysTick to generate an interrupt each 1 millisecond"问题迎刃而解。

后话:探索HAL_Delay()延迟函数如何实现的,本以为是个很轻易的过程。但实际却从启动文件->初始化HAL库->配置Clock Tree(时钟树)的分析过程中才得到线索。最终才从较宏观的层面知道是在系统时钟配置过程中完成SysTick定时器的初始化——默认配置为1ms延迟中断。HAL_Delay()函数正是借助着SysTick定时器中断实现的,从中也知道HAL_Delay()函数使用过程中需要注意的地方(需要借助中断)。文章内容看起来是零零散散的(可能还有点杂乱无章),主要原因还是本意是想作为个学习笔记之类的方式记录思考的过程,同时也分享下分析问题时的思路。在面对其他类似问题时,利用本文的分析方法相信也能得到解决方法。这也是我想分享给阅读者文章的人——多点自己的思考分析过程。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

62

主题

489

帖子

0

粉丝