应用笔记|浅谈STM32库里的回调函数

STM32单片机 2023-09-14 17:01


关键字:回调函数,HAL库



 目录预览


1.回调函数

2.STM32固件库里的回调函数

3.STM32库函数里的回调机制及触发事件

4.常见问题



01

回调函数


有人对STM32固件库里的回调函数有些好奇甚至纠结,这里简单地介绍下,以供参考。其实从用法及功能上讲他们并没有什么特别的,跟其他函数一样,也是实现特定功能的代码段。一般来讲,所谓回调函数,泛指基于事件触发而被调用执行的函数,简单点说,就是条件满足了就调用的函数,往往会跟函数指针结合起来通过函数指针实现调用。


经常会有人基于类似下面的代码介绍回调函数:


在上面代码中,那四个有关加减乘除的函数可以看成回调函数,具体何时被调用,根据函数Compute(float a,float b,float(*Action)(float a,float b))里的函数指针的赋值情况来定,被赋予哪个回调函数的地址就调用哪个回调函数。当然,使用函数指针并不是回调函数的核心特征,因事件驱动而被调用才是其核心特征。


生活中我们有时会对某人说,回头再谈、回头再聊。潜台词往往就是等时机成熟了、条件满足了再来具体交涉。这里就充满着浓浓的回调意味。


回调函数可以理解为事件响应函数或者说事件驱动函数。即使相同的事件、基于不同的场景可能会有不同应对处理,从软件代码角度讲就对应不同的回调函数代码。


我们不妨看个生活中的例子。生活中有人中了六合彩了,针对这一事件,中奖人可能有下面诸多举动之一【这里简化下,多选一】。但这件事发生在不同人身上,右边的选择很可能不尽一样。换言之,中奖了,到底会选择右边哪一项还得结合具体的人来定。


图1 中六合彩的可能后续行为


我们再切换到STM32的嵌入式开发中来,以UART接收完成事件为例。针对这一事件,不同的应用场景的应对处理往往也是五花八门、五彩缤纷。


图2 UART接收完成后可能后续动作


显然,特定的应用场景对应着特定的回调函数,一般来讲,没法简单地仅仅基于事件就拟定一段既能适用于各种场景而又富有针对性的代码。


结合上面的描述,稍微小结下。回调函数除了具有基于事件的触发而被调用执行的特征外,还具有相同事件因应不同应用场景可能需要不同的回调函数之特征,即基于特定应用场景的回调函数其内容具有特定性。


02

STM32固件库里的回调函数


说到这里,我们具体结合STM32外设固件库里回调函数来聊聊。


首先,作为一个函数库,除了个别初始化函数外,里面不存在现存的完整的回调函数。结合前面的介绍,我们知道回调函数需要结合具体场景而拟定,作为函数库根本做不到这一点,它没法事先知晓发生某个事件时不同的应用会需要采取怎样的操作。


其次,STM32库函数的确采用了回调机制,并基于可能的各种事件为STM32开发者预留了只有函数定义而无具体内容的回调函数,或者是只定义了一些基于各类事件的函数指针,具体的回调函数需要用户完成并将函数地址赋给相应的函数指针而被调用。简单点说,函数库给我们事先预留了众多的回调函数接口


STM32固件库里的回调函数采用了两种调用方式:


第一种是legacy方式,传统的回调方式,库以weak方式定义了各种空的回调函数,像下面这些。STM32库里都给我们准备好了。【下面是有关UART部分事件的弱回调函数体,内容为空】


图3 UART传输事件相关弱回调函数定义


具体开发时,我们根据事件和应用场景基于类似上面的weak函数进行重写,重写时拿掉weak,库里预留的弱定义函数尽量不用动它。比方像下面这些都是最终的用户回调函数。


图4 UART传输事件相关的用户回调函数


另外一种就是指针方式,或称注册方式。即函数库里事先基于各类事件定义好了各种回调函数指针,具体的回调函数由用户基于不同事件和应用需求撰写,然后将函数地址赋给函数指针,这个动作我们称之为回调函数进行注册,之后回调函数就可以通过函数指针而被适时调用。


比方下面是UART外设里定义的一些函数指针:【星号所指的是与UART传输完成事件有关的回调函数所用的指针】


图5 UART传输事件相关的回调函数指针


当我们将回调函数写好后,将函数地址赋给函数指针即可在相应事件发生时被调用。比方类似下面的操作代码。红星标所指代码就是在做回调函数的注册。


图6 UART传输完成事件用户回调函数及注册


给函数指针赋地址可以直接赋地址或通过调用库函数xxx_RegisterCallback完成【见上图星标代码】。


