微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > MCU和单片机设计讨论 > 请教大哥大姐们51单片机一个定时器,多处程序怎么用

请教大哥大姐们51单片机一个定时器,多处程序怎么用

时间:10-02 整理:3721RD 点击:
一个定时器,多个程序用,(如主程序中让每进20次定时中断(1秒),让灯亮一下,这样循环着,当触发外部中断时,可以是1秒也可以大于1秒,响应另一个触发事件)

你可以弄一个变量假设是N,每中断一次对N计数,根据N的数值来判断执行那个程序

大家帮我分析一下问题如下:1、现在是主程序动显在那0至7,我要的是1-8动显在那;2、触发外部中断后,加数器不动,可能是一个定时器,两个地方用,什么地方没用正确,外部中断的部分计时器没起作用;3、编译程序时出现一个警告。其它正常
#include<reg52.h>
//载入51单片机头文件
#include<intrins.h>
//载入51涵数头文件
#define uchar unsigned char
//宏定义unsigned char为 uchar
#define uint unsigned int
//宏定义unsigned int 为uint
sbit wela=P3^7;
//位定义,控制数码管有几个数量的373锁存器端口接在该I/O
sbit dula=P2^0;
//位定义,控制数码管显示什么字符的373锁存器端口接在该I/O
uchar k,j,temp,led,dd,tt,qian,bai,shi,ge;
//单字节全局变量
uint ptemp;
//双字节全局变量
uchar code dutable[]={
//定义数码管显示,code为表格的意思,该表格取名为dutable
0xc0,0xf9,0xa4,0xb0,0x99,
0x92,0x82,0xf8,0x80,0x90
//根据数码管硬件连接图写出0-9的16进制数
};
uchar code wetable[]={
0x01,0x02,0x04,0x08,
0x10,0x20,0x40,0x80
};
//根据数码管硬件连接图写出1-8个数码管亮的位
//…………以下延时程序…………
void delay(uint z)         // 这里为什么用uint(双字节),而不是用uchar(单字节,值范围256)
{
uint x,y;
for(x=z;x>0;x--)
             for(y=1000;y>0;y--);
}
//…………以上延时程序…………
//………………子程序……………………
void zuchngxiu()
{                  
                if(dd==1) /*这里进入定时器次和触发定时器的时间一起控制显示速度,现在这样就属于动太扫描*/
                {
                                dd=0; /*如果进入到20次定时器,那么将定时器里的变量dd清0,便于下一次计时*/
                                  tt++;//用于外部中断时间判断
                                if(j==8) //检测j是否等于9
                                j=0;  //如果是等于9就置0
//……………………………………
P1=0x00;//P1口全部送低电平,目的是将这低电平保存在点阵的高电平脚上,使用其不亮;
P3=0x07;/*P3口关闭高位开关(00000111)其中有三个脚接点阵高点位的锁存端,其中还有外部中断*/                                
P2=0x00; /*P2口接了三种类型,点阵低电平的锁存端5个,点阵高点位的锁存端2个,还一个是控制
8个数码管段选的锁存端,这样点阵高电平脚全部由锁器端锁存为低电平了*/
//……………关闭点阵…………………
                                wela=1;        //打开位选
                                P1=wetable[j]; //P1口取wetable位码表
                                wela=0;//关闭位选
                                j++; //每取一次位码表自加1
                                if(k>=8)  /*检测k是否大于等于11,而不是k=10(0-9)数显示,为会下面会用到*/
                                k=0;        //如果上面K大于等于11,就将K清0 ,这样做的目的是让其显示0-9的10个数,不然后一直加上去了
                                dula=1;         //打开段选,是显示数字
                                P1=dutable[k]; //P1端口取dutable数码表中值,
                                dula=0;                   //关闭段选         
                                k++; //k自加1,如果不自加1怎么知道它现在取的是第几位数了
                }         
}
//……………………………………

