首页 > 其他分享 >02. 初识HAL库

02. 初识HAL库

时间:2024-02-20 18:36:00浏览次数:32  
标签:02 定时器 HAL 函数 中断 滴答 初识 外设

一、初识HAL库

  STM32 开发中常说的 HAL 库开发,指的是利用 HAL 库固件包里封装好的 C 语言编写的驱动文件,来实现对 STM32 内部和外围电器元件的控制的过程。但只有 HAL 库还不能直接驱动一个 STM32 的芯片,其它的组件已经由 ARM 与众多芯片硬件、软件厂商制定的通用的软件开发标准 CMSIS 实现了。

  因为基于 Cortex 系列芯片采用的内核都是相同的,区别主要为核外的片上外设的差异,这些差异却导致软件在同内核,不同外设的芯片上移植困难。为了解决不同的芯片厂商生产的 Cortex 微控制器软件的兼容性问题,ARM 与芯片厂商建立了 CMSIS 标准 (Cortex MicroController Software Interface Standard)。所谓 CMSIS 标准,实际是新建了一个软件抽象层。

CorteX芯片的CMSIS分级实现

  从图中可以看出这个标准分级明显,从用户程序到内核底层实现做了分层。按照这个分级,HAL 库属于 CMSIS-Pack 中的 “Peripheral HAL” 层。CMSIS 规定的最主要的 3 个部分为:核内外设访问层(由 ARM 负责实现)、片上外设访问层和外设访问函数(后面两个由芯片厂商负责实现)。ARM 整合并提供了大量的模版,各厂商根据自己的芯片差异修改模版,这其中包括汇编文件 startup_device.s、system_.h 和 system_.c 这些与初始化和系统相关的函数。

CMSIS分级下的STM32F4的文件分布

二、HAL库简介

  库函数的引入,大大降低 STM 系列主控芯片开发的难度。ST 公司为了方便用户开发 STM32 芯片开发提供了三种库函数,从时间产生顺序是:标准库、HAL 库和 LL 库。目前 ST 已经逐渐暂停对部分标准库的支持,ST 的库函数维护重点对角已经转移到 HAL 库和 LL 库上。

【1】、标准外设库(Standard Peripheral Libraries)

  标准外设库(Standard Peripherals Library)是对 STM32 芯片的一个完整的封装,包括所有标准器件外设的器件驱动器,是 ST 最早推出的针对 STM 系列主控的库函数。标准库的设计的初衷是减少用户的程序编写时间,进而降低开发成本。几乎全部使用 C 语言实现并严格按照 Strict ANSI-C、MISRA-C 2004 等多个 C 语言标准编写。但标准外设库仍然接近于寄存器操作,主要就是将一些基本的寄存器操作封装成了 C 函数。开发者仍需要关注所使用的外设是在哪个总线之上,具体寄存器的配置等底层信息。

  ST 为各系列提供的标准外设库稍微有些区别。此外,在内部的实现上也稍微有些区别,这个在具体使用(移植)时,需要注意一下!但是,不同系列之间的差别并不是很大,而且在设计上是相同的。

【2】、HAL 库(Hardware Abstraction Layer)

  HAL 是 Hardware Abstraction Layer 的缩写,即硬件抽象层。是 ST 为可以更好的确保跨 STM32 产品的最大可移植性而推出的 MCU 操作库。这种程序设计由于抽离应用程序和硬件底层的操作,更加符合跨平台和多人协作开发的需要。HAL 库是基于一个非限制性的 BSD 许可协议(Berkeley Software Distribution)而发布的开源代码。

【3】、LL 库(Low Layer)

  LL 库(Low Layer)目前与 HAL 库捆绑发布,它的设计为比 HAL 库更接近于硬件底层的操作,代码更轻量级,代码执行效率更高的库函数组件,可以完全独立于 HAL 库来使用,但 LL 库不匹配复杂的外设,如 USB 等。所以 LL 库并不是每个外设都有对应的完整驱动配置程序。

