搜索
bottom↓
回复: 26

宏定义的神奇效果,各路大神来围观啊

[复制链接]

出0入0汤圆

发表于 2012-8-31 20:49:14 | 显示全部楼层 |阅读模式
51单片机,kiel编译器,代码如下
#define GATE_TIME  20
......
void INIT_T1(void)
{       
        TMOD=0x11;                                 //定时器模式
        TH1=(65536-(uint)(GATE_TIME*1.84))/256;
        TL1=(65536-(uint)(GATE_TIME*1.84))%256;

        ET1=1;                                        //打开定时器1中断       
        TR1=1;                                        //打开计数器1
        EA=1;                                                                        //开总中断
}

这样定时器正常工作。


然而神奇的是代码如下的话:
uchar GATE_TIME  = 20;
......
void INIT_T1(void)
{       
        TMOD=0x11;                                 //定时器模式
        TH1=(65536-(uint)(GATE_TIME*1.84))/256;
        TL1=(65536-(uint)(GATE_TIME*1.84))%256;

        ET1=1;                                        //打开定时器1中断       
        TR1=1;                                        //打开计数器1
        EA=1;                                                                        //开总中断
}
定时器定时时间不准了,而且差好几个数量级。没明白,为什么宏定义和定义成变量的差别这么大啊,我头发已经掉的差不多了,你们呢?

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

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

出0入0汤圆

发表于 2012-8-31 20:52:51 | 显示全部楼层
第一个,编译器直接算好了,放到TH1
第二个,编译器将算法放到单片机里让单片机去算,效果当然不一样
楼主有兴趣可以去看看编译后的汇编到底是怎么回事

出0入0汤圆

发表于 2012-8-31 20:54:34 | 显示全部楼层
惭愧,从没在单片机上用过小数......
(uint)(GATE_TIME*1.84)
写成((uint)GATE_TIME*184/100)试试

出0入0汤圆

发表于 2012-8-31 20:54:47 来自手机 | 显示全部楼层
本帖最后由 mydows 于 2012-8-31 20:57 编辑

宏定义编译时,先替换,后展开。你把两种定义都替换,然后展开看看。

出0入0汤圆

发表于 2012-8-31 20:56:58 | 显示全部楼层
个人理解宏定义后值GATE_TIME*1.84基本是个常量了(有可能编译器优化了),定义变量是每次使用时做乘法自然时间消耗很多了。

出0入0汤圆

发表于 2012-8-31 21:02:46 来自手机 | 显示全部楼层
可能是类型

出0入0汤圆

发表于 2012-9-1 11:03:50 | 显示全部楼层
我觉得应该是类型转换问题!

出0入0汤圆

 楼主| 发表于 2012-9-1 13:44:20 | 显示全部楼层
感谢各路高手,我已经试过了,貌似不是上面解释的原因,应该还有别的原因吧。
把程序改为
1、TH1=(65536-(uint)(GATE_TIME*184/100))/256;
2、TH1=(65536-(uint)(GATE_TIME))/256;
3、TH1=(65536-GATE_TIME))/256;
效果还是和原来一样。
关于汇编,我承认我玩不转啊。所以没法试验。
虽然乘法计算量大的话会对延时产生一定影响,但是我晶振是22,1184的,而且STC单片机支持1T模式,所以这个计算还是不会使定时器定时时间差几个数量级的。原因我继续找吧,找到了和大家分享。


      

出0入0汤圆

发表于 2012-9-1 13:49:09 | 显示全部楼层
本帖最后由 t2397362 于 2012-9-1 13:57 编辑

你把uchar换成double试试??

或者1.8跟GATE_TIME换个位置

1.8 * GATE_TIME

还有

#define GATE_TIME 20.0

出0入0汤圆

发表于 2012-9-1 13:50:35 | 显示全部楼层
帮顶,等结果。个人认为是完全一样的。只是变量一般不声明为全大写的格式。

出0入0汤圆

发表于 2012-9-1 13:55:07 | 显示全部楼层
如果二楼说是对的那换成const uchar GATE_TIME = 20那应该就一样了!

出0入0汤圆

发表于 2012-9-1 14:44:59 | 显示全部楼层
TH1=(65536-(uint)(GATE_TIME*1.84))/256;
TL1=(65536-(uint)(GATE_TIME*1.84))%256;
宏定义时,编译器已经将上面的公式算好了,二定义为uchar后,每次在定时器的中断程序里都要计算一遍,里面有浮点数乘法,还有除法,取余操作,运算量可不小哦

出0入0汤圆

发表于 2012-9-1 15:27:41 | 显示全部楼层
本帖最后由 dadatou 于 2012-9-1 15:35 编辑

初看似乎像是数据类型溢出了,但仔细分析一下,20*1.84=36.8,即使是字符型,也应该不会溢出,所以可以排除数据类型错误的问题。
于是亲自用Keil试验了一下,楼主完全相同的代码,一个字不改,两种情况软件仿真得出的结果完全相同,TH1=0xFF,TL1=0xDC 没有用硬件去测试,到此两种情况下TH

的值相同,应该不可能出现定时器定时时间不同的情况。

