找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 8108|回复: 24
打印 上一主题 下一主题
收起左侧

STC12 串口通讯问题 程序非常简单 困扰好几天了

[复制链接]
跳转到指定楼层
楼主
ID:64911 发表于 2014-8-10 06:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
现象:
1:我用stc-isp软件给单片机下载程序后重启开发板,
2:再次点stc-isp软件上的下载/编程按钮,这次我不重启开发板,为了模拟通讯,一直循环给开发板发数据。
3:没过多久,led1或者led2会点亮。
我的理解:
1:理论上TEnd=0或者1,但是这里TEnd还会等于256或者-255。不会再等于其他任何数据。
2:如果是定时器太短,导致主程序跑不动延时了,TEnd完全会不定期等于其他数据,但是没有,他只会等于256或者-255这两个特殊数据。
3:我怀疑是数据溢出 因为这两个数据跟8bit,byte有关系。
#include<reg52.h>
#define uchar8 unsigned char //8
#define uint16 unsigned int //16
#define ulong32 unsigned long //32

sbit led0=P1^0;
sbit led1=P1^1;
sbit led2=P1^2;

ulong32 TStar=0;//记录时间
ulong32 TEnd=0;
ulong32 sys1ms=0;//1ms累加,可以一直累加到49.7天

void main()
{        
        //1ms@12.000MHz
        TMOD &= 0xF0;                //设置定时器模式
        TMOD |= 0x01;                //设置定时器模式
        TL0 = 0x18;                //设置定时初值
        TH0 = 0xFC;                //设置定时初值
        TF0 = 0;                //清除TF0标志
        TR0 = 1;                //定时器0开始计时
        //4800bps@12.000MHz
        PCON |= 0x80;                //使能波特率倍速位SMOD
        SCON = 0x50;                //8位数据,可变波特率
        TMOD &= 0x0F;                //清除定时器1模式位
        TMOD |= 0x20;                //设定定时器1为8位自动重装方式
        TL1 = 0xF3;                //设定定时初值
        TH1 = 0xF3;                //设定定时器重装值
        ET1 = 0;                //禁止定时器1中断
        TR1 = 1;                //启动定时器1

        EA=1;//开启总中断
        ES=1;//开串口中断
        ET0=1;//开定时器0中断
        while(1)
        {
                ulong32 temp;
                TStar=sys1ms;//扫描周期记录初始值

                //todo_something1();
                //todo_something2 ();

                temp=sys1ms-TStar;
                if(TEnd<=temp)TEnd=temp;//扫描周期计时
               
                if(TEnd!=256 && TEnd!=-255)led0=0;
                if(TEnd==256)led1=0;
                if(TEnd==-255)led2=0;
        }
}

void Timer0_Interrupt() interrupt 1//定时器0 1ms 中断程序
{        
        TL0 = 0x18;                //设置定时初值
        TH0 = 0xFC;                //设置定时初值
        sys1ms++;                //1ms累加,可以一直累加到49.7天
}