三、HAL库框架结构

  HAL 库头文件和源文件在 STM32Cube 固件包的 STM32F4xx_HAL_Driver 文件夹中。STM32F407xx_HAL_Driver 文件夹下的 Src(Source 的简写)文件夹存放是所有外设的驱动程序源码,Inc(Include 的简写)文件夹存放的是对应源码的头文件。Release_Notes.html 是HAL 库的版本更新信息。

  打开 Src 和 Inc 文件夹,大家会发现基本都是 stm32f4xx_hal_和 stm32f4xx_ll_开头的 .c 和 .h文件。stm32f4xx_hal_开头的是 HAL 库,stm32f4xx_ll_开头的文件 LL 库的文件。

HAL库文件

  stm32f4xx_hal_ppp (c/h)中的函数和变量命名也严格按照命名规则。

HAL库函数变量命名规则

  对于 HAL 的 API 函数,常见的有以下几种:

  • 初始化/反初始化函数HAL_PPP_Init(), HAL_PPP_DeInit()
  • 外设读写函数HAL_PPP_Read(), HAL_PPP_Write(), HAL_PPP_Transmit(), HAL_PPP_Receive()
  • 控制函数HAL_PPP_Set(), HAL_PPP_Get()
  • 状态和错误HAL_PPP_GetState(), HAL_PPP_GetError()

  HAL 库封装的很多函数都是通过定义好的结构体将参数一次性传给所需函数,参数也有一定的规律,主要有以下三种:

  • 配置和初始化用的结构体:一般为 PPP_InitTypeDefPPP_ConfTypeDef 的结构体类型,根据外设的寄存器设计成易于理解和记忆的结构体成员。
  • 特殊处理的结构体:专为不同外设而设置的,带有 Process 的字样,实现一些特异化的中间处理操作等。
  • 外设句柄结构体:HAL 驱动的重要参数,可以同时定义多个句柄结构以支持多外设多模式。HAL 驱动的操作结果也可以通过这个句柄获得。有些 HAL 驱动的头文件中还定义了一些跟这个句柄相关的一些外设操作。如用外设结构体句柄与 HAL 定义的一些宏操作配合,即可实现一些常用的寄存器位操作。

HAL库驱动部分与外设句柄相关的宏

  HAL 库的回调函数,这部分允许用户重定义,并在其中实现用户自定义的功能,也是我们使用 HAL 库的最常用的接口之一。

HAL库驱动中常用的回调函数接口

四、HAL库的用户配置文件

  stm32f4xx_hal_conf.h 用于裁剪 HAL 库和定义一些变量,官方没有直接提供这个文件,但在 STM32Cube_FW_F4_V1.26.0\Drivers\STM32F4xx_HAL_Driver\Inc 这个路径下提供了一个模版文件《 stm32f4xx_hal_conf_template.h 》,我们可以直接复制这个文件重命名为 stm32f4xx_hal_conf.h,做一些简单的修改即可。

  stm32f4xx_hal_conf.h 文件 84 行左右,有个 HSE_VALUE 参数,这个参数表示我们的外部高速晶振的频率。这个参数请务必根据我们板子外部焊接的晶振频率来修改,官方默认是 25M。这里,我们使用的开发板,晶振频率为 8MHz。

#if !defined (HSE_VALUE) 
    /* 外部高速振荡器的值,单位 HZ */
    #define HSE_VALUE ((uint32_t)8000000) 
#endif /* HSE_VALUE */

  源码在 112 行左右,还有一个参数就是外部低速晶振频率,这个官方默认是 32.768KHZ。

#if !defined (LSE_VALUE)
    /* 外部低速振荡器的值,单位 HZ */
    #define LSE_VALUE ((uint32_t)32768) 
#endif /* LSE_VALUE */

  用户配置文件可以用来选择使能何种外设,源码配置在 35 行到 74 行。我们只要屏蔽某个外设的宏,则这个外设的驱动代码机会被屏蔽,从而不可用。