有可能的一种情况是,我的编译器和楼主的版本不同,或者优化等级不同,楼主编译器优先等级太高,可能出现内存覆盖的问题,楼主尝试一下,将第二段代码的两

个表达式拆分,利用中间变量逐步计算。

uchar GATE_TIME  = 20;
......
void INIT_T1(void)
{        
             uint Temp;

        TMOD=0x11;                                 //定时器模式

        Temp=GATE_TIME*1.84;       
       
        TH1=(65536-Temp)/256;
        TL1=(65536-Temp)%256;

        ET1=1;                                        //打开定时器1中断        
        TR1=1;                                        //打开计数器1
        EA=1;                                                                        //开总中断
}

出0入0汤圆

发表于 2012-9-1 15:46:10 | 显示全部楼层
2楼正解

出0入0汤圆

发表于 2012-9-1 22:44:26 | 显示全部楼层
第一个因为是宏, 所以在预处理期和编译期就进行了计算和强制类型转换. 用到的时候已经是常数值了. 占用的是编译时间.
第二个因为是你定义的类型. 是个变量, 所以与浮点数相乘会进行"类型提升",  进行的是浮点处理. 然后再强制转换, 每次都是如此, 占用的是运行时间, 速度当然会慢了.

出0入0汤圆

发表于 2012-9-1 23:33:30 | 显示全部楼层
第一个因为公式在预处理期已经都替换成常数,编译期时就已经算好了值,结果直接赋值给TH1了,只占编译时间。
第二个因为公式使用了变量,虽然变量有初始值,但变量的值在运行时可能会改变,所以编译器是不会把初始值20套进公式的,这个公式会原样套进代码中,运行时计算。因为设计到浮点运算,所以速度会慢。

出0入0汤圆

发表于 2012-9-1 23:43:17 | 显示全部楼层
himm007 发表于 2012-9-1 23:33
第一个因为公式在预处理期已经都替换成常数,编译期时就已经算好了值,结果直接赋值给TH1了,只占编译时间 ...

编译器的问题有时候是想不通的,我前几天刚遇到过一个

unsigned char i,m;

viod fun(i,(20-m))这样调用都有警告

我写程序一般不喜欢是要完全没有警告才算好
最后只得这样写
m=20-m;
fun(i,m);

出0入0汤圆

发表于 2012-9-1 23:47:14 | 显示全部楼层
x11223y 发表于 2012-9-1 23:43
编译器的问题有时候是想不通的,我前几天刚遇到过一个

unsigned char i,m;

我觉得原因不一定是在函数调用上。
具体提示什么错误呢?

出0入0汤圆

发表于 2012-9-2 00:02:36 | 显示全部楼层
我记得提示是类型方面的错误

出0入0汤圆

 楼主| 发表于 2012-9-2 18:14:03 | 显示全部楼层
大家好像都认为是运算占用了过多的时间,我开始不这样认为,现在开始动摇了。不过问题已经解决了,我直接给TH1和THL1赋值了,程序跑起来流畅了。
特别关注14楼的想法,我本来也想仿真的,但是手头没仿真器,只能软件仿真,仿真到后面程序都乱了,只能说自己能力和精力都有限啊。

出0入0汤圆

发表于 2012-9-3 09:42:24 | 显示全部楼层
楼主可能低估了浮点数的运算量。51这种没有浮点运算单元的CPU要计算浮点数,只能是软件仿真,非常费时的。

出0入0汤圆

发表于 2012-9-3 19:07:16 | 显示全部楼层
神仙聚会,本小鬼到此一游,估计与浮点运算有关,
uchar GATE_TIME  = 20;
......
void INIT_T1(void)
{        
        TMOD=0x11;                                 //定时器模式
        TH1=(65536-(uint)(GATE_TIME*184/100))/256;
        TL1=(65536-(uint)(GATE_TIME*184/100))%256;

        ET1=1;                                        //打开定时器1中断        
        TR1=1;                                        //打开计数器1
        EA=1;                                                                        //开总中断
}
会咋样?

出0入0汤圆

发表于 2012-9-3 19:43:35 来自手机 | 显示全部楼层
一般直接赋值,以免夜长梦多!

出0入0汤圆

发表于 2012-9-4 08:17:51 | 显示全部楼层
我觉得2楼不错

出0入42汤圆

发表于 2012-9-4 08:56:45 | 显示全部楼层
x11223y 发表于 2012-9-1 23:43
编译器的问题有时候是想不通的,我前几天刚遇到过一个

unsigned char i,m;

m是char,20是int,减过还是int。反对把编译器神秘化

出0入0汤圆

发表于 2012-9-4 09:23:55 | 显示全部楼层
albert_w 发表于 2012-9-4 08:56
m是char,20是int,减过还是int。反对把编译器神秘化

原来如此

出0入0汤圆

发表于 2012-9-4 09:38:24 | 显示全部楼层
x11223y 发表于 2012-9-4 09:23
原来如此

所以你要这样调用才没有警告:
viod fun(unsigned char i,  unsigned char j);
unsigned char i,m;

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

本版积分规则

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

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

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

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