这种指针方式需要我们对C语言中的结构体、函数指针有相应的了解,库只是给我们提供了相应的函数指针,具体的用户回调函数由用户根据需要来编写,将其地址赋给相应的函数指针以供调用。


而前面介绍的传统型回调函数,库则帮我们把可能涉及到的回调函数全部以弱定义的方式都准备好了,我们按需针对性选用,去掉weak填空重写。使用起来相对更直观些,无需我们对函数指针有太多了解。


目前STM32库回调机制中,作为用户到底使用上面的哪种回调方式呢?在每个系列的固件库的配置头文件中有针对各个外设事件回调函数使用方式的选择,比方以STM32F4系列为例,这里有个stm32f4xx_hal_conf.h的头文件,我们可以看到基于各个外设事件回调函数使用方式选择的宏。


图7 回调函数调用方式的选择配置


若我们不对该头文件的相应外设事件的回调函数调用方式的宏定义做调整,则默认传统回调方式,即legacy方式,非指针方式。若将相应的宏值改为1,则该外设事件相关回调函数采用指针注册方式。


03

STM32库函数里的回调机制及触发事件


整体上讲,STM32外设库里的API函数大体由三部分组成,分别是:


初始化函数

启动型执行函数

回调函数【弱定义函数或回调函数指针,最终靠用户具体完成编写】


这样的安排,让整个工程代码结构比较清晰,可以让人快速了解库结构,同时现存的API函数大大减少开发工作量,预留的回调函数接口一方面给开发者提供了便利,另一方面让用户基于不同应用场景自由组织代码而又不破坏整个软件架构。


对于回调函数,可以由哪些事件触发呢?大致分三类,分别是外设初始化操作、外设处理完成【中断】事件、外设出错【中断】事件。我们关注最多是外设处理完成中断事件相关的回调函数。


图8 回调函数触发事件的分类


04

常见问题



4.1 STM32库函数里的回调函数是什么,有何用?


回调函数终究乃用户所编写,是用户基于特定事件和应用需求而编写的功能模块,与其他函数并无本质区别。形式上讲,STM32库预先为用户做了回调函数的弱定义或基于事件的函数指针的定义。因基于特定条件发生后被调用执行而被冠以回调称号。


严格来讲,库函数里没有完整的回调函数,只有基于各类事件的弱定义的不具备实际功能的空回调函数,或者是针对各类事件而定义的各种用于调用回调函数的函数指针。我们的程序监测相应条件或事件往往是有的放矢,当相应事件出现时我们需要做相应的处理,这正是回调函数要实现的功能,也是其功用所在。


4.2 STM32工程里的回调函数与中断函数有什么区别?


STM32外设库里的回调函数的确多数时候跟中断事件及中断服务程序息息相关,往往在中断服务程序中基于特定事件调用相应的用户回调函数。很多时候,我们完全可以将用户回调函数看成中断函数的一个调用模块或延伸。


一个中断服务程序里可以因不同事件而调用不同的回调函数,即一个中断服务程序里可能包含多个不同的回调函数。比方,我们在定时器中断服务程序里可以涉及多个事件及相应的用户回调函数,定时器中断服务程序可能涉及更新事件、不同通道的比较事件或捕获事件,相应的用户回调函数往往因应用场景而异。


当然,回调函数的调用还可以是中断事件以外的其他事件触发调用,比方可以基于初始化操作来调用相应初始化回调函数。当然,在库里对某个外设的初始化可能有些默认操作,但这个默认操作很难是放之四海而皆准的操作,这时我们就得根据实际应用针对性编写初始化代码,即初始化型回调函数。


4.3 STM32库函数里的回调函数是否可以不用?


STM32库函数里的回调机制是库设计者为了便于软件框架清晰、减少开发者工作量等因素事先准备的函数声明及接口,用户使用时只需根据具体应用编写相关函数体。当然,你如果不想理睬这些回调函数声明及定义也是可以的,你根据具体应用自行组织代码完全可行。


4.4 STM32库函数里似乎存在着类似半成品的库回调函数?


STM32库函数里的确准备了一些包含用户回调函数的由库定义的回调函数,是库设计者基于各类特定事件而准备的回调函数,它会针对特定事件做一些基本而必要的操作,比方状态的检查、标志监测及清除,但它没有办法彻底写完整,因为它无法知道该事件发生后用户的真实需求是什么,该如何操作,所以它终究还是需要调用真正的用户回调函数。这样做的目的还是为了给开发者减少开发工作量、以及减少出错等。


我们不妨具体看个实例。下面的回调函数采样的指针注册方式,我们看看UART的DMA传输完成中断里传输完成的回调函数的调用过程。