#define HAL_MODULE_ENABLED
#define HAL_ADC_MODULE_ENABLED
#define HAL_CAN_MODULE_ENABLED
/* #define HAL_CAN_LEGACY_MODULE_ENABLED */
#define HAL_CRC_MODULE_ENABLED
/* ...中间省略... */
#define HAL_WWDG_MODULE_ENABLED
#define HAL_CORTEX_MODULE_ENABLED
#define HAL_PCD_MODULE_ENABLED
#define HAL_HCD_MODULE_ENABLED

  stm32f4xx_hal_conf.h 文件的 136 行左右,宏定义 TICK_INT_PRIORITY 是滴答定时器的优先级。这个优先级很重要,因为如果其它的外设驱动程序的延时是通过滴答定时器提供的时间基准,来实现延时的话,又由于实现方式是滴答定时器对寄存器进行计数,所以当我们在其它中断服务程序里调用基于此时间基准的延迟函数 HAL_Delay,那么假如该中断的优先级高于滴答定时器的优先级,就会导致滴答定时器中断服务函数一直得不到运行,从而程序卡死在这里。所以滴答定时器的中断优先级一定要比这些中断高。

/*!< tick interrupt priority */
#define TICK_INT_PRIORITY ((uint32_t)0x0F) 

这个时间基准可以是滴答定时器提供,也可以是其他的定时器,默认是用滴答定时器。

五、HAL库初始化文件

  这个文件内容比较多,包括 HAL 库的初始化、系统滴答、基准电压配置、IO 补偿、低功耗、EXTI 配置等都集合在这个文件里面。

  HAL 库初始化函数的源码在 157 行到 183 行,简化函数如下(下面的代码只针对 F4 的 HAL 固件 1.26.0 版本,其它版本可能有差异):

HAL_StatusTypeDef HAL_Init(void)
{
    /* 设置中断优先级分组 */
     HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

     /* 使用滴答定时器作为时钟基准,配置 1ms 滴答(重置后默认的时钟源为 HSI) */
     if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)
     {
         return HAL_ERROR;
     }

     /* 初始化硬件 */
     HAL_MspInit();

     /* 返回函数状态 */
     return HAL_OK;
}

  该函数是 HAL 库的初始化函数,在程序中必须优先调用,其主要实现如下功能:

  • 设置 NVIC 优先级分组为 4
  • 配置滴答定时器每 1ms 产生一个中断
  • 在这个阶段,系统时钟还没有配置好,因此系统还是默认使用内部高速时钟源 HSI 在跑程序。对于 F4 来说,HSI 的时钟频率是 16MHZ。所以如果用户不配置系统时钟的话,那么系统将会使用 HSI 作为系统时钟源
  • 调用 HAL_MspInit 函数初始化底层硬件,HAL_MspInit 函数在 stm32f4xx_hal.c 文件里面做了弱定义。

  在源码在 253 行到 274 行,有 HAL_InitTick() 函数简化函数如下:

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
    /* 配置滴答定时器 1ms 产生一次中断 */
    if (HAL_SYSTICK_Config(SystemCoreClock / (1000UL / (uint32_t)uwTickFreq)) > 0U)
    {
        return HAL_ERROR;
    }
    /* 配置滴答定时器中断优先级 */
    if (TickPriority < (1UL << __NVIC_PRIO_BITS))
    {
        HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
        uwTickPrio = TickPriority;
    }
    else
    {
        return HAL_ERROR;
    }
  
    /* 返回函数状态 */
    return HAL_OK;
}

  该函数用于初始化滴答定时器的时钟基准,主要功能如下:

  • 配置滴答定时器 1ms 产生一次中断
  • 配置滴答定时器的中断优先级。
  • 该函数是__weak 定义的 “弱函数”,用户可以重新定义该函数。

  该函数可以通过 HAL_Init() 或者 HAL_RCC_ClockConfig() 重置时钟。在默认情况下,滴答定时器是时间基准的来源。如果其他中断服务函数调用了 HAL_Delay(),必须小心,滴答定时器中断必须具有比调用了 HAL_Delay()函数的其他中断服务函数的优先级高(数值较低),否则会导致滴答定时器中断服务函数一直得不到执行,从而卡死在这里。

  滴答定时器相关的函数的源码在 303 行到 435 行,相关函数如下:

/* 该函数在滴答定时器时钟中断服务函数中被调用,一般滴答定时器 1ms 中断一次,
所以函数每 1ms 让全局变量 uwTick 计数值加 1 */
__weak void HAL_IncTick(void)
{
    uwTick += (uint32_t)uwTickFreq;
}

/* 获取全局变量 uwTick 当前计算值 */
__weak uint32_t HAL_GetTick(void)
{
    return uwTick;
}

/* 获取滴答时钟优先级 */
uint32_t HAL_GetTickPrio(void)
{
    return uwTickPrio;
}

/* 设置滴答定时器中断频率 */
HAL_StatusTypeDef HAL_SetTickFreq(HAL_TickFreqTypeDef Freq)
{
    HAL_StatusTypeDef status = HAL_OK;
    HAL_TickFreqTypeDef prevTickFreq;
    assert_param(IS_TICKFREQ(Freq));
    if (uwTickFreq != Freq)
    {
        /* 备份滴答定时器中断频率 */
        prevTickFreq = uwTickFreq;
        /* 更新被 HAL_InitTick()调用的全局变量 uwTickFreq */
        uwTickFreq = Freq;
        /* 应用新的滴答定时器中断频率 */
        status = HAL_InitTick(uwTickPrio);
        if (status != HAL_OK)
        {
            /* 恢复之前的滴答定时器中断频率 */
            uwTickFreq = prevTickFreq;
        }
    }
    return status;
}

/* 获取滴答定时器中断频率 */
HAL_TickFreqTypeDef HAL_GetTickFreq(void)
{
    return uwTickFreq;
}

/*HAL 库的延时函数,默认延时单位 ms */
__weak void HAL_Delay(uint32_t Delay)
{
    uint32_t tickstart = HAL_GetTick();
    uint32_t wait = Delay;
    /* Add a freq to guarantee minimum wait */
    if (wait < HAL_MAX_DELAY)
    {
        wait += (uint32_t)(uwTickFreq);
    }
    while ((HAL_GetTick() - tickstart) < wait)
    {
    }
}

/* 挂起滴答定时器中断,全局变量 uwTick 计数停止 */
__weak void HAL_SuspendTick(void)
{
    /* 禁止滴答定时器中断 */
    SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
}

/* 恢复滴答定时器中断,恢复全局变量 uwTick 计数 */
__weak void HAL_ResumeTick(void)
{
    /* 使能滴答定时器中断 */
    SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
}

六、HAL库中断处理

  中断是 STM32 开发的一个很重要的概念,这里我们可以简单地理解为:STM32 暂停了当前手中的事并优先去处理更重要的事务。而这些 “更重要的事务” 是由软件开发人员在软件中定义的。

HAL驱动中断处理流程

  设置外设的控制句柄结构体 PPP_HandleType 和初始化 PPP_InitType 结构体的参数,然后调用 HAL 库对应这个驱动的初始化 HAL_PPP_Init(),由于这个 API 中有针对外设初始化细节的接口 HAL_PPP_Msp_Init(),我们需要重新实现这个函数并完成外设时钟、IO 等细节差异的设置, 完成各细节处理后,使用 HAL_NVIC_SetPriority()HAL_NVIC_EnableIRQ() 来使能我们的外设中断;定义中断处理函数 PPP_IRQHandler(),并在中断函数中调用 HAL_PPP_function_IRQHandler() 来判断和处理中断标记;HAL 库中断处理完成后,根据对应中的调用我们需要自定义的中断回调接口HAL_PPP_ProcessCpltCallback(),然后,我们在这个函数中实现我们对外设想做的处理;中断响应处理完成后,STM32 芯片继续顺序执行我们定义的主程序功能,按照以上处理的标准流程完成了一次中断响应。

标签:02,定时器,HAL,函数,中断,滴答,初识,外设
From: https://www.cnblogs.com/kurome/p/18023763

