当前位置:   article > 正文

【51单片机】中断系统、定时器计数器工作原理及应用_用中断方式实现计数器

用中断方式实现计数器

【51单片机】中断系统、定时器/计数器工作原理及应用

一、中断系统

(一)中断结构

AT89S51系列单片机提供了5个中断请求源,它们分别是:外部中断0请求定时器/计数器0中断请求外部中断1请求定时器/计数器1中断请求串行口中断请求。可实现2级中断服务程序嵌套每一中断源可用软件独立控制为允许中断或关闭中断状态;每一个中断源的优先级均可用软件设置。下图为AT89S51的中断系统结构:

image-20240404183905826

各个中断触发行为总结如下表:

中断源触发行为
外部中断0(INT0*)(IT0/TCON.0=1):下降沿 (IT0/TCON.0=0):低电平
Timer0定时器0溢出
外部中断1(INT1*)(IT0/TCON.2=1):下降沿 (IT0/TCON.2=0):低电平
Timer1定时器1溢出
串行口中断发送或接受完成

(二)中断寄存器

1、中断允许寄存器IE

​ 各中断源开放或屏蔽,是由片内中断允许寄存器IE控制。IE字节地址为A8H,可进行位寻址。IE寄存器格式如图

image-20240404185338890

​ IE对中断开放和关闭实现两级控制。两级控制就是有一个总的中断开关。控制位EA (IE. 7位),当EA=0, 所有中断请求被屏蔽,CPU对任何中断请求都不接受;当EA=1时,CPU开中断,但5个中断源的中断请求是否允许,还要由IE中的低5位所对应的5个中断请求允许控制位的状态来决定。
(1)EA—中断允许总开关控制位
​ EA=0/1,所有的中断请求被屏蔽/开放。
(2)ES—串行口中断允许位
​ ES=0/1,禁止/允许串行口中断。
(3)ET0/ET1—定时器/计数器T0/T1溢出中断允许位
​ ET0/ET1=0,禁止T0/T1溢出中断。ET0/ET1=1,允许T0/T1溢出中断。
(4) EX0/EX1—外部中断0/1中断允许位
​ EX0/EX1=0,禁止外部中断0/1溢出中断。EX0/EX1=0,允许外部中断0/1溢出中断。

2、中断优先级寄存器IP

​ 中断请求源有两个中断优先级,每一个中断请求源可由软件设置为高优先级中断或低优先级中断,也可实现两级中断嵌套。

​ 所谓两级中断嵌套,就是AT89S51正在执行低优先级中断的服务所谓两级中断嵌套,就是AT89S51正在执行低优先级中断的服务程序时,可被高优先级中断请求所中断,待高优先级中断处理完毕后,再返回低优先级中断服务程序。两级嵌套如下图所示:

image-20240404191148712

IP寄存器格式如下

image-20240404191243083

(1) PS— 串行口中断优先级控制位,1一 高级; 0— 低级。
(2) PT1—T1中断优先级控制位,1一高级; 0— 低级。
(3) PX1—外部中断1中断优先级控制位,1一高级; 0—低级。
(4) PT0—T0中断优先级控制位,1一 高级; 0—低级。
(5) PX0—外部中断0中断优先级控制位,1一高级; 0—低级。

同级中断的查询次序

image-20240404191639611

如果使用C语言编程,中断查询次序号就是中断号,例如:

image-20240404191712315

3、定时器/计数器0/1控制寄存器TCON

TCON为定时器/计数器T0、T1的控制寄存器,同时也锁存T0、T1溢出中断源和外部请求中断源等,TCON的格式如下:

image-20240404192059051

4、串行口控制寄存器SCON

SCON为串行口控制寄存器,SCON的低二位锁存串口的发送中断和接收中断的中断请求标志TIRISCON格式如下

image-20240404192344678