首先,在UART的DMA启动函数HAL_UART_Transmit_ DMA()里有这样一部分内容:


图9 外设启动运行代码中库回调函数的赋值


库里就DMA传输事件准备了几个回调函数【传输完成、半完成、出错】,即上图中红线标示出来的。其实这几个回调函数还不算完整的用户回调函数,是库定义的并会做一些在它看来用户必定需要完成的一些操作,它事先帮助完成,之后才调用最终的户回调函数。我们以传输完成事件为例来看看,上图星号所标的函数。



图10 库回调函数进一步调回用户回调函数


在这个库定义的UART_DMATransmitCplt()函数里,它对DMA的传输模式做了判断,如果是Normal模式,就将UART的传输数据长度设置为0,禁止DMA后续传输功能,使能UART传输完成中断的使能。然后才来调用用户回调函数【上图中箭头所指】。如果DMA工作在循环模式,代码进到UART_DMATransmitCplt()函数后就直接调用最终的用户回调函数。也就说这些库定义的回调函数在用户回调函数的基础上做了些必要操作,用户回调函数可以看成这类库回调函数的子集。


4.5 基于STM32库来组织用户回调函数要注意什么?


前面提过了,用户回调函数主要基于初始化事件或中断事件而组织的代码。那些中断事件的回调函数的调用基本都是在中断服务程序里发生的。所以,我们在编写回调函数时要结合具体情况灵活地组织代码。要考虑中断优先级、具体事件响应的实时性等。具体点说,我们在组织回调函数时,要考虑是否一定要一股脑地全写在中断服务程序里,会不会影响别的中断响应。对于有些不紧急而又耗时的事件响应代码,可以考虑只在回调函数里设置相应标志,真正的处理代码放到主循环去完成。


还提醒一点,STM32库设计者主动给我们准备了弱定义回调函数或基于各个事件的回调函数指针,尽管很丰富了,但未必能包罗万象,必要时我们可能还得根据具体情况来额外组织些类似回调函数的事件/中断响应代码。


关于STM32 HAL库里的回调函数就简单介绍到这里,希望能帮到一些STM32开发者。


完整内容请点击“阅读原文”下载原文档。


订阅号

关注STM32

视频号


B站账号

点击“阅读原文”,可下载原文档