//………………以下是累加显示子程序…………………………
void display(uchar qian,uchar bai,uchar shi,uchar ge)
{
   wela=1;
P1=0x01;
wela=0;
  dula=1;
P1=dutable[qian];
dula=0;
delay(5);
//………………………………  
  wela=1;
P1=0x02;
wela=0;
  dula=1;
P1=dutable[bai];
dula=0;
  delay(15);
//………………………………
   wela=1;
P1=0x04;
wela=0;
  dula=1;
P1=dutable[shi];
dula=0;
  delay(15);
//………………………………
   wela=1;
P1=0x08;
wela=0;
  dula=1;
P1=dutable[ge];
dula=0;
  delay(5);
}
//………………以上是累加显示子程序………………
//……………………以下初始化程序……………………
void init()
{
ptemp=0; //给累加器一个初值
EA=1; //开总中断
ET0=1;//定时器/计数器0中断
TMOD=0x01; //设为定时器,单触发,01工作方式;
TH0=(65536-5000)/256; //装高位初值
TL0=(65536-5000)%256;  //装低位初值
TR0=1;//启动定时器

}
//…………………以上初始化程序………………………
//………………………以下主程序……………………………
void main()
{         
        init();//初始化子程序
                EX0=1;//开外部中断0
                IT0=0;//外部中断0以电平方式触发
                PX0=0;//设外部中断0为最低优先级
                PT0=1;//设该定时器为最高级优先级
                P2=0x00;//程序入口就关闭点阵锁存端,要是不关闭一会数码管语句时会让点阵也显示着内容
                P3=0x04;//给P3口除外部中断0高点平外,都低电平。        外部中断0是低平电有效的
                wela=1;
                //打开控制数码管工作的373端口,输入高电平有效,相当于输入与输出端直通
                P1=0xff;        
                //送入让那几个数码管亮的16进制数
                wela=0;
                //关闭控制数码管工作373端口,这样373就保持住输入端上一次输入的值
                dula=1;
                //打开控制数码管显示什么数字的373端口
                P1=0x00;
                /*输入让数码管显示什么数,这里是全部亮就是日字,作为检测一下数码管
                 有无坏点*/
                dula=0;
                //关闭控制数码管显示什么数字的373端口
                 P1=0xff;//P1送入高电平
P2=0x3e; //打开低位开关
P1=0x00; //P1送入低电平
P2=0xc0;//关闭位开关                        
P3=0x6f;  //打开高位开关
P1=0xff;//P1送入高电平
                led=P0=0xfe;  //这里给led赋个初值这样下面定时器里led左移才知道从什么数开始移
                delay(15);        //这里加延时程序,某值是时段肯定会和其它地方冲突造成不定时延时现象
                //调用延时子程序
        while(1)
        {
zuchngxiu(); //调用子程序
        }  
}
//        ……………………定时器0………………………………
void exter1() interrupt 1        /*exter1是中断名,后面1是单片机中断中该定时器/计数器0的中断序号 */
         {
                        TH0=(65536-5000)/256;//求抹,将整数部分装入高8位中
                        TL0=(65536-5000)%256; //求余,将余数部分装入低8位中
                        //这里的50000就是50ms(毫秒)
                        dd++;  // 这里的dd时50ms触发一次中断,每到50ms计一次数,便于其它调用
                        led=_crol_(led,1); //将上面的led值进入左移1位
                        P0=led;         //左移一位得到的值,赋予给P0口
                                 }