(1) TI一串口发送中断请求标志位。CPU将1字节的数据写入串口的发送缓冲器SBUF时,就启动一帧串行数据的发送,每发送完一帧串行数据后,硬件使TI自动置“1”。CPU响应串口发送中断时,并不清除TI中断请求标志,TI标志必须在中断服务程序中用指令对其清“0”。
(2) RI一串行口接收中断请求标志位。在串口接收完一个串行数据帧,硬件自动使RI中断请求标志置“1”。CPU在响应串口接收中断时,RI标志,并不清“0”,须在中断服务程序中用指令对RI清“0”。

(三)、外部中断的应用

1、单一外中断

要求:在单片机P2口上接有8只LED.在外部中断0输入引脚(P3. 2)接一只按钮开关K1。要求将外部中断0设置为电平触发。程序启动时,P2口上的8只LED全亮。每按一次按钮开关K1,使引脚接地,产生一个低电平触发的外中断请求,在中断服务程序中,让低4位的LED与高4位的LED交替闪烁5次。然后从中断返回,控制8只LED再次全亮。

Keil代码实现

#include <REGX52.H>

void Delay(unsigned int xms)		//@11.0592MHz
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}
}

void main()
{
	EA=1;		//总中断允许
	EX0=1;	//允许外部中断0中断
	IT0=1;	//设置外部中断为下降沿触发方式
	while(1)
	{
			P2=0x00;
	}
}

void Int0_Routine() interrupt 0
{
	unsigned char i;
	EX0=0;	//禁止外部中断0中断
	for(i=0;i<5;i++)
	{
		P2=0xf0;
		Delay(500);
		P2=0x0f;
		Delay(500);
	}
	EX0=1;	//中断返回前,打开外部中断0中断
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

Proteus仿真

普中开发板实现

2、两个外中断

要求:在单片机P2口_上接有8只LED。在外部中断0输入引脚(P3.2) 接有一只按钮开关K1。在外部中断1输入引脚(P3.3) 接有一只按钮开关K2。要求K1和K2都未按下时,P2口的8只LED呈流水灯显示,仅K1 (P3.2)按下再松开时,上下各4只LED交替闪烁10次,然后再回到流水灯显示。如果按下再松开K2 (P3. 3)时,P2口的8只LED全部闪烁10次,然后再回到流水灯显示。设置两个外中断的优先级相同。

Keil代码实现

#include <REGX52.H>

void Delay(unsigned int xms)		//@11.0592MHz延时函数
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}
}

void main()
{
	EA=1;		//总中断打开
	EX0=1;
	EX1=1;	//	允许中断0、1中断
	IT0=1;
	IT1=1;	//中断0、1设置下降沿中断
	IP=0;		//中断优先级控制寄存器低(可位寻址)
	
	while(1)
	{
		P2=0xFE;//1111 1110
		Delay(500);
		P2=0xFD;//1111 1101
		Delay(500);
		P2=0xFB;//1111 1011
		Delay(500);
		P2=0xF7;//1111 0111
		Delay(500);
		P2=0xEF;//1110 1111
		Delay(500);
		P2=0xDF;//1101 1111
		Delay(500);
		P2=0xBF;//1011 1111
		Delay(500);
		P2=0x7F;//0111 1111
		Delay(500);		
	}
}

	
void Int0_Routine() interrupt 0//外部中断0
{
	unsigned char i;
	for(i=0;i<10;i++)
	{
		P2=0xf0;
		Delay(300);
		P2=0x0f;
		Delay(300);
	}
}