STM32单片机 ST MCU (产品+工具+资料+技术+市场+活动)x 您的关注x您的支持 = STM32 单片机蝴蝶乐园
评论
  • 村田是目前全球量产硅电容的领先企业,其在2016年收购了法国IPDiA头部硅电容器公司,并于2023年6月宣布投资约100亿日元将硅电容产能提升两倍。以下内容主要来自村田官网信息整理,村田高密度硅电容器采用半导体MOS工艺开发,并使用3D结构来大幅增加电极表面,因此在给定的占位面积内增加了静电容量。村田的硅技术以嵌入非结晶基板的单片结构为基础(单层MIM和多层MIM—MIM是指金属 / 绝缘体/ 金属) 村田硅电容采用先进3D拓扑结构在100um内,使开发的有效静电容量面积相当于80个
    知白 2025-01-07 15:02 85浏览
  • 根据Global Info Research项目团队最新调研,预计2030年全球封闭式电机产值达到1425百万美元,2024-2030年期间年复合增长率CAGR为3.4%。 封闭式电机是一种电动机,其外壳设计为密闭结构,通常用于要求较高的防护等级的应用场合。封闭式电机可以有效防止外部灰尘、水分和其他污染物进入内部,从而保护电机的内部组件,延长其使用寿命。 环洋市场咨询机构出版的调研分析报告【全球封闭式电机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球封闭式电机总体规
    GIRtina 2025-01-06 11:10 106浏览
  • 这篇内容主要讨论三个基本问题,硅电容是什么,为什么要使用硅电容,如何正确使用硅电容?1.  硅电容是什么首先我们需要了解电容是什么?物理学上电容的概念指的是给定电位差下自由电荷的储藏量,记为C,单位是F,指的是容纳电荷的能力,C=εS/d=ε0εrS/4πkd(真空)=Q/U。百度百科上电容器的概念指的是两个相互靠近的导体,中间夹一层不导电的绝缘介质。通过观察电容本身的定义公式中可以看到,在各个变量中比较能够改变的就是εr,S和d,也就是介质的介电常数,金属板有效相对面积以及距离。当前
    知白 2025-01-06 12:04 178浏览
  • 大模型的赋能是指利用大型机器学习模型(如深度学习模型)来增强或改进各种应用和服务。这种技术在许多领域都显示出了巨大的潜力,包括但不限于以下几个方面: 1. 企业服务:大模型可以用于构建智能客服系统、知识库问答系统等,提升企业的服务质量和运营效率。 2. 教育服务:在教育领域,大模型被应用于个性化学习、智能辅导、作业批改等,帮助教师减轻工作负担,提高教学质量。 3. 工业智能化:大模型有助于解决工业领域的复杂性和不确定性问题,尽管在认知能力方面尚未完全具备专家级的复杂决策能力。 4. 消费
    丙丁先生 2025-01-07 09:25 86浏览
  • 本文介绍Linux系统更换开机logo方法教程,通用RK3566、RK3568、RK3588、RK3576等开发板,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。制作图片开机logo图片制作注意事项(1)图片必须为bmp格式;(2)图片大小不能大于4MB;(3)BMP位深最大是32,建议设置为8;(4)图片名称为logo.bmp和logo_kernel.bmp;开机
    Industio_触觉智能 2025-01-06 10:43 87浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 149浏览
  • 彼得·德鲁克被誉为“现代管理学之父”,他的管理思想影响了无数企业和管理者。然而,关于他的书籍分类,一种流行的说法令人感到困惑:德鲁克一生写了39本书,其中15本是关于管理的,而其中“专门写工商企业或为企业管理者写的”只有两本——《为成果而管理》和《创新与企业家精神》。这样的表述广为流传,但深入探讨后却发现并不完全准确。让我们一起重新审视这一说法,解析其中的矛盾与根源,进而重新认识德鲁克的管理思想及其著作的真正价值。从《创新与企业家精神》看德鲁克的视角《创新与企业家精神》通常被认为是一本专为企业管
    优思学院 2025-01-06 12:03 132浏览
  • 根据环洋市场咨询(Global Info Research)项目团队最新调研,预计2030年全球无人机锂电池产值达到2457百万美元,2024-2030年期间年复合增长率CAGR为9.6%。 无人机锂电池是无人机动力系统中存储并释放能量的部分。无人机使用的动力电池,大多数是锂聚合物电池,相较其他电池,锂聚合物电池具有较高的能量密度,较长寿命,同时也具有良好的放电特性和安全性。 全球无人机锂电池核心厂商有宁德新能源科技、欣旺达、鹏辉能源、深圳格瑞普和EaglePicher等,前五大厂商占有全球
    GIRtina 2025-01-07 11:02 79浏览
  • 随着市场需求不断的变化,各行各业对CPU的要求越来越高,特别是近几年流行的 AIOT,为了有更好的用户体验,CPU的算力就要求更高了。今天为大家推荐由米尔基于瑞芯微RK3576处理器推出的MYC-LR3576核心板及开发板。关于RK3576处理器国产CPU,是这些年的骄傲,华为手机全国产化,国人一片呼声,再也不用卡脖子了。RK3576处理器,就是一款由国产是厂商瑞芯微,今年第二季推出的全新通用型的高性能SOC芯片,这款CPU到底有多么的高性能,下面看看它的几个特性:8核心6 TOPS超强算力双千
    米尔电子嵌入式 2025-01-03 17:04 55浏览
  •     为控制片内设备并且查询其工作状态,MCU内部总是有一组特殊功能寄存器(SFR,Special Function Register)。    使用Eclipse环境调试MCU程序时,可以利用 Peripheral Registers Viewer来查看SFR。这个小工具是怎样知道某个型号的MCU有怎样的寄存器定义呢?它使用一种描述性的文本文件——SVD文件。这个文件存储在下面红色字体的路径下。    例:南京沁恒  &n
    电子知识打边炉 2025-01-04 20:04 100浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 134浏览
  • PLC组态方式主要有三种,每种都有其独特的特点和适用场景。下面来简单说说: 1. 硬件组态   定义:硬件组态指的是选择适合的PLC型号、I/O模块、通信模块等硬件组件,并按照实际需求进行连接和配置。    灵活性:这种方式允许用户根据项目需求自由搭配硬件组件,具有较高的灵活性。    成本:可能需要额外的硬件购买成本,适用于对系统性能和扩展性有较高要求的场合。 2. 软件组态   定义:软件组态主要是通过PLC
    丙丁先生 2025-01-06 09:23 85浏览
  • By Toradex 秦海1). 简介嵌入式平台设备基于Yocto Linux 在开发后期量产前期,为了安全以及提高启动速度等考虑,希望将 ARM 处理器平台的 Debug Console 输出关闭,本文就基于 NXP i.MX8MP ARM 处理器平台来演示相关流程。 本文所示例的平台来自于 Toradex Verdin i.MX8MP 嵌入式平台。  2. 准备a). Verdin i.MX8MP ARM核心版配合Dahlia载板并
    hai.qin_651820742 2025-01-07 14:52 57浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