【STM32】STM32Cube和HAL库使用初体验+回调函数解读 https://blog.csdn.net/m0_63077733/article/details/134311058
【STM32】通俗易懂的讲解回调函数 https://blog.csdn.net/weixin_43414694/article/details/110192518
**** 应用笔记 | 浅谈STM32库里的回调函数 http://news.eeworld.com.cn/mp/STM32/a293206.jspx
--------------------------------------------------------------------
关联参考:
STM32 回调函数 (***)https://blog.csdn.net/ken2232/article/details/135759275
回调函数(callback)是什么?(*****) https://blog.csdn.net/ken2232/article/details/132665409
STM32:回调函数的本质 (***) https://blog.csdn.net/ken2232/article/details/135812953
callback (韦氏词典)
Callback Definition & Meaning - Merriam-Webster
1. <programming> A scheme used in event-driven programs where the program registers a subroutine (a "callback handler") (子例程)to handle a certain event. The program does not call the handler directly but when the event occurs, the run-time system calls the handler, usually passing it arguments to describe the event.
=======================================
摘录:STM32 回调函数 详解
https://wenku.csdn.net/answer/e01e5c5b80d847b0bf1e0e46c3cc058f
回调函数是一种常用的“函数编程技术”,它允许我们在程序中注册一个函数,当某个事件发生时,系统会自动调用这个函数。在 STM32 的开发中,回调函数经常被用来处理各种外设中断事件。
在 STM32 中,回调函数通常被定义为一个函数指针,当外设中断发生时,系统会将这个指针指向的函数作为中断处理函数来执行。以下是一个简单的回调函数的示例:
/**
* @brief EXTI line detection callbacks.
* @param GPIO_Pin Specifies the pins connected EXTI line
* @retval None
*/
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(GPIO_Pin);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
}
摘录: https://blog.csdn.net/as480133937/article/details/98983268/
关于GPIO的八种模式请参看 《STM32 GPIO八种模式及工作原理详解》
按键消抖:
/**
* 函数功能: 按键外部中断回调函数
* 输入参数: GPIO_Pin:中断引脚
* 返 回 值: 无
* 说 明: 无
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin==KEY1_GPIO_PIN)
{
if(HAL_GPIO_ReadPin(KEY1_GPIO,KEY1_GPIO_PIN)==KEY1_DOWN_LEVEL)
{
BEEP_TOGGLE;
LED1_ON;
LED2_ON;
LED3_ON;
}
__HAL_GPIO_EXTI_CLEAR_IT(KEY1_GPIO_PIN);
}
else if(GPIO_Pin==KEY2_GPIO_PIN)
{
if(HAL_GPIO_ReadPin(KEY2_GPIO,KEY2_GPIO_PIN)==KEY2_DOWN_LEVEL)
{
BEEP_TOGGLE;
LED1_OFF;
LED2_OFF;
LED3_OFF;
}
__HAL_GPIO_EXTI_CLEAR_IT(KEY2_GPIO_PIN);
}
}
摘录:【STM32】通俗易懂的讲解回调函数
https://blog.csdn.net/weixin_43414694/article/details/110192518
回调函数在程序开发中是一个非常重要的概念,所谓的回调其实就是不同程序模块之间的接口和约定,是软件分层设计的基本方式。
注:翻译过来的概念,难以理解。还是采用英文来理解,比较容易。
callback
- call then back:执行具有返回值的函数。
- call without back:执行没有返回值的函数。
拓展:
回调(callback)是一种非常重要的机制,主要可以用来实现软件的分层设计,使得不同软件模块的开发者的工作进度可以独立出来,不受时空的限制,需要的时候通过约定好的接口(或者标准)相互契合在一起,也就是C++或者JAVA等现代编程语言声称的所谓面向接口编程。同时回调也是定制化软件的基石,通过回调机制将软件的前端和后端分离,前端提供逻辑策略,后端提供逻辑实现。
注:回调机制,可以看作是一种针对“面向接口编程”的:设计模式?被调用的那个指定函数,称为:callback 函数。
人事组织模型:
老板 ---> 秘书 ---> 员工
你还应该注意到,在上图中一个最重要的信息:左边的两个人和右边的那个人之间有一条饱含深意的虚线,他意味着左右两边是两拨人,左边的人负责做饭和吃饭,右边的人负责跑腿、砍价等。比如:你只要知道怎么找到前台小妹,传给她参数让她干活就行了,根本不需要知道她究竟到哪儿买的菜跑了多远的路,另外管好大厨,让他死命放辣椒就行了,也根本不需要管小妹什么时候会去找大厨,以及他们之间的任何其他事情。对于右边的小妹而言,她只要根据你的嘱咐勤快跑腿,有必要的时候“回调”一下大厨就行了,也根本不管大厨做的饭究竟啥口味。
瞧,左右两拨人各干各的,互不耦合,独立性强,但又可以非常方便地相互合作,达成一个更丰富的功能。这里,大厨就是一个回调函数——一个不被设计者(你)直接调用,而是被其他人(小妹)回过来调用的函数。你传递给小妹的电话号码,相当于一个能找到大厨的指针,被称之为函数指针,回调机制就是靠传递函数指针来告知回调函数的位置的。
————————————————
版权声明:本文为CSDN博主「努力work,早发paper」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43414694/article/details/110192518
摘录:应用笔记 | 浅谈STM32库里的回调函数
http://news.eeworld.com.cn/mp/STM32/a293206.jspx
0 1 回调函数
有人对STM32固件库里的回调函数有些好奇甚至纠结,这里简单地介绍下,以供参考。
其实从用法及功能上讲他们并没有什么特别的,跟其他函数一样,也是实现特定功能的代码段。
一般来讲,所谓回调函数,泛指基于事件触发而被调用执行的函数,简单点说,就是条件满足了就调用的函数,往往会跟函数指针结合起来通过函数指针实现调用。
根据函数Compute(float a,float b,float(*Action)(float a,float b))里的函数指针的赋值情况来定,被赋予哪个回调函数的地址就调用哪个回调函数。当然,使用函数指针并不是回调函数的核心特征,因事件驱动而被调用才是其核心特征。
生活中我们有时会对某人说,回头再谈、回头再聊。潜台词往往就是等时机成熟了、条件满足了再来具体交涉。这里就充满着浓浓的回调意味。
回调函数可以理解为事件响应函数或者说事件驱动函数。即使相同的事件、基于不同的场景可能会有不同应对处理,从软件代码角度讲就对应不同的回调函数代码。
图2 UART接收完成后可能后续动作
显然,特定的应用场景对应着特定的回调函数,一般来讲,没法简单地仅仅基于事件就拟定一段既能适用于各种场景而又富有针对性的代码。
结合上面的描述,稍微小结下。回调函数除了具有基于事件的触发而被调用执行的特征外,还具有相同事件(?接收完成)因不同应用场景可能需要不同的回调函数之特征,即基于特定应用场景的回调函数其内容具有特定性。
========================
**** 应用笔记 | 浅谈STM32库里的回调函数
http://news.eeworld.com.cn/mp/STM32/a293206.jspx
目录预览
1.回调函数
2.STM32固件库里的回调函数
3.STM32库函数里的回调机制及触发事件
4.常见问题
0 1 回调函数
有人对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方式
库以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 (1U),则该外设事件相关回调函数采用指针注册方式。
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库设计者主动给我们准备了弱定义回调函数或基于各个事件的回调函数指针,尽管很丰富了,但未必能包罗万象,必要时我们可能还得根据具体情况来额外组织些类似回调函数的事件/中断响应代码。
------
思考与说明?:
实际上,对于某个 HAL库函数,用户完全可以将它删除掉,完全可以忘记掉它;然后,采用标准外设库的方法来重写,或者是采用其他的编程方法来重新实现它,比如:直接采用汇编语言来实现。
但是,
HAL 库是可以直接采用 cubeMX图形界面工具,来自动生成这些默认名称的 HAL库函数的。
要自己写吗?已经有这么好用的自动代码生成工具了,还有什么理由需要自己来重新实现它呢?
除非是有非用不可得理由,否则,还是直接采用官方默认的函数名称,默认的代码组织方式来工作,这样才最为省时省力吧?
假如使用 HAL库编程,却不使用 cubeMX工具的话,那么,可能就是在自找麻烦了。
------
关于STM32 HAL库里的回调函数就简单介绍到这里,希望能帮到一些STM32开发者。
:https://blog.csdn.net/weixin_43414694/article/details/110192518