基于定时器中断的单片机交通灯控制系统

1.基础知识
1.1、中断源

中断源符号 名称 中断引起原因 中断号
/INT0 外部中断0 P3.2引脚低电平或下降沿信号 0
T0 定时器0中断 定时,计数器0计数回0溢出 1
/INT01 外部中断1 P3.3引脚低电平或下降沿信号 2
T1 定时器1中断 定时/计数器1计数回0溢出 3
TI/RI 串行口中断 串行通信完成一帧数据发送或接收引起中断 4

1.2、定时器中断
设置模式:这里用的是定时器0中断,中断模式为1。


 void zhongduan()
 {
  TMOD=0x01;
  TH0=(65536-45872)/256;
  TL0= (65536-45872)%256;

  EA=1;
  ET0=1;
  TR0=1;
 }

(1)TMOD=0x01 表示设置计时器 0 为工作方式 1,这种方式使用计数器模式,并且计数器的值被装入 TH0 和 TL0 寄存器中。
(2)TH0=(65536-45872)/256 和 TL0=(65536-45872)%256 是设置定时器计数初值的代码。65536是2的16次方,定时器是加1计数器16位。晶振为11.0592,故所记次数应为45872,计时器每隔50000(50ms)微秒发起一次中断。45872的来历,为(50000/(12*(1/11.0592M))
(3)EA=1,ET0=1,TR0=1 是开启中断的代码。EA 表示总中断使能开关,设置为 1 时表示打开所有中断。ET0 表示定时器 0 的中断使能开关,设置为 1 时表示打开定时器 0 中断。TR0 表示定时器 0 的运行控制开关,设置为 1 时表示启动定时器 0 的计数器开始计时。

void Time0() interrupt 1
{
 TH0=(65536-45872)/256 ;
 TL0=(65536-45872)%256 ;
 count++;

}

在程序的结束,要重新设置定时器0的重载值,以便定时器可以继续计时并产生下一个中断。由于定时器的工作方式是在计数器减到0时产生中断,因此每次产生中断后都需要重新设置定时器的重载值,从而控制中断的时间间隔。这里的值根据定时器的时钟频率和所需的中断时间间隔计算而得。
2、代码
2.1、宏定义、头文件与延时
大多数程序都会用到,前面的博客有说,这里就不说了

#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int

sbit duan=P2^6;
sbit wei =P2^7;

uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,
                        0x07,0x7f,0x6f};
uchar shijian ,deng = 2;
uchar count;


//延时
void delay(uint k)
{
uint i,j;
 for(i=0;i<k;i++)
 {
   for(j=0;j<113;j++)
   {
   	 ;
   }  
 }
}

解释一下这里的全局变量count,是计数用的,是整个程序定时的关键。
变量deng就是指灯的意思,将deng初始化为2是因为最开始需要亮绿灯,因此将deng设置为2,表示初始状态为绿灯。如果将deng初始值设置为其他数字,例如0或1或3,则初始状态为红灯、黄灯或黄灯(绿灯和黄灯之间的转换),这都不符合交通信号灯的正常工作流程,因此将deng初始值设置为2最合适。(不懂得话。可以看一下后面的红绿灯程序的switch,case)
2.2、数码管显示

 void display(uchar a,b)
 {

 P0=0xfb;
 wei=1;
 wei=0;
 P0=table[a];
 duan=1;
 duan=0;

 delay(5);


 P0=0xf7;
 wei=1;
 wei=0;
 P0=table[b];
 duan=1;
 duan=0;

 delay(5);


 }

这段代码实现了数码管的显示功能。在这里,a和b分别是需要显示的十位数和个位数。具体的显示流程是:

选择第一个数码管(P0=0xfb),并将要显示的十位数写入P0口。

打开第一个数码管的使能(wei=1),并在一段时间后关闭(wei=0)。

选择第二个数码管(P0=0xf7),并将要显示的个位数写入P0口。

打开第二个数码管的使能(wei=1),并在一段时间后关闭(wei=0)。

在每个数码管的使能开启后,相应的数字通过将数码管段选(duan)开启来实现。而在开启使能和关闭使能之间需要适当的延时,以确保数字能够正确显示。

段选根据端口连线

2.3、中断部分在本文第一部分讲过了,这里不在赘述

 void zhongduan()
 {
  TMOD=0x01;
  TH0=(65536-45872)/256;
  TL0= (65536-45872)%256;

  EA=1;
  ET0=1;
  TR0=1;
 }

2.4、红绿灯显示

