打印

单片机C语言请教!

[复制链接]
11236|76
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
space005|  楼主 | 2008-10-1 09:20 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
#include <AT89X51.H>
unsigned int  TD3=0;//延时时间参数
void t0(void) interrupt 1 using 3 
{
TH0=(65536-50000)/256; //定义100mS中断时间常数(6M晶振)
TL0=(65536-50000)%256;
TD3++;
}
void main()
{//1
TMOD=0x11; //定义T0定时器工作为方式1
TH0=(65536-50000)/256; //定义100mS中断时间常数
TL0=(65536-50000)%256; 
ET0=1;  
EA=1; 
TR0=1;
while(TD3<=18000); //定洒水时间间隔为30min  
TD3=0;
}//1

以上程序不知为什么,当TD3为17920时就清零了!为什么不是18000时再清零呢!
我是用KEIL调试的!

相关帖子

沙发
chen3bing| | 2008-10-1 18:04 | 只看该作者

不错

我试了一下,果然如此。
期待高手出现。

使用特权

评论回复
板凳
救火车| | 2008-10-1 18:59 | 只看该作者

我来分析原因。

17920的十六进制数为0x4600
问题出在TD3等于0x45FF,再增一进位时。
主程序这句 while(TD3<=18000);从汇编的角度看,它是先判断低位,再判断高低。
当判断完低位FF后,还未判断高位时,定时器产生了中断。TD3增一,中断以后,TD3变成0x4600。这时再判断高位,变成了0x46.
实际上因为进位时,中断响改变了TD3,造成误判。
把TD3当成了0x46ff 十进制的 18175 ,它大于18000.
其实把TD3当成0x45ff或0x4600都没事。但程序偏偏当成了0x46ff,就出问题了。
另外,在TD3小于0x4500的每次低位向高位进位时,也存在相同的问题。不过即使误判的值也是小于18000.
你的while循环,运行很快,所以被中断干扰的机会极大。
更多的细节请搜一下我竞选版主时的**《一个容易忽略的错误》

使用特权

评论回复
地板
awey| | 2008-10-1 19:21 | 只看该作者

中断里做个标志,处理放在主程序中

void t0(void) interrupt 1 using 3 
{
TH0=(65536-50000)/256; 
TL0=(65536-50000)%256;
Timer0_On=1;
}

while(TD3<=18000)
{
if(Timer0_On==1)
   {
    TD3++;
    Timer0_On=0;
    }
}

使用特权

评论回复
5
冷漠| | 2008-10-1 20:17 | 只看该作者

主要缺陷。

LZ原中断代码有缺陷:
void t0(void) interrupt 1 using 3 
{
TH0=(65536-50000)/256; //定义100mS中断时间常数(6M晶振)
TL0=(65536-50000)%256;   
TD3++;
}
***************************************************************
修改:
void t0(void) interrupt 1 using 3 
{
TL0=(65536-50000)%256;  //******先修改T0低8位!教材上强调过的。*****

TH0=(65536-50000)/256; //定义100mS中断时间常数(6M晶振)
TD3++;
}


(还有更精确的计时程序,((65536-50000)%256)+7 什么的;)

使用特权

评论回复
6
救火车| | 2008-10-2 08:04 | 只看该作者

5楼说的,不是楼主的问题吧。

使用特权

评论回复
7
冷漠| | 2008-10-2 12:04 | 只看该作者

实验证实过了。

未修改前:TD3记录到17920退出(0x4600)。

修改顺序后:TD3记录到18000退出(0x4650)。

使用特权

评论回复
8
救火车| | 2008-10-2 19:07 | 只看该作者

楼上的,说不通啊。

是按5楼的方法改的吗?
请重复几次试验,确认每次都是18000.

使用特权

评论回复
9
chen3bing| | 2008-10-3 10:04 | 只看该作者

是的

我试了5次,都是18000(4650)。

使用特权

评论回复
10
救火车| | 2008-10-3 12:15 | 只看该作者

奇怪,我已经亲手做过实验。5次都是18001.

TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
中断里先重置TH0时,试验5次,TD3的结果都是4600 


TL0=(65536-50000)%256;
TH0=(65536-50000)/256;
中断里先重置TL0时,试验5次,TD3的结果都是4651

我现在非常奇怪,正在查找原因......



使用特权

评论回复
11
xinzha| | 2008-10-3 12:17 | 只看该作者

ll

3楼和5楼的都有道理,也许顺序变了产生的影响就是由于顺序正确之后,每两次进入中断的时间比以前小了一点点,正好阻止了在低位判断和高位判断中间产生中断的现象。
如果楼主想验证,建议写另外一个判断,先判断td3高位,再判断低位,估计就不会出现这种不同了。

使用特权

评论回复
12
救火车| | 2008-10-3 12:31 | 只看该作者

楼上说的有道理

中断里先重置TH0时,试验5次,TD3的结果都是4651 

