STM32物联网项目-回调函数

回调函数

一般解释

回调函数就是一个通过函数指针调用的函数。如果你把函数的地址传递给中间函数的形参,中间函数通过函数指针调用其所指向的函数时,我们就说这是回调函数。

Fun1()			//应用层
{
	Fun2(Fun3);		//Fun2中间层,Fun3回调函数
}

通俗解释

函数Fun1调用函数Fun2,同时将函数Fun3作为形参传递给Fun2,此时,Fun1为应用层函数,Fun2为中间层函数,Fun3为回调函数,回调函数是一种说法而已。当Fun2被调用时,Fun3也会被接着调用

意义

利于代码结构,将代码分为应用层,中间层,硬件驱动层,彼此独立,方便程序的编辑,阅读,修改与移植;

结构化编程时,结构体只需要定义中间函数,减小内存的开销。

伪代码1

Fun1()
{
    Fun2()
    {
    	Fun3_1();
    	Fun3_2();
        …………
    }
}

这种函数调用写法的缺陷:

当Fun3有多个功能函数时,每次增加一个,都要修改Fun2,这样代码的耦合性就大

通过函数指针的好处是,Fun2与Fun3实现隔离,比如Fun3具有多个功能函数,增加或减少时,不需要修改Fun2的代码。

伪代码2

LED.h

定义带有三个函数指针的结构体类型

//定义结构体类型
typedef struct
{
    LED_Num_t LED_Num;
    void (*LED_ON)(uint8_t );
    void (*LED_OFF)(uint8_t );
    void (*LED_Flip)(uint8_t );
}LED_t;

LED.c

初始化结构体,每个函数指针都指向一个函数

LED_t LED = 
{
    LED1,
    LED_ON,
    LED_OFF,
    LED_Flip
};

main.c

通过结构体调用函数

int main()
{
	LED.LED_ON();
	LED.LED_OFF();
}

这种函数调用写法的缺陷:

1、如果功能函数增多,那结构体里面的函数指针也要增多,增加内存开销

2、main函数或其他调用函数要修改,main函数想要调用增多的功能函数,也要增加调用的操作

回调函数调用关系

在这里插入图片描述

回调函数代码使用示例

原本LED.h头文件里的结构体

//定义结构体类型
typedef struct
{
    void (*LED_ON)(uint8_t );
    void (*LED_OFF)(uint8_t );
    void (*LED_Flip)(uint8_t );
}LED_t;

修改后

本来结构体里是三个分别指向 LED_ON、LED_OFF、LED_Flip的指针,现在改为只定义一个中间层的函数指针LED_Fun,该指针名字为LED_Fun,可指向一个函数,该函数无返回值,一个参数类型为uint8_t,另一个参数为一个函数指针(指向一个无返回值,参数类型为uint8_t的函数)

本来 LED_ON、LED_OFF、LED_Flip函数是用 static 修饰为静态函数的,只能通过结构体变量进行调用,现在用回调函数后,通过函数指针调用,所以要在源文件中把这三个函数的 static 去掉,并在头文件中加 extern 声明为外部可调用

//定义枚举类型
typedef enum
{
    LED1 = (uint8_t)0x01,
    LED2 = (uint8_t)0x02,
    LED3 = (uint8_t)0x03
}LED_Num_t;

//定义结构体类型
typedef struct
{
    void (*LED_Fun)(uint8_t,void (*CallBack)(uint8_t));		//中间层函数
}LED_t;

/* extern variables-----------------------------------------------------------*/
extern LED_t LED;
extern void LED_ON(uint8_t );		//函数声明为外部可调用
extern void LED_OFF(uint8_t );
extern void LED_Flip(uint8_t );
/* extern function prototypes-------------------------------------------------*/ 

LED.c

源文件里实现中间函数LED_Fun(),直接调用CallBack指针指向的函数;在结构体里初始化LED_Fun函数

/* Private variables----------------------------------------------------------*/
static void LED_Fun(uint8_t ,void (*CallBack)(uint8_t));
/* Public variables-----------------------------------------------------------*/
LED_t LED = 
{
    LED_Fun
};
/* Private function prototypes------------------------------------------------*/

/*
* @name   LED_Fun
* @brief  LED功能函数,中间虚拟函数(回调函数)
* @param  LED_NUM:LED灯编号,CallBack:回调函数指针
* @retval None   
*/
static void LED_Fun(uint8_t LED_NUM,void (*CallBack)(uint8_t))
{
    (*CallBack)(LED_NUM);
}

main函数或其他函数使用

STA_Machine.c状态机源文件通过结构体变量LED,调用LED_Fun函数,并传入第一个参数LED灯编号,然后第二个参数就传入具体实现功能的函数,因为函数名就等于函数的首地址,所以把LED_OFF函数的首地址给到了函数指针CallBack,当LED_Fun函数被调用,则LED_OFF函数也会紧接着被调用,从而实现回调函数机制

/*
* @name   Fun_STA1
* @brief  状态函数1
* @param  None
* @retval None   
*/
static void Fun_STA1()
{
    HAL_Delay(500);                     //延时500ms
    LED.LED_Fun(LED1,LED_OFF);         //熄灭LED1    LED.LED_Fun(LED2,LED_OFF);         //熄灭LED2
    LED.LED_Fun(LED3,LED_OFF);         //熄灭LED3
    STA_Machine.ucSTA_Machine_Status = STA2;      //切换到STA2状态
}
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值