void redgreenyellow()
{
 uchar shi,ge;

  if(shijian==0)
  {
   switch(deng)
   {
    case 0:shijian=10;deng=1;break;//红灯
	case 1:shijian=5;deng=2;break; //黄灯
	case 2:shijian=10;deng=3;break;//绿灯
	case 3:shijian=5;deng=0;break; //黄灯
   }

   switch(deng)
   {
    case 1:P1=0xfc;break;//红灯
	case 2:P1=0xcf;break; //黄灯
	case 3:P1=0xf3;break;//绿灯
	case 0:P1=0xcf;break; //黄灯
   }

  }

  shi = shijian/10;
  ge  = shijian%10;
  display(shi,ge);

  if(count==20)
  {
	count=0;
	shijian--;
  }
  
}

这段代码实现了红绿灯的控制和倒计时显示的功能。具体来说,代码中定义了两个变量 shi 和 ge 分别表示时间的十位数和个位数。在 if (shijian==0) 语句块中,程序首先根据当前的红绿灯状态 deng 设置下一个状态,并更新相应的倒计时时间 shijian。然后根据新的红绿灯状态 deng 在数码管上显示相应的时间。最后,程序检测到 count 的值为 20 时,说明经过了 20 次中断周期,即 1 秒钟,此时将 shijian 减一,更新时间,并将 count 重置为 0,以便下一次计时。

需要注意的是,代码中对 deng 变量的值进行了多次判断和修改。这是因为程序需要根据当前的红绿灯状态来决定下一个状态,所以需要不断地对 deng 变量进行判断和修改。

同时,注意一个小细节case空格0!!

2.5、主程序
这里比较简单,是对其他程序的综合调用

//主函数
void main()
{
zhongduan();
while(1)
{
 redgreenyellow();
}

}

2.6、小尾巴
最后一点,是中断程序服务程序非常重要,没有它,前面的不能实现定时功能

void Time0() interrupt 1
{
 TH0=(65536-45872)/256 ;
 TL0=(65536-45872)%256 ;
 count++;

}

在这个程序中,中断服务程序是通过定时器中断触发的,每当定时器溢出时,中断服务程序会被执行一次。在这个中断服务程序中,count变量的作用是用来计时的。每次中断服务程序被执行时,count的值会加1,这样可以通过累加count的值来计算时间。在这个程序中,count的单位是50毫秒,所以count累加20次就代表1秒过去了。

3.完整代码

//红绿灯,需要用到的模块有数码管显示秒数,定时中断,以及相应的灯。
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int

sbit duan=P2^6;
sbit wei =P2^7;

uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,
                        0x07,0x7f,0x6f};
uchar shijian ,deng = 2;
uchar count;


//延时
void delay(uint k)
{
uint i,j;
 for(i=0;i<k;i++)
 {
   for(j=0;j<113;j++)
   {
   	 ;
   }  
 }
}



 //数码管显示部分
 void display(uchar a,b)
 {

 P0=0xfb;
 wei=1;
 wei=0;
 P0=table[a];
 duan=1;
 duan=0;

 delay(5);


 P0=0xf7;
 wei=1;
 wei=0;
 P0=table[b];
 duan=1;
 duan=0;

 delay(5);


 }

  //中断部分,采用定时器中断0

 void zhongduan()
 {
  TMOD=0x01;
  TH0=(65536-45872)/256;
  TL0= (65536-45872)%256;

  EA=1;
  ET0=1;
  TR0=1;
 }


 //红绿灯显示模块
void redgreenyellow()
{
 uchar shi,ge;

  if(shijian==0)
  {
   switch(deng)
   {
    case 0:shijian=10;deng=1;break;//红灯
	case 1:shijian=5;deng=2;break; //黄灯
	case 2:shijian=10;deng=3;break;//绿灯
	case 3:shijian=5;deng=0;break; //黄灯
   }

   switch(deng)
   {
    case 1:P1=0xfc;break;//红灯
	case 2:P1=0xcf;break; //黄灯
	case 3:P1=0xf3;break;//绿灯
	case 0:P1=0xcf;break; //黄灯
   }

  }

  shi = shijian/10;
  ge  = shijian%10;
  display(shi,ge);

  if(count==20)
  {
	count=0;
	shijian--;
  }
  
}


//主函数
void main()
{
zhongduan();
while(1)
{
 redgreenyellow();
}

}


void Time0() interrupt 1
{
 TH0=(65536-45872)/256 ;
 TL0=(65536-45872)%256 ;
 count++;

}

4.运行结果
动态的,截几个图演示一下。


物联沃分享整理
物联沃-IOTWORD物联网 » 基于定时器中断的单片机交通灯控制系统

发表评论