//…………………………这里定时器中断优先等级比外部中断0高…………
//……………触发外部中断0后始终循环…………
void exter0() interrupt 0         /*exter0是断名,后面0是单片机中断中该外部中断0的中断序号 */
{  //1
        init();
        ptemp=0;  //给加累数初值0
        while(1)
                {  //2
                if(tt>=1)//判断tt是否大于等于1,这里tt就是主程序里dd=20,tt自加1
                {  //3
                tt=0; //置0重新计时
                ptemp++;//初值自加1
                if(ptemp==10000)
                {  //4
                ptemp=0;
                if(ptemp==0)
                { //5
                wela=1;  //打开位选
                P1=0xff; //将P1口置数11111111全部高电平, 打开8个数码管
                wela=0;  //关闭位选
                dula=1;//打开段选
                temp=0xfe;        //赋temp值为11111110 ,也就是数码管的a段
                P1=temp; //将temp的值赋予给P1口
                delay(10);        //调用延时程序
                while(1)
                {         //6
                if(temp==0xdf) /*检测temp是否等于(高位11011111低位),因为数码管七段加一个DP点
                我们不要DP(P1.7)点和G段(P1.6)亮*/
                temp=0x7f; //置位到DP(P1.7)点,也就是P1.6和P1.7跳过(高位0111 1111低位)
                temp=_crol_(temp,1);  //左移一位
                P1=temp;          //移位后的值赋予给P1口
                delay(8);          //调用延碧
                }        //6
                qian=temp/1000;
                bai=temp%1000/100;
                shi=temp%1000%100/10;
                ge=temp%10;
                display(qian,bai,shi,ge);
                }
                }
                }                        
                }
}


中断里怎么还有While(1)啊 ,程序怎么中断返回啊

3楼说的对 中断里面 不应该有while(1)语句吧 要不就该无线循环了

找到问题了,还加了一小部分其它循环

