搜索
bottom↓
回复: 10

[讨论]浅淡函数指针的实用性

[复制链接]

出0入0汤圆

发表于 2009-6-15 23:29:10 | 显示全部楼层 |阅读模式
之前做过一个项目,用户测试阶段
要求提供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.MainBoard];
        Task.UpdateData = Func_UpdateData[Task.MainBoard];
        Task.SetLevel   = Func_SetLevel[Task.MainBoard];
        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差了
当不只一处调用时,差别就更大了,函数指针的优势尽显

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入0汤圆

 楼主| 发表于 2009-6-15 23:31:48 | 显示全部楼层

(原文件名:1.jpg)

PC端界面,可以保存成excel的xls格式交还给我,
我直接用软件输出成程序的数据格式,调用编译器生成hex就可以生产了...

软件的编辑功能还是很不错的....
看似都是简单的控件,做了很多人性化的背后处理...
比如解决了DataGrid该死的必须失去焦点才有TextChanged事件,致使左侧图形化编辑(RadioButton等)不同步显示的问题
CheckBox也是不论点哪(只要在单元格内)都可以响应,表格的逻辑错误时会自动定位
左侧的RadioButton位置是可以改变布局并保存的
下载时是自定义格式的分块下载,有CRC16校验,超时判断,块错误自动重下等功能
(去年刚入门单片机,不知道还有超级终端这种现成的协议,NC了一回)

还有输入方式的效率优化等等就不多说了

出0入0汤圆

 楼主| 发表于 2009-6-15 23:44:32 | 显示全部楼层
本来只是想说说函数指针,结果扯太多了,见谅...

出0入0汤圆

发表于 2009-6-16 08:48:16 | 显示全部楼层
顶。

说道函数指针,就想到通用函数指针,一般通用指针可以用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);
}

出0入0汤圆

发表于 2009-6-16 09:01:51 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2009-6-16 09:13:15 | 显示全部楼层
嗯,通用函数指针的确是使用起来有些麻烦的,
直接给每个不同函参的函数都定义一个类型会感觉很奇怪
直接强制转换又会失去些可读性,
所以我一般是不用的,尽量把函数做成无函参的,好在实际中绝大部分情况是可行的...

出0入0汤圆

发表于 2009-6-16 09:26:12 | 显示全部楼层
学习咯!

出0入0汤圆

发表于 2009-6-16 10:08:36 | 显示全部楼层
通用函数指针并不难做,参考下内核当中的一些实现方法,要通用的话,参数就传递一个指针(不需要参数的则指针为NULL),至于这个指针是仅指向一个变量还是一个结构体或数组,由函数自己去解释。

出0入0汤圆

发表于 2009-6-16 10:13:22 | 显示全部楼层
精彩!

出0入0汤圆

发表于 2014-4-10 20:03:06 | 显示全部楼层
好文章哦,相见恨晚!学习了!

出0入0汤圆

发表于 2014-4-12 17:40:00 | 显示全部楼层
  帮顶!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-26 13:16

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表