相关文章

  • 2024-02-20-物联网C语言(10-文件)
    10.文件10.1文件的概念文件用来存放程序、文档、音频、视频数据、图片等数据的。文件就是存放在磁盘上的,一些数据的集合。在windows下可以通过写字板或记事本打开文本文件对文件进行编辑保存。写字板和记事本是微软程序员写的程序,对文件进行打开、显示、读写、关闭。作为......
  • 2024-02-20 闲话
    今天学习了\(\displaystyle\int_{-\infty}^{+\infty}\exp(-x^2)dx\)的做法。然后把昨天说的题做掉了,不过有一个性质是\(a<0\)否则\(\exp(ax^2+bx+c)\)不能是概率密度函数。上午的大学物理实在是太难了,量纲学不会一点。于是去知乎上找了一个RAG的文章看,然后吸取经验......
  • 2024年2月18日——KMP算法(未完成
    点击查看代码#include<bits/stdc++.h>usingnamespacestd;constintmaxn=1e6+10;intkmp[maxn];intla,lb,j;chara[maxn],b[maxn];intmain(){ cin>>a+1>>b+1; la=strlen(a+1),lb=strlen(b+1); for(inti=2;i<=lb;i++){ while(j&&b[j+1......
  • 初识flowable
    三个月前开始做flowable的项目,刚刚了解一些又中断了,乘着重新开始之前,赶快恶补一下。相信大家在之前已经了解很多关于flowable的知识了,但是很乱,我也是这样。1、对于一个流程来说,你肯定要开启流程。当提交人提交的那一瞬间,流程被开启。这个开启方法需要我们自己去写,并且可以携带......
  • 2022 CCPC湖北省赛
    2022CCPC湖北省赛​ 这场打的怎么说,很难受。过年来与几个亲戚家的孩子见了面,被灌了不少白酒,没感觉什么酱香有啥好喝的,脑子倒是快成浆糊了。怒了,加训。题解里签到题的做法会写的简单点,这个[每日一棵splay](2022HubeiProvincialCollegiateProgrammingContest题解ABFJK......
  • 李宏毅《机器学习》总结 - 2022 HW8(Anomaly Detection、ResNet) Strong Baseline
    重新学习了一下ResNet。。这作业平均一跑就是3、4个小时题目大意是让你做异常检测(anomalydetection),即给你一些正常的图片,再让你测试图片是正常的还是异常的(可以理解为2分类问题,只不过其中一个类别是无限大的)代码:https://www.kaggle.com/code/skyrainwind/hw8-anomaly-detec......
  • windows server 2019/2022安装WSUS更新服务器配置System.Runtime.InteropServices.COM
    现象: 2024-02-1814:41:10Postinstallstarted2024-02-1814:41:10Detectedroleservices:Api,UI,WidDatabase,Services2024-02-1814:41:10Start:LoadSettingsFromXml2024-02-1814:41:10Start:GetConfigValuewithfilename=UpdateServices-Services.xmlit......
  • 2024.2
    一些以前遗留的题如果想起来了也写进这吧,总归要整理的。ARC171B.Chmax600分的ARCB确实有点难度。先理清操作的实际含义:建出\(i\rightarrowp_i\)的置换环结构,在置换环上从点\(i\)开始走,当下一个点的编号大于自身的时候就走,否则就停下来。然后\(B_i\)就代表最后停留......
  • nginx启动报错:ngx_http_image_filter_module.so" version 1016001 instead of 1022001
    问题现象,启动nginx,提示版本不对[root@k8s-test-node2modules]#/data/nginx/sbin/nginxnginx:[emerg]module"/usr/lib64/nginx/modules/ngx_http_image_filter_module.so"version1016001insteadof1022001in/usr/share/nginx/modules/mod-http-image-filter.conf:1......
  • Android命令-lshal
    一、lahal--help翻译/#lshal--helplshal:列出并调试HIDLHAL。(对于AIDLHAL,请参阅“dumpsys”)commands:list列出HIDLHAL。debug调试指定的HIDLHAL。help打印帮助消息。wait如果HIDLHAL尚未启动,请等待其启动。如果未指定命令,则默认为“l......