void Int1_Routine() interrupt 2//外部中断1
{	
			unsigned char i;
			for(i=0;i<10;i++)
			{
				P2=0xff;
				Delay(300);
				P2=0x00;
				Delay(300);
			}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

Proteus仿真

普中开发板实现

3、中断嵌套

要求:设计一中断嵌套程序:要求K1和K2都未按下时,P2口8只LED呈流水灯显示,当按一下K1时,产性一个低优先级外中断0请求(跳沿触发),进入外中断0中断服务程序,上下4只LED交替闪烁。此时按一下K2时,产生一个高优先级的外中断1请求(跳沿触发),进入外中断1中断服务程序,使8只LED全部闪烁。当显示5次后,再从外中断1返回继续执行外中断0中断服务程序,即P2口控制8只LED,上、 下4只LED交替闪烁。设置外中断0为低优先级,外中断1为高优先级。

Keil代码实现

嵌套中断与两个外部中断不同之外在于中断优先级有高低之分,因此,只需在两个外部中断的代码的基础上,将优先级做调整即可实现:

//两个外部中断的优先级设置为同优先级
	IP=0;		//中断优先级控制寄存器低(可位寻址)
//中断嵌套按要求设置为外部中断1优先级高于外部中断0
	PX0=0;	
	PX1=1;	//中断1优先于中断0
  • 1
  • 2
  • 3
  • 4
  • 5

Proteus仿真

三、定时器/计数器工作原理

(一)定时器/计数器结构

​ AT89S51定时器/计数器结构如下图,定时器/计数器T0、T1由特殊功能寄存器TH0、TL0和TH1、TL1构成。

计数器模式是对加在T0和T1两个引脚上的外部脉冲进行计数。

定时器模式是对系统时钟信号经12分频后的内部脉冲信号(机器周期)计数。由于系统时钟频率是定值,可根据计数值计算出定时时间。两个定时器/计数器属于增1计数器,即每计一个脉冲,计数器增1。

image-20240404201257382

(二)定时器/计数器寄存器

1、定时器/计数器控制寄存器TCON

TCON为定时器/计数器T0、T1的控制寄存器,同时也锁存T0、T1溢出中断源和外部请求中断源等,TCON的格式如下:

image-20240404192059051

2、定时器/计数器工作模式寄存器TMOD

​ 定时和计数功能由特殊功能寄存器TMOD的控制位C/T*进行选择,TMOD寄存器的各位信息表如下 所列。可以看出,2个定时/计数器有4种操作模式,通过TMODM1M0选择。2个定时/计数器的模式0、1和2都相同,模式3不同,各模式下功能如下所述。

image-20240404202242293

通常选用TMOD工作模式为方式1,即M1=0,M1=1;

(三)计算定时器的计数初值

*定时时间=(2^16-X)12/晶振频率

(四)定时/计数器应用

1、定时器T0中断

要求:在Prodeus和普中单片机板上分别完成采用定时计数器控制LED灯每隔1S周期性亮灭的实验。要求KEIL仿真中的虚拟逻辑仪对LED管脚进行波形观察,测量真实的周期数,并与软件循环进行周期定时的精度进行对比,看哪一种方式更加精准。

定时器初值:设定时时间1ms (即5000μs),设T0计数初值为X,假设晶振的频率为11.0592MHz,则定时时间为:

1000=(2^16-X)*12/11.0592==>X=64614,转换为十六进制为0xFC66,其中0XFC装入TH00X66装入TL0

Keil代码实现

#include <REGX52.H>

void Timer0_Init()
{
	//设置定时器工作模式TMOD为方式1
	//TMOD为不可位寻址,只能整体赋值,可采用&=、|=使其只改变某些位
	TMOD&=0xf0;//高四位不变,低四位清零
	TMOD|=0x01;//最低位置1,其余位不变 //TOMD=0x01
	//定时时间=(2^16-X)*12/晶振频率
	TL0=0x66;
	TH0=0xfc;//定时1ms
	//设置定时器控制寄存器TCON
	TF0=0; //中断溢出标志位先清0,避免刚配置好就中断
	TR0=1; //定时器开始工作
	//中断
	EA=1;//允许总中断
	ET0=1;//T0的中断溢出允许位,T0=1,允许中断
}

void main()
{
	Timer0_Init();
	while(1)
	{}
}

void Timer0_Runtine() interrupt 1
{
	unsigned int Count;//计数
	TL0=0x66;
	TH0=0xfc;
	Count++;
	if(Count>=1000)//计数累加到1000,定时器定时1000ms=1s
	{
			P2_0=~P2_0;//LED状态取反
		Count=0;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

Proteus仿真

普中开发板实现

Keil软件中采用虚拟逻辑仪测试周期准确性
  • 采用软件定时查看波形如下图:
  • 采用定时器定时查看波形如下图:

​ 如图软件定时一个周期内约为0.927215秒,而采用定时器定时一周期内约为0.930211秒。

​ 因此定时器定时较软件定时更为精确。

2、计数器T0中断

要求:采用计数器中断,实现按4次按键开关后,P2口的8只LED闪烁不停。

计数器初值:由于每按1次按键开关,计数1次,按4次后,P2口的8只LED闪烁不停。因此计数器初值为2^16-4=65532,转化为十六进制后为0XFFFC,所以TH0=0XFFTL0=0XFC

Keil代码实现

#include <REGX52.H>
void Delay(unsigned int xms)
{
	unsigned char i, j;
	while(xms--)
	{
			i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}
}

void Count0_Init()	//计数器
{
	//设置计数器0工作模式1
	TMOD&=0xf0;
	TMOD|=0x05;// C/T置1为计数器模式
	TL0=0xfc;
	TH0=0xff;//计数4次,65536-4=65532
	//设置计数器控制寄存器TCON
	TF0=0;
	TR0=1;
	//中断
	EA=1;
	ET0=1;
}

void main()
{
	Count0_Init();
	while(1)
	{
		
	}
}

void Count0_Routine() interrupt 1
{
	while(1)
	{
		P2=~P2;
		Delay(500);
		P2=~P2;
		Delay(500);
	}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

Proteus仿真

普中开发板实现

​ 由于我所使用的普中开发板为STC89C52R芯片,该芯片定时/计数器T0引脚为P3_4,而独立按键引脚为P3_0~P3_3,因此无法采用独立按键在该开发板上验证。

四、中断服务程序优化

要求: 在上述的按键控制LED的实验中,中断函数采用了软件延时函数控制LED间隔周期,为了避免中断服务程序执行较长耗时的代码,将外部中断程序进行优化:在中断服务程序中只改变中断模式标志位,在主函数中根据不同的中断模式标志,选择不同的亮灯模式。

Keil代码实现

#include <REGX52.H>

void Delay(unsigned int xms)		//@11.0592MHz延时函数
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}
}

void Init0()//外部中断0初始化
{
	EA=1;
	EX0=1;
	IT0=1;//下降沿触发
}

unsigned char Key_Interrupt_Flag=0;//中断模式标志位

void Init0_Routine() interrupt 0
{
	Key_Interrupt_Flag=1;//模式标志位置1
}
	
void main()
{
	unsigned char Mode=0;
	Init0();//外部中断初始化
	while(1)
	{
		if(Key_Interrupt_Flag)//如果中断标志位为1 
		{
			Key_Interrupt_Flag=0;//标志位置0
			Mode++;//切换亮灯模式 
			if(Mode>=3)
			{
				Mode=0;//循环切换
			}
		}
		
		switch (Mode)//亮灯模式选择
		{
			case 0:
				{P2=0xfe;}
				break;
			case 1:
				{P2=0xf0;Delay(300);P2=0X0F;Delay(300);}
				break;
			case 2:
				{P2=0XAA;Delay(300);P2=0x55;Delay(300);}
				break;
			default:
				break;
		}	
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

Proteus仿真

普中开发板实现

五、总结

​ 通过此次学习51单片机中断系统和定时/计数器的相关内容,掌握了中断系统的原理和使用方法,了解了如何利用中断来实现对外部事件的实时响应,提高了系统的灵活性和可靠性。学习了定时/计数器的工作原理和编程方法,掌握了如何利用定时器来实现时间精确控制,提高了系统的时间管理能力。了解了中断和定时/计数器的工作机制,学会了合理设置中断优先级和定时器的工作参数,从而能够有效地节省系统资源,提高系统的性能和效率。通过实际的应用案例,如按键中断、定时器控制LED闪烁等,将理论知识与实际操作相结合,加深了对中断系统和定时/计数器的理解和掌握。

​ 总的来说,通过学习51单片机的中断系统和定时/计数器,我不仅提升了对单片机系统的整体认识和理解,还增强了自己的实践能力和问题解决能力,为今后的学习和工作打下了坚实的基础。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/637205
推荐阅读
相关标签
  

闽ICP备14008679号