回调函数用法

本文通过一个简单的C++示例介绍了回调(callback)的概念及其实现方式,利用函数指针在特定时刻调用预先设置的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Callback的本质是设置一个函数指针进去,然后在需要需要触发某个事件时调用该方法。下面写个小例子加强理解

#include <iostream>
using namespace std;

typedef void (* callback)(const char *ch) ;
void printfun(const char *ch) ;
void fun(const char *name , callback cal) ;
int main()
{
    char *name = "我喜欢C++!" ;
    fun(name , printfun) ;

    return 0 ;
}

void printfun(const char *ch)
{
    cout<<ch<<"\n" ;
}
void fun(const char *name , callback cal)
{
    cal(name) ;
}

这里写图片描述

<think>我们正在讨论ARM Cortex-M0内核中的回调函数使用方法。回调函数在嵌入式系统中常用于事件驱动编程,例如中断处理、定时器到期、外设状态变化等场景。 根据引用[1]中关于Cortex-M处理器的描述,我们知道Cortex-M系列处理器使用嵌套矢量中断控制器(NVIC)来处理中断,并且采用基于堆栈的异常模型。在中断发生时,处理器会自动保存上下文,然后跳转到中断服务例程(ISR)执行。回调函数通常就是在这种中断服务例程中被调用的。 在Cortex-M0中,回调函数的使用通常涉及以下步骤: 1. 定义回调函数:编写一个函数,其参数和返回值根据具体需求而定。 2. 将回调函数的地址赋值给某个函数指针,这个函数指针通常由某个外设驱动或中间件提供。 3. 在适当的时机(例如初始化外设时),注册这个回调函数,使得当特定事件(如中断)发生时,可以调用该回调函数。 下面我们通过一个简单的示例来说明回调函数的使用。假设我们使用一个定时器,当定时器中断发生时,我们调用一个回调函数。 示例代码(基于STM32 HAL库): ```c #include "stm32f0xx.h" // 假设使用STM32F0系列,它是Cortex-M0内核 // 定义回调函数类型 typedef void (*TimerCallback)(void); // 全局回调函数指针 TimerCallback timerCallback = NULL; // 注册回调函数 void RegisterTimerCallback(TimerCallback callback) { timerCallback = callback; } // 定时器中断服务函数 void TIM1_BRK_UP_TRG_COM_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_UPDATE) != RESET) { __HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_UPDATE); // 调用回调函数 if (timerCallback != NULL) { timerCallback(); } } } // 用户定义的回调函数 void MyTimerCallback(void) { // 在这里处理定时器中断事件,例如翻转LED HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); } int main(void) { HAL_Init(); // 初始化系统时钟等 // 初始化定时器 // ... 省略定时器初始化代码 // 注册回调函数 RegisterTimerCallback(MyTimerCallback); // 启动定时器并开启中断 HAL_TIM_Base_Start_IT(&htim1); while (1) { // 主循环 } } ``` 在这个示例中: 1. 我们定义了一个回调函数类型`TimerCallback`,它是一个无参数无返回值的函数指针。 2. 我们提供了一个注册函数`RegisterTimerCallback`,用于设置回调函数。 3. 在定时器中断服务函数中,我们检查中断标志,然后调用已注册的回调函数(如果存在)。 4. 用户定义的回调函数`MyTimerCallback`用于执行具体的任务(这里翻转一个LED)。 注意:在实际项目中,回调函数通常由外设驱动提供注册接口,而不是像上面这样自己实现全局变量。例如,STM32 HAL库中,很多外设都有回调函数的机制,比如定时器、串口等。HAL库使用了一种更通用的方式,即每个外设都有一个回调函数指针成员,并且提供了注册回调函数的HAL函数。 例如,使用HAL库的定时器回调函数: ```c // 重写定时器更新中断回调函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM1) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); } } ``` 在这个HAL库的例子中,我们不需要自己注册回调函数到全局变量,因为HAL库已经为我们定义好了回调函数指针,并在中断服务函数中调用它。我们只需要在用户代码中实现`HAL_TIM_PeriodElapsedCallback`函数即可。 总结: 在Cortex-M0内核中,回调函数的使用方法与其他Cortex-M系列类似。主要步骤包括定义回调函数、注册回调函数(将回调函数的地址传递给调用者)以及在事件发生时调用回调函数。在中断处理中使用回调函数时,要确保回调函数的执行时间尽可能短,避免影响其他中断和系统性能。 §§相关问题§§ 1. Cortex-M0中断服务函数中调用回调函数需要注意什么? 2. 如何在Cortex-M0上实现多个回调函数的注册? 3. Cortex-M0中断处理流程中,回调函数是在哪个阶段被调用的?</think>### Cortex-M0内核回调函数使用方法 在Cortex-M0内核中,回调函数主要用于中断处理、外设事件响应等场景。其核心原理是将自定义函数指针注册到特定事件处理机制中,当事件触发时系统自动调用该函数。以下是具体使用方法: #### 一、回调函数基本使用步骤 1. **定义回调函数类型** ```c typedef void (*CallbackFunc)(void* context); // 定义函数指针类型 ``` 2. **声明回调函数指针** ```c CallbackFunc myCallback = NULL; // 初始化为空 ``` 3. **实现回调注册函数** ```c void RegisterCallback(CallbackFunc func) { myCallback = func; // 注册用户自定义函数 } ``` 4. **实现回调触发逻辑** ```c void TIMx_IRQHandler(void) { // 中断服务函数示例 if (TIMx->SR & TIM_SR_UIF) { // 检查中断标志 TIMx->SR &= ~TIM_SR_UIF; // 清除标志 if (myCallback != NULL) { // 检查回调是否注册 myCallback(NULL); // 执行回调 } } } ``` 5. **用户自定义回调实现** ```c void MyCustomCallback(void* context) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 示例:翻转LED } ``` 6. **主函数中注册回调** ```c int main(void) { // 初始化代码... RegisterCallback(MyCustomCallback); // 注册回调函数 while(1); } ``` #### 二、完整示例(定时器中断回调) ```c #include "stm32f0xx.h" // Cortex-M0设备头文件 // 回调类型定义 typedef void (*TimerCallback)(void); // 全局回调指针 volatile TimerCallback timerCallback = NULL; // 回调注册接口 void RegisterTimerCallback(TimerCallback cb) { timerCallback = cb; } // 定时器中断服务函数 void TIM1_BRK_UP_TRG_COM_IRQHandler(void) { if (TIM1->SR & TIM_SR_UIF) { TIM1->SR &= ~TIM_SR_UIF; // 清除中断标志 if (timerCallback) { // 检查回调是否有效 timerCallback(); // 执行回调 } } } // 用户自定义回调 void UserCallback(void) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 翻转PA5引脚 } int main(void) { HAL_Init(); SystemClock_Config(); // GPIO初始化 __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 定时器初始化 __HAL_RCC_TIM1_CLK_ENABLE(); TIM1->PSC = 48000-1; // 预分频(48MHz/48k=1kHz) TIM1->ARR = 500-1; // 自动重载值(0.5s) TIM1->DIER |= TIM_DIER_UIE; // 使能更新中断 NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn); TIM1->CR1 |= TIM_CR1_CEN; // 启动定时器 RegisterTimerCallback(UserCallback); // 注册回调 while(1); } ``` #### 三、关键注意事项 1. **中断安全** - 回调函数执行时间应尽量短(通常<10μs) - 避免在回调中使用阻塞操作(如`HAL_Delay()`) - 对共享资源使用临界区保护: ```c __disable_irq(); // 进入临界区 // 操作共享资源 __enable_irq(); // 退出临界区 ``` 2. **多回调管理** ```c #define MAX_CALLBACKS 5 TimerCallback callbacks[MAX_CALLBACKS]; void RegisterMultiCallback(TimerCallback cb) { for(int i=0; i<MAX_CALLBACKS; i++){ if(callbacks[i] == NULL) { callbacks[i] = cb; break; } } } void TIMx_Handler(void) { // ... for(int i=0; i<MAX_CALLBACKS; i++){ if(callbacks[i]) callbacks[i](); } } ``` 3. **上下文传递** ```c typedef void (*ContextCallback)(void*); void RegisterContextCallback(ContextCallback cb, void* ctx) { // 存储上下文指针 } ``` #### 四、典型应用场景 1. **外设中断处理**(如USART接收完成) 2. **定时器事件响应**(周期任务触发) 3. **DMA传输完成通知** 4. **低功耗模式唤醒处理** > 在Cortex-M0中,NVIC会自动保存/恢复寄存器上下文[^1],但回调函数仍需遵循AAPCS调用规范(如R0-R3用于参数传递)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值