/* 正确的如下:还有一个问题就是触发外部中断后进入第三状态时,怎么退出来,
进入中断后第1状态:每隔1秒加1,加到100数,跳到
第2状态:8个数码管各自点亮口字,循环5次后进入第3状态:8个数码管组成的大口子,循环5次后,
让其再回到第1状态这样循环着除非单片机复位,现在问题停在第3状态循环出不来,没有可调用的涵数,
待学习请助中,请高手们指点一下迷区*/
#include<reg52.h>
//载入51单片机头文件
#include<intrins.h>
//载入51涵数头文件
#define uchar unsigned char
//宏定义unsigned char为 uchar
#define uint unsigned int
//宏定义unsigned int 为uint
sbit wela=P3^7;
//位定义,控制数码管有几个数量的373锁存器端口接在该I/O
sbit dula=P2^0;
//位定义,控制数码管显示什么字符的373锁存器端口接在该I/O
sbit kk=P3^2;
uchar k,j,temp,led,dd,tt,qian,bai,shi,ge,rr,yj,ee;
//单字节全局变量
uint ptemp;
//双字节全局变量
uchar code dutable[]={
//定义数码管显示,code为表格的意思,该表格取名为dutable
0xc0,0xf9,0xa4,0xb0,0x99,
0x92,0x82,0xf8,0x80,0x90
//根据数码管硬件连接图写出0-9的16进制数
};
uchar code wetable[]={
0x01,0x02,0x04,0x08,
0x10,0x20,0x40,0x80
};
//根据数码管硬件连接图写出1-8个数码管亮的位
//…………以下延时程序…………
void delay(uint z)  // 这里为什么用uint(双字节),而不是用uchar(单字节,值范围256)
{
uint x,y;
for(x=z;x>0;x--)
      for(y=1000;y>0;y--);
}
//…………以上延时程序…………
//………………子程序……………………
void zuchngxiu()
{     
  if(dd==1) /*这里进入定时器次和触发定时器的时间一起控制显示速度,现在这样就属于动太扫描*/
  {
    dd=0; /*如果进入到20次定时器,那么将定时器里的变量dd清0,便于下一次计时*/
      tt++;//用于外部中断时间判断
   
//……………………………………
P1=0x00;//P1口全部送低电平,目的是将这低电平保存在点阵的高电平脚上,使用其不亮;
P3=0x07;/*P3口关闭高位开关(00000111)其中有三个脚接点阵高点位的锁存端,其中还有外部中断*/   
P2=0x00; /*P2口接了三种类型,点阵低电平的锁存端5个,点阵高点位的锁存端2个,还一个是控制
8个数码管段选的锁存端,这样点阵高电平脚全部由锁器端锁存为低电平了*/
//……………关闭点阵…………………
    wela=1; //打开位选
    P1=wetable[j]; //P1口取wetable位码表
    wela=0;//关闭位选
    j++; //每取一次位码表自加1
      if(j==8) //检测j是否等于9
    j=0;  //如果是等于9就置0
    dula=1;  //打开段选,是显示数字
    P1=dutable[k]; //P1端口取dutable数码表中值,
    dula=0;     //关闭段选   
    k++; //k自加1,如果不自加1怎么知道它现在取的是第几位数了
    if(k>=9)  /*检测k是否大于等于9,因为要显示的数是1-8的数*/
    k=1; /*如果上面K大于等于9,就将K清1 ,而不是将K清0,这样做的目的是让其显示从1数开始显示9位数(1-8),
    如果清0,那就会从0开始显数*/
  }  
}
//……………………………………
//………………以下是累加显示子程序…………………………
void display(uchar qian,uchar bai,uchar shi,uchar ge)
{
   wela=1;
P1=0x01;
wela=0;
  dula=1;
P1=dutable[qian];
dula=0;
delay(1);
//………………………………  
  wela=1;
P1=0x02;
wela=0;
  dula=1;
P1=dutable[bai];
dula=0;
  delay(1);
//………………………………
   wela=1;
P1=0x04;
wela=0;
  dula=1;
P1=dutable[shi];
dula=0;
  delay(1);
//………………………………
   wela=1;
P1=0x08;
wela=0;
  dula=1;
P1=dutable[ge];
dula=0;
  delay(1);
}
//………………以上是累加显示子程序………………
//……………………以下循环8位数码管组成的口子子程序…………………………
void  xunhuankou()
{
dula=1; //打开段选,
  P1=0xfe; //显示a段,这时8个数码管都显示a段的,所以在这前要先选好位
    dula=0;
  delay(10);

   
   wela=1;
   P1=wetable[yj]; //P1口取wetable位码表 也就是第一们数码管
delay(3);  
P1=0x02;
delay(3);
  P1=0x04;
    delay(3);
  P1=0x08;
    delay(3);
  P1=0x10;
   delay(3);
  P1=0x20;
  delay(3);
  P1=0x40;  
      delay(3);
  P1=0x80;  
    delay(3);  
wela=0;
//……………………
dula=1;
P1=0xfd;   
delay(3);
P1=0xfb;
delay(3);
P1=0xf7;
dula=0;
delay(3);
//……………………
wela=1;
P1=0x80;
delay(3);
P1=0x40;
delay(10);
P1=0x20;
delay(3);
P1=0x10;
delay(3);
P1=0x08;
delay(3);
P1=0x04;
delay(3);
P1=0x02;
delay(3);
P1=0x01;
wela=0;
delay(3);
//…………………………
dula=1;
P1= 0xef;
delay(3);
P1= 0xdf;
delay(3);
dula=0;
//………………………………
}
//……………………以上循环8位数码管组成的口子子程序………………………………