void Comm_Interrupt() interrupt 4//串行中断程序
{
        if(TI)
        {
                TI = 0;
        }
        if(RI)
        {        
                 char value;
         value=SBUF;
         RI=0;
        }
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:19715 发表于 2014-8-10 10:57 | 只看该作者
void Comm_Interrupt() interrupt 4//串行中断程序
{
        if(TI)
        {
                TI = 0;
        }
         if(RI)
        {        
                char value;
                value=SBUF;
                RI=0;
        }
}

回复

使用道具 举报

板凳
ID:64911 发表于 2014-8-10 11:47 | 只看该作者
明白 发表于 2014-8-10 10:57
void Comm_Interrupt() interrupt 4//串行中断程序
{
        if(TI)

你好 我把串行中断程序改成你这样后   症状1确实没有了,
但是症状2就出来了,我是这样做的:
1:我用stc-isp给单片机下载修改后的程序,
2:重启开发板,然后点stc-isp下载/编程按钮,我不重启开发板,相当于模拟通讯。
3:大概在2分钟之内,led2还是会点亮。。
回复

使用道具 举报

地板
ID:19715 发表于 2014-8-10 12:37 | 只看该作者
程序运行,TEnd完全有可能去到256,
主要是因为定时器0的中断周期太短,主程序跑不动

评分

参与人数 1威望 +20 收起 理由
admin + 20 赞一个!

查看全部评分

回复

使用道具 举报

5#
ID:64911 发表于 2014-8-10 13:10 | 只看该作者
本帖最后由 xiehuipeng 于 2014-8-10 13:57 编辑
明白 发表于 2014-8-10 12:37
程序运行,TEnd完全有可能去到256,
主要是因为定时器0的中断周期太短,主程序跑不动

那个Tend还会=-255.
我觉得这应该是溢出的问题 Tend只会出现这两个特殊的数字256或者-255  因为这两个数据跟8bit,byte有关系。
如果是按你说的,那应该会不定期出现其他数值。我最开始的程序就是通讯读到Tend只会等于256或者-255 。
再次说明一下:只会等于256和-255,不可能出现其他值。大哥可以试一下。。



回复

使用道具 举报

6#
ID:19715 发表于 2014-8-10 22:20 | 只看该作者
xiehuipeng 发表于 2014-8-10 13:10
那个Tend还会=-255.
我觉得这应该是溢出的问题 Tend只会出现这两个特殊的数字256或者-255  因为这两个数 ...

确实不解???应该问问老姚
回复

使用道具 举报

7#
ID:19715 发表于 2014-8-10 22:56 | 只看该作者
问题找到了,是因为程序做减法太过频繁导致
回复

使用道具 举报

8#
ID:19715 发表于 2014-8-10 23:14 | 只看该作者
操作太过于频繁导致
有可能【TStar=sys1ms;】之后的数值TStar=0x*****1ff,sys1ms=0x******100,
意思就是说在移动最低字节的时候,TStar和ys1ms都是TStar=0x******ff,在没有来得及次低字节的时候,
定时器中断来了,中断响应处理之后sys1ms肯定变成0x******100,又回到移动次低字节,
这个时候次低字节不再是原来值,而是多1了,
所以出现,执行【TStar=sys1ms】之后,TStar=0x*****1ff,而sys1ms=0x******100,
temp=(sys1ms-TStar);的结果就是:
(0x******100)-(0x*****1ff)=FFFFFF01=-255
回复

使用道具 举报

9#
ID:19715 发表于 2014-8-10 23:23 | 只看该作者
数值中的‘* ’可以是任意十六进制数,所以出现这种可能性就更多
回复

使用道具 举报

10#
ID:19715 发表于 2014-8-10 23:36 | 只看该作者
既然问题找到了,就可以解决了,
加上(sys1ms加1)标志位:incOK
void main()
{        
        //1ms@12.000MHz
        TMOD &= 0xF0;                //设置定时器模式
        TMOD |= 0x01;                //设置定时器模式
        TL0 = 0x18;                //设置定时初值
        TH0 = 0xFC;                //设置定时初值
        TF0 = 0;                //清除TF0标志
        TR0 = 1;                //定时器0开始计时

        //4800bps@12.000MHz
        PCON |= 0x80;                //使能波特率倍速位SMOD
        SCON = 0x50;                //8位数据,可变波特率
        TMOD &= 0x0F;                //清除定时器1模式位
        TMOD |= 0x20;                //设定定时器1为8位自动重装方式
        TL1 = 0xF3;                //设定定时初值
        TH1 = 0xF3;                //设定定时器重装值
        ET1 = 0;                //禁止定时器1中断
        TR1 = 1;                //启动定时器1

        EA=1;//开启总中断
        ES=1;//开串口中断
        ET0=1;//开定时器0中断
                incOK=0;   //sys1ms加1标志位就可以incOK
        while(1)
        {
                ulong32 temp;
           if(incOK)        //是加上1,才做减法                {
                        TStar=sys1ms;//扫描周期记录初始值
                   temp=(sys1ms-TStar);
                incOK=0;
                }
                if(TEnd<=temp)TEnd=temp;//扫描周期计时               
           if((TEnd!=256) && (TEnd!=-255))led0=0;
                if(TEnd==256)led1=0;
                if(TEnd==-255)led2=0;
        }
}
回复

使用道具 举报

11#
ID:19715 发表于 2014-8-10 23:38 | 只看该作者
#include<reg52.h>
#define uchar8 unsigned char //8
#define uint16 unsigned int //16
#define ulong32 unsigned long //32
sbit led0=P2^0;
sbit led1=P2^1;
sbit led2=P2^2;
bit  incOK;
ulong32 TStar=0;//记录时间
ulong32 TEnd=0;
ulong32 sys1ms=0;//1ms累加,可以一直累加到49.7天
void Timer0_Interrupt() interrupt 1//定时器0 1ms 中断程序
{        
        TL0 = 0x18;                //设置定时初值
        TH0 = 0xFC;                //设置定时初值
        sys1ms++;                //1ms累加,可以一直累加到49.7天
                incOK=1;
}
void Comm_Interrupt() interrupt 4//串行中断程序
{
        if(TI)
        {
                TI = 0;
        }
        else if(RI)
        {        
              unsigned  char value;
                value=SBUF;
                        RI=0;
        }
}
void main()
{         //1ms@12.000MHz
        TMOD &= 0xF0;                //设置定时器模式
        TMOD |= 0x01;                //设置定时器模式
        TL0 = 0x18;                //设置定时初值
        TH0 = 0xFC;                //设置定时初值
        TF0 = 0;                //清除TF0标志
        TR0 = 1;                //定时器0开始计时
        //4800bps@12.000MHz
        PCON |= 0x80;                //使能波特率倍速位SMOD
        SCON = 0x50;                //8位数据,可变波特率
        TMOD &= 0x0F;                //清除定时器1模式位
        TMOD |= 0x20;                //设定定时器1为8位自动重装方式
        TL1 = 0xF3;                //设定定时初值
        TH1 = 0xF3;                //设定定时器重装值
        ET1 = 0;                //禁止定时器1中断
        TR1 = 1;                //启动定时器1

        EA=1;//开启总中断
        ES=1;//开串口中断
        ET0=1;//开定时器0中断
                incOK=0;   //sys1ms加1标志位就可以incOK
        while(1)
        {
                ulong32 temp;
                                if(incOK)  //是加上1,才做减法
                                        {
                        TStar=sys1ms;//扫描周期记录初始值
                        temp=(sys1ms-TStar);
                                        incOK=0;
                                        }
                if(TEnd<=temp)TEnd=temp;//扫描周期计时               
                if((TEnd!=256) && (TEnd!=-255))led0=0;
                if(TEnd==256)led1=0;
                if(TEnd==-255)led2=0;
        }
}
回复

使用道具 举报

12#
ID:19715 发表于 2014-8-10 23:46 | 只看该作者
32位的二进制做计算,消耗时间多一点。
这样可以确保在计算过程,不来中断
回复

使用道具 举报

13#
ID:19715 发表于 2014-8-10 23:53 | 只看该作者
同样道理:原来症状2也可能出现于
TEnd==-65535,不过机辨少很多
回复

使用道具 举报

14#
ID:19715 发表于 2014-8-11 10:35 | 只看该作者
也可以停止定时器TR0 =0;      
处理好数据之后,
才重新启动定时器, TR0 =1;
用定时初始值为0,        TL0 =TH0 = 0;
加上16位中断溢出(比如uchar型:sys65ms),
构成18位二进制定时器
这样定时时间可以去到16秒(晶振12mhz)

#include<reg52.h>
#define uchar8 unsigned char //8
#define uint16 unsigned int //16
#define ulong32 unsigned long //32
sbit led0=P2^0;
sbit led1=P2^1;
sbit led2=P2^2;
ulong32 TEnd=0;        //记录时间
uchar8 sys65ms=0;//65ms累加,可以一直累加到16秒
void Timer0_Interrupt() interrupt 1//定时器0 1ms 中断程序
{        
        sys65ms++;                //1ms累加,可以一直累加到49.7天
}
void Comm_Interrupt() interrupt 4//串行中断程序
{
        if(TI)
        {
                TI = 0;
        }
        else if(RI)
        {        
              unsigned  char value;
                value=SBUF;
                        RI=0;
        }
}
void main()
{         //1ms@12.000MHz
        TMOD &= 0xF0;                //设置定时器模式
        TMOD |= 0x01;                //设置定时器模式
        TL0 = 0;                //从0开始跑时
        TH0 = 0;                //从0开始跑时
        TF0 = 0;                //清除TF0标志
        TR0 = 1;                //定时器0开始计时
        //4800bps@12.000MHz
        PCON |= 0x80;                //使能波特率倍速位SMOD
        SCON = 0x50;                //8位数据,可变波特率
        TMOD &= 0x0F;                //清除定时器1模式位
        TMOD |= 0x20;                //设定定时器1为8位自动重装方式
        TL1 = 0xF3;                //设定定时初值
        TH1 = 0xF3;                //设定定时器重装值
        ET1 = 0;                //禁止定时器1中断
        TR1 = 1;                //启动定时器1

           EA=1;//开启总中断
        ES=1;//开串口中断
        ET0=1;//开定时器0中断
        while(1)
        {       ulong32 temp;
               TR0=0;
                       temp=sys65ms;//扫描周期记录
               temp<<=8;        //扫描周期记录
               temp+=TH0;        //扫描周期记录
               temp<<=8;        //扫描周期记录
               temp+=TL0;   //扫描周期记录
                               
                sys65ms = 0;     //重新计时
                TL0 = 0;         //重新计时      
                        TH0 = 0;         //重新计时   
                TR0=1;                               
                if(TEnd<=temp)TEnd=temp;//扫描周期计时              
           if((TEnd!=256) && (TEnd!=-255))led0=0;
                if(TEnd==256)led1=0;
                if(TEnd==-255)led2=0;
        }
}
回复

使用道具 举报

15#
ID:64911 发表于 2014-8-11 12:18 | 只看该作者
本帖最后由 xiehuipeng 于 2014-8-11 12:34 编辑
明白 发表于 2014-8-11 10:35
也可以停止定时器TR0 =0;      
处理好数据之后,
才重新启动定时器, TR0 =1;

非常感谢你 解决我大问题了 应该就是这个原因。。
是这样的 我原本程序比这个复杂很多 因为有以上这个问题 才把程序精简到最小化 便于大家看懂。
如果按照你这种解决方法 那我整个程序的计时架构就整个变掉了
里面很多地方都要32bit计时 精度要1ms  大概要10几或者20个独立的计时器 每个计时器 是靠2个变量 初值和终值 互不干涉 独立计时
计时规则就是:取当前syst1ms为初值,计时到终值就是当前syst1ms -初值

原本程序的主函数是这样的
void main()
{        
        Timer0Init();//0-0定时器0初始化
        UartInit();//0-1串口初始化
        EA=1;//开启总中断
        ES=1;//开串口中断
        ET0=1;//开定时器0中断
        while(1)
        {
                ulong32 temp;
                x_sysScan.TStar=x_sys1ms;//最大扫描周期 记录初始值

                GetInput();//1-输入采集
                Check();//2-主程序检查
                SetOutput();//3-输出刷新
                CommJudge();//4-通讯逻辑判断

                temp=x_sys1ms-x_sysScan.TStar;
                if(x_sysScan.TEnd<=temp)x_sysScan.TEnd=temp;//最大扫描周期计算
        }
}


回复

使用道具 举报

16#
ID:19715 发表于 2014-8-11 13:26 | 只看该作者
15个独立的32bit计时器,
入栈出栈需要20条指令,
每一个32bit计时器,自加自判大概25条指令
这样中断要执行的指令数差不多:400条
假如平均每一条指令需要时间2us,
中断周期1毫秒,能够执行500条指令,
自身就用去400条,
剩下的100条指令时间,还要被串口所中断,
。。
。。
主程序“跑得动”吗

回复

使用道具 举报

17#
ID:64911 发表于 2014-8-11 13:41 | 只看该作者
本帖最后由 xiehuipeng 于 2014-8-11 13:42 编辑
明白 发表于 2014-8-11 13:26
15个独立的32bit计时器,
入栈出栈需要20条指令,
每一个32bit计时器,自加自判大概25条指令

不是这样的
定时器0中断程序只有这3条指令 因为sys1ms可以一直累加运行49.7天,一般机器运行一天就关机重启了
在主程序里 这20个32bit的计时器的初值 和终值计算都是用这同一个sys1ms来计算的
比如 ulong32 sys_time_初值[20],sys_time_终值[20];
void mian()
{
if(条件1==1)sys_time_初值[0]=sys1ms;
if(条件2==1)sys_time_初值[1]=sys1ms;
if(条件3==1)sys_time_初值[2]=sys1ms;
if(条件1==0)sys_time_终值[0]=sys1ms-sys_time_初值[0];
if(条件2==0)sys_time_终值[1]=sys1ms-sys_time_初值[1];
if(条件3==0)sys_time_终值[2]=sys1ms-sys_time_初值[2];
}
void Timer0_Interrupt() interrupt 1//0-0定时器0 1ms 中断程序
{        
        TL0 = 0x18;                //设置定时初值
        TH0 = 0xFC;                //设置定时初值
        sys1ms++;                //1ms累加,可以一直累加到49.7天
}这就是我程序的核心思想。。
回复

使用道具 举报

18#
ID:19715 发表于 2014-8-11 13:48 | 只看该作者
这样可以用TR0停止和启动定时器,
回复

使用道具 举报

19#
ID:19715 发表于 2014-8-11 13:49 | 只看该作者
void Timer0_Interrupt() interrupt 1//0-0定时器0 1ms 中断程序
{        
        TL0 = 0x18;                //设置定时初值
        TH0 = 0xFC;                //设置定时初值
        sys1ms++;                //1ms累加,可以一直累加到49.7天
}
是3句c语言,不是3条指令
回复

使用道具 举报

20#
ID:64911 发表于 2014-8-11 13:50 | 只看该作者
明白 发表于 2014-8-11 13:49
void Timer0_Interrupt() interrupt 1//0-0定时器0 1ms 中断程序
{        
        TL0 = 0x18;         ...

嗯嗯 呵呵 写错了
回复

使用道具 举报

21#
ID:64911 发表于 2014-8-11 13:59 | 只看该作者
明白 发表于 2014-8-11 13:48
这样可以用TR0停止和启动定时器,

本来1ms定时器会间隔1ms累加 我如果在主程序里频繁地停止和启动定时器 那应该就不准了吧 至少主程序执行消耗的时间就不计算了吧。
回复

使用道具 举报

22#
ID:19715 发表于 2014-8-11 14:00 | 只看该作者
程序方式:
void main()
{        各个初始化
        while(1)
        {               TR0=0;
                       //记录时间:time1
                //记录时间:time2
                //记录时间:time3
              开始重新计时                                 
                 //重新计时time1
                 //重新计时 time2   
                  //重新计时 time3   
                TR0=1;   
                  其他事情1                           
                 其他事情2   
        }
}
回复

使用道具 举报

23#
ID:19715 发表于 2014-8-11 14:07 | 只看该作者
1ms,分辨率最大就只有1ms,
我前面介绍的18位二进制定时器,分辨率可以去到1us
相差多少倍,
程序很多地方是依靠自己的纠结,自己去衡量,然后自己做出处理,
想高精度,看一下汇编,
回复

使用道具 举报

24#
ID:64911 发表于 2014-8-11 14:31 | 只看该作者
明白 发表于 2014-8-11 14:07
1ms,分辨率最大就只有1ms,
我前面介绍的18位二进制定时器,分辨率可以去到1us
相差多少倍,

谢谢大哥 我看懂你这个计时方式了 非常巧妙 学习了。。
回复

使用道具 举报

25#
ID:19715 发表于 2014-8-11 15:15 | 只看该作者
xiehuipeng 发表于 2014-8-11 14:31
谢谢大哥 我看懂你这个计时方式了 非常巧妙 学习了。。

这种计时方式,不用重装定时器初值,其实也是一种“纯天然”重装定时器初值
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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