回调函数简介,自己写一个简单的回调函数

本文通过形象的比喻解释回调函数的概念,并提供了一个简单的回调函数实现案例,包括回调函数的声明、定义和触发条件设置。同时介绍了回调函数在Windows系统DLL和其他DLL中的使用规则,以及不同调用规则如cdecl、WINAPI、fastcall和thiscall的区别。

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

回调函数简介,自己写一个简单的回调函数
   什么是回调函数?
  首先做一个形象的比喻:
你有一个任务,但是有一部分你不会做,或者说不愿做,所以我来帮你做这部分,你做你其它的任务工作或者等着我的消息,但是当我完成的时候我要通知你我做好了,你可以用了,我怎么通知你呢?你给我一部手机,让我做完后给你打电话,我就打给你了,你拿到我的成果加到你的工作中,继续完成其它的工作.这就叫回叫,手机是我通知你的手段,它就是回叫函数,也叫回调函数.
    也有这样的说法(比较容易理解):
  回调函数就好像是一个中断处理函数,系统在符合你设定的条件时自动调用。为此,你需要做三件事:
  1. 声明;
  2. 定义;
3. 设置触发条件,就是在你的函数中把你的回调函数名称转化为地址作为一个参数,以便于DLL调用。
系统所使用的回调函数:
回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自己定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。
程序调用规则:
c(_cdcel)调用规则:
1、参数从右到左压入堆栈
2、函数返回后,调用者负责清除堆栈,这种调用常会生成较大的可执行程序
WINAPI(_stdcall)调用规则:
1、参数从右到左压入堆栈
2、被调用函数返回前自行清理堆栈,生成的代码比cdecl的要小
        
         __fastcall:
__fastcall 它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈)。
thiscall:
thiscall只用在C++成员函数的调用,函数参数按照从右向左的顺序入栈,类实例的this指针通过ECX寄存器传递。需要注意的是thiscall不是C++的关键字,不能使用thiscall声明函数,它只能由编译器使用。
参考:
《天书夜读》 第一章)
http://blog.youkuaiyun.com/qfdebug/archive/2009/03/15/3992055.aspx
http://icelander.javaeye.com/blog/413493
一下是我写的一个回调函数Demo:
CallbackComm.h:
#ifndef _CALLBACK_COMM_H_
       #define _CALLBACK_COMM_H_
#include <Windows.h>
// 延时 时长
       #define MAX_WAIT   15
//声明回调函数类型。
       typedef void (WINAPI *PFCALLBACK)(int Param) ;
// 被回调的函数
      void __stdcall CallbackFC(int Param);
//线程函数
       DWORD WINAPI ThreadProc1( LPVOID lpParameter );
#endif
/////////////////////////////////////////// end file ///////////////////////////////////////////////
CallbackComm.cpp:
#include "CallbackComm.h"
       #include <math.h>
       #include <stdio.h>


       void __stdcall CallbackFC(int Param)
      {
            printf("\n this is a test ,this program was delayed %d second \n",Param);
       }


      DWORD WINAPI   ThreadProc1( LPVOID lpParameter )
     {
              int delay = rand()%MAX_WAIT;
             Sleep(delay*1000);


              PFCALLBACK CallbackFunc= (PFCALLBACK) lpParameter;
             CallbackFunc(delay);


              return TRUE;
       }
/////////////////////////////////////////// end file ///////////////////////////////////////////////
main.cpp:
#include "CallbackComm.h"


      int main()
     {
CreateThread(
                    NULL,
                       0,
                     ThreadProc1,
                    (LPVOID)CallbackFC,
                      0,
                    NULL );
system("pause");
}
  
### STM32 CubeMX 定时器配置延时函数示例教程 #### 一、定时器简介 定时器是一种可以用来测量时间间隔或生成周期信号的外设,在嵌入式开发中非常常见。通过设置不同的参数,可以在特定的时间触发事件或者执行某些操作。 #### 二、CubeMX配置定时器 为了利用STM32中的定时器资源创建延时功能,首先需要借助STM32CubeMX工具完成基本配置工作。启动该软件并新建一个项目后,按照如下步骤进行: - **选择目标MCU**:根据实际使用的芯片型号挑选对应的设备。 - **引脚分配**:虽然对于简单的延时程序来说不需要特别指定GPIO口作为输入/输出端子,但如果计划结合外部电路一起运作,则需在此处定义好连接关系。 - **启用TIMx模块**: - 进入“Pinout & Configuration”标签页下的RCC选项卡; - 找到System Core分类里的TIM组件(例如TIM2),点击进入其属性页面; - 将Channel Channel1至N通道关闭以免干扰正常计数过程;只保留Update Request Source为“UIF”,即更新请求源仅限于溢出标志位; - 设置Prescaler预分频系数以及Period自动重装载值,这两个数值决定了最终产生的延迟长度。具体计算方法可参照官方文档说明[^1]。 #### 三、代码分析 当上述准备工作完成后,下一步就是查看由STM32CubeMX自动生成的相关初始化语句,并理解其中涉及的关键API调用逻辑。 ##### 3.1 自动化代码解析 在`main.c`文件内会发现类似下面这样的片段用于开启选定的定时器实例及其关联特性: ```c /* USER CODE BEGIN TIM2_Init 0 */ /* USER CODE END TIM2_Init 0 */ htim2.Instance = TIM2; htim2.Init.Prescaler = __HAL_RCC_SYSCLK_CONFIG()/10000 - 1; // 设定预分频因子使得每毫秒发生一次中断 htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 999; // 计数值达到此上限就会触发中断 htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } ``` 这段代码实现了对定时器的基础设定,包括但不限于预分频比例、向上计数模式、最大计数值等重要参数的选择。值得注意的是这里采用了宏定义的形式动态调整了预分频寄存器的内容以便适应不同应用场景的需求。 ##### 3.2 中断服务例程(ISR) 为了让定时器能够发挥预期的作用——即产生固定时间段之后发出通知给CPU处理其他任务——还需要注册相应的回调机制来响应这种变化。通常情况下是在`stm32f4xx_it.c`里找到对应位置添加如下所示的部分: ```c void TIM2_IRQHandler(void){ if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE)!= RESET && __HAL_TIM_GET_IT_SOURCE(&htim2,TIM_IT_UPDATE)!=RESET){ HAL_IncTick(); /* Increment system tick */ __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE); // 清除已发生的中断标记防止重复触发 } } ``` 每当检测到上升沿到来之时便会跳转至此段落内部执行相应指令序列直至结束为止。此外还应注意及时清除已经发生的中断状态以防造成死循环现象的发生。 #### 四、实现微秒级延时函数 除了基于定时器构建较为复杂的控制流程之外,有时候也希望能够获得更加精细粒度上的等待效果。此时就可以考虑采用STM HAL库所提供的辅助接口达成目的。比如下面给出了一种简易版的us级别暂停方案供参考: ```c #include "stm32f4xx_hal.h" // 假设系统核心时钟频率为84MHz #define SYS_CLK_FREQ_HZ ((uint32_t)(84 * 1000 * 1000)) static void delay_us(uint32_t us) { uint32_t ticksstart = HAL_GetTick(); while((HAL_GetTick() - ticksstart)<(us*SYS_CLK_FREQ_HZ/(1000*1000))); } int main() { ... delay_us(5); ... return 0; } ``` 不过需要注意这种方法依赖于全局滴答定时器(`SysTick`)来进行粗略估算,因此精度上可能不如直接操控硬件定时器那样理想。如果追求更高的准确性则建议继续深入研究如何合理运用后者提供的各种特性和优势[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值