//……………………以下初始化程序……………………
void init()
{
kk=1; //给外部中断0由软件置高电平
ptemp=0; //给累加器一个初值
EA=1; //开总中断
ET0=1;//定时器/计数器0中断
TMOD=0x01; //设为定时器,单触发,01工作方式;
TH0=(65536-5000)/256; //装高位初值
TL0=(65536-5000)%256;  //装低位初值
TR0=1;//启动定时器
}
//…………………以上初始化程序………………………
//……………………以下是外部中断中循环子程序……………………………………
void zhongduan()
{
   init();//初始化程序
ptemp=0;  //给加累数初值0
while(1)
  {  
  if(tt>=1)//判断tt是否大于等于1,这里tt就是主程序里dd=20,tt自加1
  tt=0; //置0重新计时
  ptemp++;//初值自加1
  if(ptemp==101) //给累加数定一个最大数
   
{
  ptemp=0;
  if(ptemp==0)
  {
  wela=1;  //打开位选
  P1=0xff; //将P1口置数11111111全部高电平, 打开8个数码管
  wela=0;  //关闭位选
  dula=1;//打开段选
  temp=0xfe; //赋temp值为11111110 ,也就是数码管的a段
  P1=temp; //将temp的值赋予给P1口
  delay(10); //调用延时程序
  while(1)
  {  
  temp=_crol_(temp,1);  //左移一位
  P1=temp;   //移位后的值赋予给P1口
  delay(8);   //调用延碧
  rr++;//自加1,便于下面调用(rr=6刚好循环一次口字)
        if(temp==0xdf) /*检测temp是否等于(高位11011111低位),因为数码管七段加一个DP点
  我们不要DP(P1.7)点和G段(P1.6)亮*/
  temp=0x7f; //置位到DP(P1.7)点,也就是P1.6和P1.7跳过(高位0111 1111低位)
  if(rr>=30)//就是循环5个口字
   {
   while(1)
   {
   xunhuankou();//调用循环8位数码管组成的口子子程序
   ee++;//每循环一次自加1
//……………………………………………………………………………………
//…………………有待解决小循环里的死循环…………………………………
//if(ee==5)//让8位数码管组成的口子循环5次
//问题来了,到5次后没有返回函数了,待解决(最好还是返回到外部中断一开始的地方)
//………………………………………………………………………………………………
//………………………………………………………………………………………………
    }
///
   }
}
}
  }
  qian=ptemp/1000;
  bai=ptemp%1000/100;
  shi=ptemp%1000%100/10;
  ge=ptemp%10;
  display(qian,bai,shi,ge);
  }
}

//……………………以上是外部中断中循环子程序……………………………………
//………………………以下主程序……………………………
void main()
{   
        init();//初始化子程序
  EX0=1;//开外部中断0
  IT0=0;//外部中断0以电平方式触发
  PX0=0;//设外部中断0为最低优先级
  PT0=1;//设该定时器为最高级优先级
  P2=0x00;//程序入口就关闭点阵锁存端,要是不关闭一会数码管语句时会让点阵也显示着内容
  P3=0x04;//给P3口除外部中断0高点平外,都低电平。 外部中断0是低平电有效的
  wela=1;
  //打开控制数码管工作的373端口,输入高电平有效,相当于输入与输出端直通
  P1=0xff;
  //送入让那几个数码管亮的16进制数
  wela=0;
  //关闭控制数码管工作373端口,这样373就保持住输入端上一次输入的值
  dula=1;
  //打开控制数码管显示什么数字的373端口
  P1=0x00;
  /*输入让数码管显示什么数,这里是全部亮就是日字,作为检测一下数码管
   有无坏点*/
  dula=0;
  //关闭控制数码管显示什么数字的373端口
   P1=0xff;//P1送入高电平
P2=0x3e; //打开低位开关
P1=0x00; //P1送入低电平
P2=0xc0;//关闭位开关   
P3=0x6f;  //打开高位开关
P1=0xff;//P1送入高电平
  led=P0=0xfe;  //这里给led赋个初值这样下面定时器里led左移才知道从什么数开始移
  delay(15); //这里加延时程序,某值是时段肯定会和其它地方冲突造成不定时延时现象
  //调用延时子程序
  j=0;  //给初值0
        k=1;  /*给初值1,这里为什么不是给初值0,因为k段码表也就是显示数字的表,我们现在
  让程序1开始显,而utable表第一位数0xc0是数为0数,*/
while(1)
{
zuchngxiu(); //调用子程序
  }  
}
// ……………………定时器0………………………………
void exter1() interrupt 1 /*exter1是中断名,后面1是单片机中断中该定时器/计数器0的中断序号 */
  {
   TH0=(65536-5000)/256;//求抹,将整数部分装入高8位中
   TL0=(65536-5000)%256; //求余,将余数部分装入低8位中
   //这里的50000就是50ms(毫秒)
   dd++;  // 这里的dd时50ms触发一次中断,每到50ms计一次数,便于其它调用
   led=_crol_(led,1); //将上面的led值进入左移1位
   P0=led;  //左移一位得到的值,赋予给P0口
     }
