c语言延时函数delay_<STM32>HAL_Delay功能实现——从启动到初始化时钟树的过程中寻找答案...

前言:本文章目的是透过ST官方固件包(STM32CubeF1 firmware package)内的参考手册(Reference manual)UM1847与STM32F10xxx家族芯片的参考手册RM0008,分析HAL库是如何初始化时钟的。

分析这问题的原因有:

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

HAL库文件的组成部分

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

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

f3c7fd2d92f7a7d198fc6eca856027ce.png

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

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

  • startup_stm32f103rbtx.s

6b51330db8024c6f91ae6dd52b9bd36a.png
    • 初始化设置SP(堆栈指针)
    • 初始化设置PC(程序指针)
    • 设置中断向量表(中断入口地址)
    • 配置时钟系统
    • 进入main主程序
  • system_stm32f1xx.c

a614095aaf61b07639966066fb70aea5.png
    • SystemInit() 初始化系统时钟函数——该功能是在"startup_stm32f1xx_xx.s"文件中调用

在还未进入main主程序前,主要涉及的的文件就是上面这两个针对特定芯片的配置文件。这里还未涉及到主要的配置时钟树的过程,这里是在为后面使用HAL库做前期的必要准备(类似于OSI模型的最底层,目的是服务于上层,对于上层HAL库来说下层是透明的,实现HAL库编写的程序代码可以简单的移植到不同芯片中)

时钟树(Clock tree)——相关配置HAL库函数

91aeebf4bd5c19962c9c8f7189062571.png

接触STM32系列芯片的的不会对该时钟树图陌生,这里做了点简单的标注,辅助理解时钟是怎么经过分频、倍频、选择器最后被系统时钟SYSCLK选用的。主要突出了STM32有5个时钟源,分别是HSE、HSI、LSI、LSE和PLLCLK(PLL由HSI或HSE提供输入源)。SYSCLK(系统时钟)则是来源于HSI、PLLCLK或HSE其中一个来提供,最大支持72MHz。

  • 配置时钟的HAL库文件——stm32f1xx_hal_rcc.c、stm32f1xx_hal_rcc_ex.c

v2-2e26c48c98e8896514472efdc48f6af0_b.jpg

bce6a939ce2431b9b59248c67e55d601.png

注意:使用HAL库函数前,必须在main函数中先对HAL库进行初始化(HAL_Init()函数)。另外,如果在STM32CubeMX配置中没有进行初始化配置,而自己编写代码调用HAL库来进行初始化配置时,需要在"stm32f1xx_hal_conf.h"文件中的"Module Selection"中,通过取消相关HAL库的注释开启支持。

  • stm32f1xx_hal_rcc.c => HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct)

a8897ff7c4b44a811e134a543ee253a1.png

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)

注意:HSE的开启再分为External source (HSE bypass)和External crystal/ceramic resonator (HSE crystal) 具体信息见RM0008文档7.2.1HSE Clock。

b3e95a05adba602ed1592689938c8580.png
  • stm32f1xx_hal_rcc.c => HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)

940d1e1df98f591c77023158359df8bf.png

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中描述)

5ac2087516f6557ca238e6eef7037288.png

只需要在主函数中通过结构体配置好相关参数,再调用HAL_RCC_OscConfig()和 HAL_RCC_ClockConfig()就完成了整体时钟树的配置。

总结分析——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()函数中完成。

c90ec19e4923602e9fe5b59e71f122d8.png

在HAL_InitTick的@note中明确写出了该函数会在HAL_Init()和通过HAL_RCC_ClockConfig()配置时钟时调用,再结合HAL_Init()的解释。

bf819f541d052f0a1dbcb261516102c5.png

"Configures the SysTick to generate an interrupt each 1 millisecond"问题迎刃而解。


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

后面估计还会写一篇关于STM32CubeIDE(MacOS)入门使用指南。有兴趣的可以关注期待下(我这文笔...可能也没谁想看吧...Anyway, 主要还是总结笔记同时提供思路)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值