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進行驗證。