//…………………………这里定时器中断优先等级比外部中断0高…………
//……………触发外部中断0后始终循环…………
void exter0() interrupt 0  /*exter0是断名,后面0是单片机中断中该外部中断0的中断序号 */
{  
zhongduan();//调用外部中断循环子程序
}

问题原因:
1、现在是主程序动显在那0至7,我要的是1-8动显在那;
因为j和k没给初值,默认给的是0,我要的是从1开始,而k就是显示数字的码表从0开始的变量名,所以一开始的时候就给k初值为1,显示1-8就是9位数,再置1重新开始这样循环;
2、触发外部中断后,加数器不动,可能是一个定时器,两个地方用,什么地方没用正确,外部中断的部分计时器没起作用;
3、编译程序时出现一个警告。其它正常
2和3问题是同一个,在外部中断程序中,有大括号,括错位了;

谢谢。因为刚学,中断里加循环,只是想让一个程序两种或多种效果变化的效果,还没有到那么完美的水平。练习一些思路和用法,一步一步加深运用

再用一个中断啊

上面的问题解决了,触发外部中断后把while(1) 死循环换成for就可以了
//……………………以下是外部中断中循环子程序……………………………………
void zhongduan()
{
init();//初始化程序
        ptemp=0;  //给加累数初值0
        while(1)   //这个循环是让它在满足条件的情况下循环累加数显示,关系量ptemp
//        for(ptemp=0;ptemp<101;ptemp++)         //如果这里启用for,而禁用while,执行完后会返回到主函数
                {  
                if(tt>=1)//判断tt是否大于等于1,这里tt就是主程序里dd=20,tt自加1
                tt=0; //置0重新计时
                ptemp++;//初值自加1
                if(ptemp==101) //给累加数定一个最大数
        {
                ptemp=0;
                if(ptemp==0)
                {
                wela=1;  //打开位选
                P1=0xff; //将P1口置数11111111全部高电平, 打开8个数码管
                wela=0;  //关闭位选
                dula=1;//打开段选
                temp=0xfe;        //赋temp值为11111110 ,也就是数码管的a段
                P1=temp; //将temp的值赋予给P1口
                delay(10);        //调用延时程序
           for(rr=0;rr<61;rr++)
        //        while(1) //这个循环是让它满足条件下循环小口字关系量rr
                {         
                temp=_crol_(temp,1);  //左移一位
                P1=temp;          //移位后的值赋予给P1口
                delay(8);          //调用延碧
                rr++;//自加1,便于下面调用(rr=6刚好循环一次口字)
        if(temp==0xdf) /*检测temp是否等于(高位11011111低位),因为数码管七段加一个DP点
                我们不要DP(P1.7)点和G段(P1.6)亮*/
                temp=0x7f; //置位到DP(P1.7)点,也就是P1.6和P1.7跳过(高位0111 1111低位)
                if(rr>=60)//就是循环5个口字
                 {
                for(ee=0;ee<10;ee++)   //这里要是用while(1)就会死循环
        //        while(1) //这个循环是让它满足条件下循环大口字 ,关系量ee
                        {
                 xunhuankou();//调用循环8位数码管组成的口子子程序
                 ee++;//每循环一次自加1
                 }
                 }
        }       
        }       
                }       
                qian=ptemp/1000;
                bai=ptemp%1000/100;
                shi=ptemp%1000%100/10;
                ge=ptemp%10;
                display(qian,bai,shi,ge);
                }   
}   
//……………………以上是外部中断中循环子程序……………………………………

建议小编  把中断程序写的简短  最好只设置中断标志 然后在主函数调用处理函数,不然中断停留时间太长,下次终端到来会导致数据丢失。

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top