51单片机学习2


中断概念

cGZAG6.png

关键词:

中断源:(中断嵌套,中断优先级)

​ 51有5个,51有6个(多一个T2定时器/计数器2);

​ 默认优先级(由高到低):INT0——外部中断0,P3.2口,低电平或下降沿引起(序号:0;入口地址:0003H);

INT1——外部中断1,P3.3口,低电平或下降沿引起(序号:1;入口地址:000BH);

T0——定时器/计数器0中断,由T0计数器计满回零引起(序号:2;入口地址:0013H);

T1——定时器/计数器1中断,由T1计数器计满回零引起(序号:3;入口地址:001BH);

T2——定时器/计数器2中断,由T2计数器计满回零引起(序号:4;入口地址:0023H);

TI/IR——串行口中断,串行端口完成一帧字符发送/接收后引起(序号:5;入口地址:002BH);

中断允许寄存器IE:

​ 设定各个中断源的打开和关闭,是特殊功能寄存器,字节地址:A8H,位地址:A8H~AFH,可进行位寻址(即可对寄存器的每一位进行单独操作,复位时,IE全被清零。

cGnTS0.md.png

EA:全局中断允许位;

**–**:无效位;

ET2:定时器/计数器2中断允许位(为1时打开,为0时关闭);

ES:串行口中断允许位;

ET1:定时器/计数器1中断允许位;

EX1:外部中断1中断允许位;

ET0:定时器/计数器0中断允许位;

EX0:外部中断0中断允许位。

中断优先级寄存器IP:

特殊功能寄存器,B8H~BFH。可位寻址,复位时,全清零

cGug91.md.png

**–**:无效位;

PS:串行口中断优先级控制位(为1定义为高优先级中断);

PT1:定时器/计数器1中断优先级控制位;

PX1:外部中断1中断优先级控制位;

PT0:定时器/计数器0中断优先级控制位;

PX0:外部中断0中断优先级控制位;

按优先级可形成中断嵌套。

单片机的定时器中断

定时器/计数器实质是加1计数器(16位),由高8位和低8位两个寄存器组成:TMOD(定时器/计数器工作方式寄存器)

TCON(控制寄存器,控制T0、T1的启动停止及设置溢出标志)

cGMmsf.md.png

加1计数器输入计数脉冲两个来源;由系统的时钟振荡器输出脉冲经12分频后送来;T0或T1引脚输入的外部脉冲源,每来一个脉冲计数器加1,加到全1时,在输入一个脉冲,计数器回零,且计数器的溢出使TCON寄存器中TF0或TF1置1,向CPU发送中断请求(定时器/计数器中断允许时)

定时器/计数器工作方式寄存器TMOD

​ 字节地址:89H,不能位寻址,用来确定定时器的工作方式及功能选择,复位时全清零

cUZZHU.md.png

TMOD高四位设置定时器1,低四位用于设置定时器0;

GATE:门控制位

GATE=0,定时器/计数器启动与停止仅受TCON寄存器中的TRX(X=0,1)来控制;

GATE=1,定时器/计数器启动与停止由TCON寄存器中TRX和外部中断引脚(INT0或INT1)上的电平状态来共同控制。

C/T:定时器模式(=0)和计数器模式(=1)选择位;

M1M0:工作方式选择位

(每个定时器/计数器都有4种工作方式)

cUeNLV.md.png

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

​ 字节地址:88H~8FH,可位寻址,TCON寄存器用来控制定时器的启、停,标志定时器溢出和中断情况。复位时全清零。

cUmK61.md.png

TF1、TR1、TF0和TR0位用于定时器/计数器;IE1、IT1、IE0和IT0位用于外部中断。

TF1:定时器1溢出标志(当定时器1溢出时,由硬件使TF1置1,并且申请中断。进入中断服务程序后,由硬件自动清0。)(如果使用定时器的中断,则该位完全不用人为去操作,但是如果用软件查询方式,当查询到该位置1后,就需要用软件清0。)

TR1:定时器1运行控制位。(由软件清0关闭定时器1.当GATE=1,且INT1为高电平时,TR1置1启动定时器1;当GATE=0 时,TR1置1启动定时器1。

TF0:定时器0溢出标志,功能及操作方法同TF1。

TR0:定时器0运行控制位,同TR1。

IE1:外部中断1请求标志。

。。。。。。。。。

**计算定时器的初值问题 **

默认TH0TL0为0,当用定时器的方式1时,机器周期为Tcy(一个机器周期为12个时钟周期),定时器产生一次中断的时间为t,则需计数的个数N=t/Tcy,装入THX和TLX中的数分别为;

​ THX = (65536-N)/256 TLX = (65536-N)%256

应用:

补充:中断服务程序的写法(C51)

形式:
void 函数名 () interrupt 中断号 using 工作组

{

		中断服务程序内容

}
说明:中断号:中断源的序号
	 using 工作组:指这个中断函数使用单片机内存中4组工作寄存器中的哪一组,一般不写,会自动分配。

写单片机的定时器程序时,再开始处需对定时器及中断寄存器做初始化配置:

1、对TMOD赋值,确定T0和T1的工作方式;

2、计算初值,并将初值写入TH0、TL0或TH1、TL1;

3、中断方式时,则对IE赋值,开放中断;

4、使TR0或TR1置位,启动定时器/计数器定时或计数。

/************************************************************
	9、利用定时器0工作方式1,LED 1S闪烁
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int 
sbit led1 = P1^0;
uchar num;
void main()
{
	//定时器初始化
	TMOD = 0x01;			//设置定时器0为工作方式1(M1M0为01)
	TH0 = (65536-45872)/256;	//装初值11.0592M晶振定时50ms数为45872
	TL0 = (65536-45872)%256;
	EA = 1;				//开总中断
	ET0 = 1;				//开定时器0中断
	TR0 = 1;				//启动定时器0
	while(1);				//程序停止在这里等待中断发生
}
void T0_time()interrupt 1		//中断服务程序
{
	TH0 = (65536-45872)/256;	//重装初值
	TL0 = (65536-45872)%256;	
	num++;				//num每加1次判断一次是否到20次
	if(num==20)				//如果到了20次,说明1秒时间到
	{
		num = 0;			//然后把num清0重新再计20次
		led1 = ~led1;
	}
}

能在主程序中完成的功能就不在中断函数中写,中断函数中要高效简洁:

优化后:

/************************************************************
	9、利用定时器0工作方式1,LED 1S闪烁
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int 
sbit led1 = P1^0;
uchar num;
void main()
{
	//定时器初始化
	TMOD = 0x01;			//设置定时器0为工作方式1(M1M0为01)
	TH0 = (65536-45872)/256;	//装初值11.0592M晶振定时50ms数为45872
	TL0 = (65536-45872)%256;
	EA = 1;				//开总中断
	ET0 = 1;				//开定时器0中断
	TR0 = 1;				//启动定时器0
	while(1)
	{
		if(num==20)				//如果到了20次,说明1秒时间到
	{
		num = 0;			//然后把num清0重新再计20次
		led1 = ~led1;
	}
	}				//程序停止在这里等待中断发生
}
void T0_time()interrupt 1		//中断服务程序
{
	TH0 = (65536-45872)/256;	//重装初值
	TL0 = (65536-45872)%256;	
	num++;				//num每加1次判断一次是否到20次
	
}
/*********************************************************
	10、中断(TX-1C)定时器0的方式1实现第一个发光管以200ms间隔闪烁
		用定时器1的方式1实现数码管前两位59s循环计时
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int 
sbit dula = P2^6;
sbit wela = P2^7;
sbit led1 = P1^0;
uchar code table[]={
		0x3f , 0x06 , 0x5b , 0x4f ,
		0x66 , 0x6d ,0x7d , 0x07 , 
		0x7f , 0x6f , 0x77 , 0x7c ,
		0x39 , 0x5e , 0x79 , 0x71 };
void delayms(uint);
void display(uchar,uchar);
uchar num,num1,num2,shi,ge;
void main()
{
	TMOD = 0x11;		//设置定时器0和1为工作方式1(0001 0001)
	TH0 = (65535-45872)/256;	//装初值
	TL0 = (65536-45872)%256;	
	TH1 = (65536-45872)/256;	//装初值
	TL1 = (65536-45872)%256;
	EA = 1;				//开总中断
	ET0 = 1;				//开定时器0中断
	ET1 = 1;				//开定时器1中断
	TR0 =1;				//启动定时器0
	TR1 = 1;				//启动定时器1
	while(1)
	{
		display(shi,ge);
	}
}
void display(uchar shi,uchar ge)	//显示子函数
{
	dula = 1;
	P0 = table[shi];			//送段选数据
	dula = 0;
	P0 = 0xff;				//送位选数据前关闭所有显示,防止打开位选锁存时原来的段选数据通过位选锁存器造成混乱
	wela = 1;
	P0 = 0xfe;				//送位选数据
	wela = 0;
	delayms(5);
	
	dula = 1;
	P0 = table[ge];
	dula = 0;
	P0 =0xff;
	wela = 1;
	P0 = 0xfd;
	wela = 0;
	delayms(5);
}

void delayms(uint xms)
{
	uint i,j;
	for(i=xms;i>0;i--)
		for(j=110;j>0;j--);
}
void T0_time() interrupt 1
{
	TH0=(65536-45872)/256;		//重装初值
	TL0=(65536-45872)%256;	
	num1++;
	if(num1==4)				//4次中断=200ms
	{
		num1 = 0;			//清0
		led1 = ~led1;
	}
}

void T1_time() interrupt 3
{
	TH1=(65536-45872)/256;		//重装初值
	TL1=(65536-45872)%256;
	num2++;
	if(num2==20)			//20次中断=1秒
	{
		num2=0;			//清0
		num++;
		if(num==60)			//该数用来送数码管显示,到60后归0
			num=0;
		shi=num/10;			//把一个2位数分离后分别送数码管显示
		ge=num%10;			//十位和个位
	}
}
/*********************************************************
	10、中断(BST-M51)定时器0的方式1实现第一个发光管以200ms间隔闪烁
		用定时器1的方式1实现数码管前两位59s循环计时
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int 
sbit wei1 = P2^4;//定义第一位数码管
sbit wei2 = P2^5;//定义第二位数码管
sbit wei3 = P2^6;//定义第三位数码管
sbit wei4 = P2^7;//定义第四位数码管
sbit led1 = P1^0;
uchar code table[]={
		0x3f , 0x06 , 0x5b , 0x4f ,
		0x66 , 0x6d ,0x7d , 0x07 , 
		0x7f , 0x6f , 0x77 , 0x7c ,
		0x39 , 0x5e , 0x79 , 0x71 };
void delayms(uint);
void display(uchar,uchar);
uchar num,num1,num2,shi,ge;
void main()
{
	TMOD = 0x11;		//设置定时器0和1为工作方式1(0001 0001)
	TH0 = (65535-45872)/256;	//装初值
	TL0 = (65536-45872)%256;	
	TH1 = (65536-45872)/256;	//装初值
	TL1 = (65536-45872)%256;
	EA = 1;				//开总中断
	ET0 = 1;				//开定时器0中断
	ET1 = 1;				//开定时器1中断
	TR0 =1;				//启动定时器0
	TR1 = 1;				//启动定时器1
	while(1)
	{
		display(shi,ge);
	}
}
void display(uchar shi,uchar ge)	//显示子函数
{
	
	P0 = table[shi];			//送段选数据
	wei1 = 1;
	wei2 = 0;
	wei3 = 0;
	wei4 = 0;
	delayms(5);
	
	P0 = table[ge];
	wei1 = 0;
	wei2 = 1;
	wei3 = 0;
	wei4 = 0;                                                                                                                                                                                                                  
	delayms(5);
}

void delayms(uint xms)
{
	uint i,j;
	for(i=xms;i>0;i--)
		for(j=110;j>0;j--);
}
void T0_time() interrupt 1
{
	TH0=(65536-45872)/256;		//重装初值
	TL0=(65536-45872)%256;	
	num1++;
	if(num1==4)				//4次中断=200ms
	{
		num1 = 0;			//清0
		led1 = ~led1;
	}
}

void T1_time() interrupt 3
{
	TH1=(65536-45872)/256;		//重装初值
	TL1=(65536-45872)%256;
	num2++;
	if(num2==20)			//20次中断=1秒
	{
		num2=0;			//清0
		num++;
		if(num==60)			//该数用来送数码管显示,到60后归0
			num=0;
		shi=num/10;			//把一个2位数分离后分别送数码管显示
		ge=num%10;			//十位和个位
	}
}

注意:不能将判断发光二极管亮灭时间是否达到的语句写在主程序中。


文章作者: 旧时南风
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 旧时南风 !
评论
  目录