C语言回调函数–初识回调函数
回调函数简介:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
回调运行机制
1、定义一个回调函数;
2、提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;
3、当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。
回调意义
因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。
回调函数是继承自C语言的。在C++中,应只在与C代码建立接口或与已有的回调接口打交道时,才使用回调函数。除了上述情况,在C++中应使用虚拟方法或仿函数(functor),而不是回调函数。
基于单片机(nrf51822)平台测试回调函数
一、首先使用typedef声明执行函数的指针类型,返回值类型、参数类型
格式:typedef int (*POINTER)() 即:返回值(指针名)(参数列表)
例:
typedef int (*CallBackFun)(char *p); //为回调函数命名,类型命名为CallBackFun,参数为char *p
二、编写回调函数(方法)原型,格式应符合上面声明的CallBackFun
格式:int Afun(char *p) 即:返回值 函数名(参数列表)
例
int Call2(char *p,int(*ptr)())
{
printf("==%s! \n\r",p);
(*ptr)(p);
return 0;
}
三、编写执行回调函数的函数
格式一:int call(CallBackFun pCallBack, char *p) 通过命名方式
格式二:int call2(char *p, int (*ptr)()) 通过回调函数指针
例:
int call(CallBackFun pCallBack,char *p)
{
printf("call print %s! \n\r",p);
pCallBack(p);
return 0;
}
四、编写主程序
在main函数中,通过Call1函数执行回调函数
例:
char *p = "hello";
call1(Afun, p);
介绍部分到此为止。附main.c代码
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf_gpio.h"
#include "nrf_adc.h"
#include "app_uart.h"
#include "nrf_delay.h"
#include "app_error.h"
#include "app_timer.h"
#include "app_button.h"
#define RX_PIN_NUMBER 11
#define TX_PIN_NUMBER 9
#define RTS_PIN_NUMBER 8
#define CTS_PIN_NUMBER 10
#define UART_TX_BUF_SIZE 128 /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE 128
#define LED0 18
#define LED1 19
#define BUTTON0 16
#define BUTTON1 17
#define BUTTONS_NUMBER 2
#define APP_TIMER_PRESCALER 0
#define APP_TIMER_MAX_TIMERS 8
#define APP_TIMER_OP_QUEUE_SIZE 8
//汉字测试
uint32_t err_code;
static void lfclk_config(void);
void uart_error_handle(app_uart_evt_t * p_event);
void uart_init(void);
void button_event_handler(uint8_t pin_no, uint8_t button_action);
void app_button_user_init(void);
//首先使用typedef声明执行函数的指针类型,返回值类型、参数类型。
//typedef int (*POINTER)() 即:返回值(指针名)(参数列表)
typedef int (*CallBackFun)(char *p);
//编写回调函数(方法)原型,格式应符合上面声明的CallBackFun
//格式:int Afun(char *p) 即:返回值 函数名(参数列表)
int Afun(char *p)
{
printf("Afun %s!\n\r",p);
return 0;
}
int Bfun(char *p)
{
printf("Bfun %s!\n\r",p);
return 0;
}
//格式一:int call(CallBackFun pCallBack, char *p) 通过命名方式
int call1(CallBackFun pCallBack,char *p)
{
printf("call print %s! \n\r",p);
pCallBack(p);
return 0;
}
//格式二:int call2(char *p, int (*ptr)()) 通过回调函数指针
int call2(char *p,int(*ptr)())
{
printf("==%s! \n\r",p);
(*ptr)(p);
return 0;
}
int call3(char *p, CallBackFun pCallBack)
{
printf("--%s! \n\r",p);
pCallBack(p);
return 0;
}
int main()
{
char *p = "hello";
nrf_gpio_cfg_output(LED0);
nrf_gpio_cfg_output(LED1);
lfclk_config();
uart_init();
app_button_user_init();
nrf_delay_ms(100);
printf("**\n\n\r");
call1(Afun, p);
call1(Bfun, p);
printf("\n\r");
nrf_delay_ms(100);
call2(p, Afun);
call2(p, Bfun);
printf("\n\r");
nrf_delay_ms(100);
call3(p, Afun);
call3(p, Bfun);
while(1)
{
}
}
void uart_error_handle(app_uart_evt_t * p_event)
{
if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)
{
APP_ERROR_HANDLER(p_event->data.error_communication);
}
else if (p_event->evt_type == APP_UART_FIFO_ERROR)
{
APP_ERROR_HANDLER(p_event->data.error_code);
}
}
void uart_init(void)
{
const app_uart_comm_params_t comm_params =
{
RX_PIN_NUMBER,
TX_PIN_NUMBER,
RTS_PIN_NUMBER,
CTS_PIN_NUMBER,
APP_UART_FLOW_CONTROL_DISABLED,
false,
UART_BAUDRATE_BAUDRATE_Baud115200
};
APP_UART_FIFO_INIT(&comm_params,
UART_RX_BUF_SIZE,
UART_TX_BUF_SIZE,
uart_error_handle,
APP_IRQ_PRIORITY_LOW,
err_code);
APP_ERROR_CHECK(err_code);
}
void button_event_handler(uint8_t pin_no, uint8_t button_action)
{
static uint8_t i = 0;
printf("%d\n\r",i++);
}
void app_button_user_init(void)
{
uint32_t timer_ticks = APP_TIMER_TICKS(100, APP_TIMER_PRESCALER);
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false);
static app_button_cfg_t P_button[BUTTONS_NUMBER] =
{
{
BUTTON0,
APP_BUTTON_ACTIVE_LOW,
NRF_GPIO_PIN_NOPULL,
button_event_handler
},
{
BUTTON1,
APP_BUTTON_ACTIVE_LOW,
NRF_GPIO_PIN_NOPULL,
button_event_handler
}
};
err_code = app_button_init((app_button_cfg_t *)P_button,BUTTONS_NUMBER,timer_ticks);
err_code = app_button_enable();
}
static void lfclk_config(void)
{
NRF_CLOCK->LFCLKSRC = (CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos);
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
NRF_CLOCK->TASKS_LFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0)
{
//Do nothing.
}
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
}
之所以再nrf51822平台做测试与验证,是因为nRF51822在开发的过程中,其底层库及协议栈经常使用回调函数的思路。在这个平台上学习和理解回调函数有利于后续的开发。
当然,例程中验证回调函数都使用的是‘printf’函数,因此可移植到STM32等其他硬件平台,也可使用Visual studio进行验证。