[讨论]浅淡函数指针的实用性
之前做过一个项目,用户测试阶段要求提供HEX文件,说要自行修改相关输出动作表
觉得他们提出一份动作,我们烧写一个样片太麻烦
在我看来是很NC的,而且他们不一定能改...
后来,我提出一个方案
用atmega32来代替原本的pic16f57:作了块atmega32的芯片转接板,焊上插针后,直接兼容PIC16F57的I/O,VDD,VSS等
再在上面叠加一块串口通信板,连接PC,PC端用c#写了相关动作编辑调试软件,
可以自动转换成表格数据下载到M32的RAM中在线调试动作,或保存到EEPROM中装机后运行,
同类型产品硬件有较多种型号,关于动作表及其它输出,在不同硬件上用到的端口是不一样的
为了提高输出能力,某些地方用了双口线并连的方式(我觉得很NC的方法)
但此时正好给atmega32识别不同硬件带来了方便,一个口输入带上拉,一个口输出0,自动识别硬件的能力就有了
但相关输出时,如何能简化操作呢
于是用到了函数指针
======================================
首先,定义void_Func类型做为无类型函数指针
typedef void (*void_Func)(void);
然后,定义任务类型
typedef struct
{
void_Func InitPort;
void_Func UpdateData;
void_Func SetLevel;
...//其它成员这里就不写了
} TASK;
extern TASK Task;
================================
再定义函数指针数组,以下数组成员都是函数名,有相关定义和实现
和硬件有关,需要不同操作的函数都定义了相关函数指针
const void_Func Func_InitPort[] =
{
Init_Port,
InitPort_1,//具体产品型号就不写了,用数字代替
InitPort_2,
InitPort_3,
};
const void_Func Func_UpdateData[] =
{
UpdateData_Default,
UpdateData_1,
UpdateData_2,
UpdateData_3
};
const void_Func Func_SetLevel[] =
{
SetLevel_Default,
SetLevel_1,
SetLevel_2,
SetLevel_3
};
================================
使用时
执行完自动检测型号后,将型号放到Task.Task.MainBoard中
然后调用Task_SetFunction()
void Task_SetFunction()
{
Task.InitPort = Func_InitPort;
Task.UpdateData = Func_UpdateData;
Task.SetLevel = Func_SetLevel;
Task.InitPort();
}
================================
最后,凡是需要用到相关端口操作的地方,就可以用函数指针实现了
也就是可以
Task.InitPort();//初始化端口
Task.UpdateData();//表格数据转换化端口输出
Task.SetLevel();//输出等级控制
这样用了,看起来简洁明了,效率也高
如果是常规的方法,每个用到这些函数的地方都判断一次
Task.MainBoard的值来选择函数,看上去会让我很不爽,也没现在这种有效率
特别是当Task.MainBoard类型较多时,更为明显
=================================
下面是函数指针编译出的相关汇编代码
=================================
(0058) Task.InitPort();
3C0 91E0 0490 LDS R30,0x0490
3C2 91F0 0491 LDS R31,0x0491
3C4 940E 06AA CALL xicall
=================================
(0241) Task.UpdateData();
501 91E0 0492 LDS R30,0x0492
503 91F0 0493 LDS R31,0x0493
505 940E 06AA CALL xicall
=================================
(0252) Task.SetLevel();
515 91E0 0494 LDS R30,0x0494
517 91F0 0495 LDS R31,0x0495
519 940E 06AA CALL xicall
=================================
xicall:
6AA 920A ST -Y,R0
6AB 95C8 LPM
6AC 920A ST -Y,R0
6AD 9631 ADIW R30,1
6AE 95C8 LPM
6AF 2DF0 MOV R31,R0
6B0 91E9 LD R30,Y+
6B1 9009 LD R0,Y+
6B2 9409 IJMP
=================================
当有3个不同时,其对应汇编的代码效率和运行效率应该已经不比if else差了
当不只一处调用时,差别就更大了,函数指针的优势尽显 http://cache.amobbs.com/bbs_upload782111/files_16/ourdev_453594.jpg
(原文件名:1.jpg)
PC端界面,可以保存成excel的xls格式交还给我,
我直接用软件输出成程序的数据格式,调用编译器生成hex就可以生产了...
软件的编辑功能还是很不错的....
看似都是简单的控件,做了很多人性化的背后处理...
比如解决了DataGrid该死的必须失去焦点才有TextChanged事件,致使左侧图形化编辑(RadioButton等)不同步显示的问题
CheckBox也是不论点哪(只要在单元格内)都可以响应,表格的逻辑错误时会自动定位
左侧的RadioButton位置是可以改变布局并保存的
下载时是自定义格式的分块下载,有CRC16校验,超时判断,块错误自动重下等功能
(去年刚入门单片机,不知道还有超级终端这种现成的协议,NC了一回)
还有输入方式的效率优化等等就不多说了 本来只是想说说函数指针,结果扯太多了,见谅... 顶。
说道函数指针,就想到通用函数指针,一般通用指针可以用void *,
但是,通用函数指针呢?
只好用强制转换。
int test(int a,int b)
{
return a+b;
}
typedef void handle_t (void);
handle_t *pf1;
int pf2;
volatile int result;
int main()
{
pf1=(handle_t*)test;
pf2=(int)test;
result=(*(int (*)(int ,int))pf1)(1,2);
result=(*(int (*)(int ,int))pf2)(3,4);
while(1);
} mark 嗯,通用函数指针的确是使用起来有些麻烦的,
直接给每个不同函参的函数都定义一个类型会感觉很奇怪
直接强制转换又会失去些可读性,
所以我一般是不用的,尽量把函数做成无函参的,好在实际中绝大部分情况是可行的... 学习咯! 通用函数指针并不难做,参考下内核当中的一些实现方法,要通用的话,参数就传递一个指针(不需要参数的则指针为NULL),至于这个指针是仅指向一个变量还是一个结构体或数组,由函数自己去解释。 精彩! {:biggrin:}好文章哦,相见恨晚!学习了! 帮顶!
页:
[1]