while(TD3<=18000)
{
k++;
}

我在WHILE里加了一条指令,结果变了,不再是4600而是4651.
由此可知,改变了置TH0 TL0的顺序,只是使进入中断的时间比以前小了一点点。还有就是while循环太短,恰好每次进中断的位置都一样。所以看到结果每次都一样。
建议程序这样写:
do 
{
    EA=0; //禁止所有中断
    TD4=TD3;
    EA=1; //允许所有中断
} while(TD4<=18000);


使用特权

评论回复
13
hotpower| | 2008-10-3 13:09 | 只看该作者

volatile unsigned int TD3=0;

使用特权

评论回复
14
救火车| | 2008-10-3 17:17 | 只看该作者

哈哈!我把菜农大叔也忽悠进来了。

但是hotpower的方法,并没有解决问题。
我经过验证,发现用volatile声明变量,并没有纠正错误。

volatile unsigned int  TD3=0;
最后结果还是4600

使用特权

评论回复
15
hotpower| | 2008-10-3 17:53 | 只看该作者

哈哈~~~俺真没看正题~~~

使用特权

评论回复
16
hotpower| | 2008-10-3 20:44 | 只看该作者

这是1个数据临界的问题,8位机是常事

在OS中一般采用关中断,裸奔是采用2次读入。

因为中断周期相对较长,故可用暂存器先读入TD3,再nop后将暂存器与TD3比较。

若相等则认为TD3读出正确,这是将暂存器与1800比较即可。(不能用TD3比较)

这2种方法都能正确测试,前者可能简单些,如12楼所示。

后者可不影响中断。

使用特权

评论回复
17
xhtxzxw| | 2008-10-4 09:10 | 只看该作者

嘿嘿

看看反汇编吧!

while语句的:
LOOP: SETB    C
      MOV     A,16H     ;15H=TD3的高字节,16H=TD3的低字节
      SUBB    A,#050H
      MOV     A,15H     ;当TD3==45FFH时发生中断,问题就出来了!
      SUBB    A,#046H   ;从中断响应函数里回来,TD3高字节就是46H了!
      JC      LOOP      ;接上一个注释,这里CY标志就==0了,不Jump回去了!

中断的:
      PUSH    ACC
      MOV     TH0,#03CH
      MOV     TL0,#0B0H
      INC     16H
      MOV     16H
      JNZ     L_0
      INC     15H
L_0:  POP     ACC
      RETI
调换YH0,TL0的顺序,只能是一种巧合吧? 一个while语句对应起来相当于这么多汇编语句,其中任何时刻都有可能发生与之相关的中断,所以在中断外对相关量的引用是一个严肃的问题啊!问题的实质是:51是8位CPU,而相关的量TD3是16位的,在C里看起来 "while(字节变量 <= 字节常量)" 和 "while(整数变量 <= 整数常量)" 似乎没有本质的区别,而且,大多数情况下也确实没有本质的区别,但,在此处,"变量"在中断响应函数中发生变化,而中断外又要对这种变化作出判断,问题就出现了.
推荐的做法是:增加一个字节量TD4,主函数改成类似下面的样子:
unsigned TD4 = 0;
main()
{
    ... ...               //初始化等,不变
    while(1)
    {
        while(TD4 == 0);
        TD4 = 0;
        //与灌溉相关的语句
     }
}
中断改成:
void t0(void) interrupt 1 using 3 
{
    TL0=(65536-50000)%256;
    TH0=(65536-50000)/256; //100mS中断时间常数(6M晶振)
    TD3++;
    if(TD3>=18000) 
    {
        TD4 = 1;
        TD3 = 0;
    }
}
似乎吃饱了撑的慌,为什么要增加一个变量TD4呢???注意它是字节量!在51里对字节量的增减操作和判断分别都是一个语句就搞定的,这就避免了发生所谓的"换手"的问题.
(关于"换手"问题的说明: 假定做某件事可以得到4650H以上个$的报酬,但这些$按16进制规则放在两个钱包里,首先让你看零头$,你发现有FFH个$,再让你看大头$,本来是4500H个$,可赶巧在此时按事先规定的规则发生了某件事,大头变成了4600H个$,并且不让你知道或者你没有意识到同时零头$回0了!于是,你的判断就成了大头=4600H,零头=FFH,满足要求!)

使用特权

评论回复
18
xinzha| | 2008-10-4 15:18 | 只看该作者

ll

这个编译器真是够烂的了,巨大的一个bug。

使用特权

评论回复
19
救火车| | 2008-10-4 15:23 | 只看该作者

编译器就是这样的。只能人来适应编译器。

使用特权

评论回复
20
xinzha| | 2008-10-4 16:31 | 只看该作者

ll

所以嘛,编译器也有好坏之分,幸亏我做单片机的时候不用c。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

8

主题

16

帖子

0

粉丝