单片机中断系统
中断的概念:
CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断 。
随着计算机技术的应用,人们发现中断技术不仅解决了快速主机与慢速I/O设备的数据传送问题,而且还具有如下优点:
分时操作。 CPU可以分时为多个I/O设备服务,提高了计算机的利用率;
实时响应。 CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;
可靠性高。 CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高。
1、(P3.2)可由IT0(TCON.0)选择其为低电平有效还是下降沿有效。当CPU检测到P3.2引脚上出现有效的中断信号时,中断标志IE0(TCON.1)置1,向CPU申请中断。
2、(P3.3)可由IT1(TCON.2)选择其为低电平有效还是下降沿有效。当CPU检测到P3.3引脚上出现有效的中断信号时,中断标志IE1(TCON.3)置1,向CPU申请中断。
3、TF0(TCON.5),片内定时/计数器T0溢出中断请求标志。当定时/计数器T0发生溢出时,置位TF0,并向CPU申请中断。
4、TF1(TCON.7),片内定时/计数器T1溢出中断请求标志。当定时/计数器T1发生溢出时,置位TF1,并向CPU申请中断。
5、RI(SCON.0)或TI(SCON.1),串行口中断请求标志。当串行口接收完一帧串行数据时置位RI或当串行口发送完一帧串行数据时置位TI,向CPU申请中断。
中断允许控制
EX0(IE.0),外部中断0允许位;
ET0(IE.1),定时/计数器T0中断允许位;
EX1(IE.2),外部中断0允许位;
ET1(IE.3),定时/计数器T1中断允许位;
ES(IE.4),串行口中断允许位;
EA (IE.7), CPU中断允许(总允许)位。
中断请求标志
IT0(TCON.0),外部中断0触发方式控制位。
当IT0=0时,为电平触发方式。
当IT0=1时,为边沿触发方式(下降沿有效)。
IE0(TCON.1),外部中断0中断请求标志位。
IT1(TCON.2),外部中断1触发方式控制位。
IE1(TCON.3),外部中断1中断请求标志位。
TF0(TCON.5),定时/计数器T0溢出中断请求标志位。
TF1(TCON.7),定时/计数器T1溢出中断请求标志位。
中断优先级
中断源
定时/计数器
51单片机定时/计数器的工作由两个特殊功能寄存器控制。TMOD用于设置其工作方式;TCON用于控制其启动和中断申请。
工作方式寄存器TMOD
工作方式寄存器TMOD用于设置定时/计数器的工作方式,低四位用于T0,高四位用于T1。其格式如下:
GATE是门控位, GATE=0时,用于控制定时器的启动是否受外部中断源信号的影响。只要用软件使TCON中的TR0或TR1为1,就可以启动定时/计数器工作;GATA=1时,要用软件使TR0或TR1为1,同时外部中断引脚INT0/1也为高电平时,才能启动定时/计数器工作。即此时定时器的启动条件,加上了INT0/1引脚为高电平这一条件。
C/T :定时/计数模式选择位。C/T =0为定时模式;C/T =1为计数模式。
M1M0:工作方式设置位。定时/计数器有四种工作方式。
控制其启动和中断申请TCON
TCON的低4位用于控制外部中断,已在前面介绍。TCON的高4位用于控
制定时/计数器的启动和中断申请。其格式如下:
TF1(TCON.7):T1溢出中断请求标志位。T1计数溢出时由硬件自动置TF1为1。CPU响应中断后TF1由硬件自动清0。T1工作时,CPU可随时查询TF1的状态。所以,TF1可用作查询测试的标志。TF1也可以用软件置1或清0,同硬件置1或清0的效果一样。
TR1(TCON.6):T1运行控制位。TR1置1时,T1开始工作;TR1置0时,T1停止工作。TR1由软件置1或清0。所以,用软件可控制定时/计数器的启动与停止。
TF0(TCON.5):T0溢出中断请求标志位,其功能与TF1类同。
TR0(TCON.4):T0运行控制位,其功能与TR1类同。
TLx与THx之间的搭配关T0(TL0-0x8A,TH0-0x8C), T1(TL1-0x8B,TH1-0x8D)
1)、TLx与THx之间32进制。即当TLx计到32个脉冲时,TLx归0同时THx进1。这也称为方式0。
2)、TLx与THx之间256进制。即当TLx计到256个脉冲时,TLx归0同时THx进1。这也称为方式1。在方式1时,最多计65536个脉冲产生溢出。在主频为11.0592M时,每计一个脉冲为1.085us,所以溢出一次的时间为1.085usx65536=71.1ms。
3)、THx用于存放TLx溢出后,TLx下次计数的起点。这也称为方式2。
4)、THx与TLx分别独立对自己的输入脉冲计数。这也称为方式3。
5、定时器初始化
1)、确定定时器的计数模式。
2)、确定TLx与THx之间的搭配关系。
3)、确定计数起点值。即TLx与THx的初值。
STC单片机STC89C52RC定时器延时时间的计算
延时时间要根据晶振频率计算,不同板子可能有所不同。
时钟周期:
1/时钟源,在我现在这块板子上,晶振频率是11.0592M,也就是时钟周期是 1/11059200秒
机器周期:
一般51单片机是12个时钟周期,我的板子也就是 12/11059200秒
单次定时最长时间:
如果是16位的计数器,16位最大值是65535,共可计数65536次。基本的常数一定要记住,还要记住8位最大值是255,共可计数256次,还要记住8位上每位代表的数值。
12 * 65536/11059200 = 0.0711 s,也就是,71 ms内的定时可以单次定时就完成。如果定时时间超过71 ms,就要循环了。
一次定时需要几次机器周期:
计算公式:定时秒数/机器周期
比如我要定时1秒, 1/(12/11059200)= 921600次,16位计数器最大可计数65536次,921600次早就益出了。我们可以每次定时10 ms,循环100次就可以定时1秒了,1 s缩小100百倍就是10 ms, 也就是每次需要计数9216次。
确实计数器初始值:
定时10 ms时,如果计数器从0开始计数,我们就不知道什么时候到了9216次。所以应该计数了9216次,16位计数器最多计数95536次,然后就溢出,一溢出TCON的TF位就会置1,我们只要经常检测TF位就可以知道什么时候完成10ms的定时了。
计算公式:计数器初始值=最大计数次数 - 需要计数次数
如果定时10 ms,计数器的初始值就是 65536 - 9216
计算计数器的高位和低位:
16位的计数器,也就是两个8位组成,8位的最大计数次数是256。所以:
计数器高位 = 初始值/256
计数器低位 = 初始值%256
例如:
设置 外部中断0
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit k3=P3^2; //定义按键K3
sbit led=P2^0; //定义P20口是led
void delay(u16 i)
{
while(i--);
}
void Int0Init()
{
//设置INT0
IT0=1;//跳变沿出发方式(下降沿)
EX0=1;//打开INT0的中断允许。
EA=1;//打开总中断
}
void main()
{
Int0Init(); // 设置外部中断0
while(1);
}
void Int0() interrupt 0 //外部中断0的中断函数
{
delay(1000); //延时消抖
if(k3==0)
{
led=~led;
}
}
例如:
设置 定时器0
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit led=P2^0; //定义P20口是led
void Timer0Init()
{
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
void main()
{
Timer0Init(); //定时器0初始化
while(1);
}
void Timer0() interrupt 1
{
static u16 i;
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
i++;
if(i==1000)
{
i=0;
led=~led;
}
}
例如
交通灯
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
//--定义使用的IO口--//
#define GPIO_DIG P0
#define GPIO_TRAFFIC P1
sbit RED10 = P1^0; //上人行道红灯
sbit GREEN10 = P1^1; //上人行道绿灯
sbit RED11 = P1^2;
sbit YELLOW11= P1^3;
sbit GREEN11 = P1^4;
sbit RED00 = P3^0; //右人行道红灯
sbit GREEN00 = P3^1; //右人行道绿灯
sbit RED01 = P1^5;
sbit YELLOW01= P1^6;
sbit GREEN01 = P1^7;
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
u8 DisplayData[8];
u8 Second;
void delay(u16 i)
{
while(i--);
}
void DigDisplay()
{
u8 i;
for(i=0;i<8;i++)
{
switch(i) //位选,选择点亮的数码管,
{
case(0):
LSA=0;LSB=0;LSC=0; break;//显示第0位
case(1):
LSA=1;LSB=0;LSC=0; break;//显示第1位
case(2):
LSA=0;LSB=1;LSC=0; break;//显示第2位
case(3):
LSA=1;LSB=1;LSC=0; break;//显示第3位
case(4):
LSA=0;LSB=0;LSC=1; break;//显示第4位
case(5):
LSA=1;LSB=0;LSC=1; break;//显示第5位
case(6):
LSA=0;LSB=1;LSC=1; break;//显示第6位
case(7):
LSA=1;LSB=1;LSC=1; break;//显示第7位
}
GPIO_DIG=DisplayData[i];//发送段码
delay(100); //间隔一段时间扫描
GPIO_DIG=0x00;//消隐
}
}
void Timer0Init()
{
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
void main()
{
Second = 1;
Timer0Init();
while(1)
{
if(Second == 70)
{
Second = 1;
}
//--宝田路通行,30秒--//
if(Second < 31)
{
DisplayData[0] = 0x00;
DisplayData[1] = 0x00;
DisplayData[2] = smgduan[(30 - Second) % 100 / 10];
DisplayData[3] = smgduan[(30 - Second) %10];
DisplayData[4] = 0x00;
DisplayData[5] = 0x00;
DisplayData[6] = DisplayData[2];
DisplayData[7] = DisplayData[3];
DigDisplay();
//--宝田路通行--//
GPIO_TRAFFIC = 0xFF; //将所有的灯熄灭
RED00 = 1;
GREEN00 = 1;
GREEN11 = 0; //宝田路绿灯亮
GREEN10 = 0; //宝田路人行道绿灯亮
RED01 = 0; //前进路红灯亮
RED00 = 0; //前进路人行道红灯亮
}
//--黄灯等待切换状态,5秒--//
else if(Second < 36)
{
DisplayData[0] = 0x00;
DisplayData[1] = 0x00;
DisplayData[2] = smgduan[(35 - Second) % 100 / 10];
DisplayData[3] = smgduan[(35 - Second) %10];
DisplayData[4] = 0x00;
DisplayData[5] = 0x00;
DisplayData[6] = DisplayData[2];
DisplayData[7] = DisplayData[3];
DigDisplay();
//--黄灯阶段--//
GPIO_TRAFFIC = 0xFF; //将所有的灯熄灭
RED00 = 1;
GREEN00 = 1;
YELLOW11 = 0; //宝田路黄灯亮
RED10 = 0; //宝田路人行道红灯亮
YELLOW01 = 0; //前进路红灯亮
RED00 = 0; //前进路人行道红灯亮
}
//--前进路通行--//
else if(Second < 66)
{
DisplayData[0] = 0x00;
DisplayData[1] = 0x00;
DisplayData[2] = smgduan[(65 - Second) % 100 / 10];
DisplayData[3] = smgduan[(65 - Second) %10];
DisplayData[4] = 0x00;
DisplayData[5] = 0x00;
DisplayData[6] = DisplayData[2];
DisplayData[7] = DisplayData[3];
DigDisplay();
//--黄灯阶段--//
GPIO_TRAFFIC = 0xFF; //将所有的灯熄灭
RED00 = 1;
GREEN00 = 1;
RED11 = 0; //宝田路红灯亮
RED10 = 0; //宝田路人行道红灯亮
GREEN01 = 0; //前进路绿灯亮
GREEN00 = 0; //前进路人行道绿灯亮
}
//--黄灯等待切换状态,5秒--//
else
{
DisplayData[0] = 0x00;
DisplayData[1] = 0x00;
DisplayData[2] = smgduan[(70 - Second) % 100 / 10];
DisplayData[3] = smgduan[(70 - Second) %10];
DisplayData[4] = 0x00;
DisplayData[5] = 0x00;
DisplayData[6] = DisplayData[2];
DisplayData[7] = DisplayData[3];
DigDisplay();
//--黄灯阶段--//
GPIO_TRAFFIC = 0xFF; //将所有的灯熄灭
RED00 = 1;
GREEN00 = 1;
YELLOW11 = 0; //宝田路黄灯亮
RED10 = 0; //宝田路人行道红灯亮
YELLOW01 = 0; //前进路红灯亮
RED00 = 0; //前进路人行道红灯亮
}
}
}
void Timer0() interrupt 1
{
static u16 i;
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
i++;
if(i==1000)
{
i=0;
Second ++;
}
}
例如
脉冲发生器
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
//--定义使用的IO--//
#define GPIO_DIG P0
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
u8 DisplayData[8];
//--定义全局变量--//
unsigned long Freq; //用来存放要显示的频率值
unsigned long TimeCount; //用于计算1S钟的
void delay(u16 i)
{
while(i--);
}
void DigDisplay()
{
u8 i;
for(i=0;i<8;i++)
{
switch(i) //位选,选择点亮的数码管,
{
case(0):
LSA=0;LSB=0;LSC=0; break;//显示第0位
case(1):
LSA=1;LSB=0;LSC=0; break;//显示第1位
case(2):
LSA=0;LSB=1;LSC=0; break;//显示第2位
case(3):
LSA=1;LSB=1;LSC=0; break;//显示第3位
case(4):
LSA=0;LSB=0;LSC=1; break;//显示第4位
case(5):
LSA=1;LSB=0;LSC=1; break;//显示第5位
case(6):
LSA=0;LSB=1;LSC=1; break;//显示第6位
case(7):
LSA=1;LSB=1;LSC=1; break;//显示第7位
}
GPIO_DIG=DisplayData[i];//发送段码
delay(10); //间隔一段时间扫描
GPIO_DIG=0x00;//消隐
}
}
void Timer_Config()
{
//--定时器T1做计数器,工作方式1(16位定时器),只由TRx打开计数器--//
//--定时器T0做定时器,工作方式1(16位定时器),只由TRx打开定时器--//
TMOD=0x51;
//--设置定时器晶振为12MHZ时定时50ms--//
TH0=0x3C;
TL0=0xB0;
//--打开中断-//
ET0=1;
ET1=1;
EA=1;
//--打开定时器*/
TR0=1;
TR1=1;
}
void main()
{
Timer_Config();
while(1)
{
if(TR1 == 0) //当计数器停下的时候,表明计数完毕
{
Freq = Freq + TL1; //读取TL的值
Freq = Freq + (TH1 * 256); //读取TH的值
//--求频率的个十百千万十万位--//
DisplayData[0] = smgduan[Freq%1000000/100000];
DisplayData[1] = smgduan[Freq%100000/10000];
DisplayData[2] = smgduan[Freq%10000/1000];
DisplayData[3] = smgduan[Freq%1000/100];
DisplayData[4] = smgduan[Freq%100/10];
DisplayData[5] = smgduan[Freq%10];
//--显示完,重新计算下一次频率。--//
Freq = 0;//将计算的频率清零
TH1 = 0; //将计数器的值清零
TL1 = 0;
TR0 = 1; //开启定时器
TR1 = 1; //开启计数器
}
//--显示求得的数值--//
DigDisplay();
}
}
void Timer0() interrupt 1
{
//--12MHZ设置定时50ms的初值--//
TH0=0x3C;
TL0=0xB0;
TimeCount++;
if(TimeCount==20)//计时到1S
{
TR0=0;
TR1=0;
TimeCount=0;
}
}
void Timer1() interrupt 3
{
//--进入一次中断,表明计数到了65536--//
Freq=Freq+65536;
}
例如
串口通讯
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
void UsartInit()
{
SCON=0X50; //设置为工作方式1
TMOD=0X20; //设置计数器工作方式2
PCON=0X80; //波特率加倍
TH1=0XF3; //计数器初始值设置,注意波特率是4800的
TL1=0XF3;
ES=1; //打开接收中断
EA=1; //打开总中断
TR1=1; //打开计数器
}
void main()
{
UsartInit(); // 串口初始化
while(1);
}
void Usart() interrupt 4
{
u8 receiveData;
receiveData=SBUF;//出去接收到的数据
RI = 0;//清除接收中断标志位
SBUF=receiveData;//将接收到的数据放入到发送寄存器
while(!TI); //等待发送数据完成
TI=0; //清除发送完成标志位
}
例如 485通讯
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit RS485DIR=P1^0; //RS485DIR=0为接收状态 RS485DIR=1为发送状态
void delay(u16 i)
{
while(i--);
}
void UsartInit()
{
SCON=0X50; //设置为工作方式1
TMOD=0X20; //设置计数器工作方式2
PCON=0X80; //波特率加倍
TH1=0XF3; //计数器初始值设置,注意波特率是4800的
TL1=0XF3;
ES=1; //打开接收中断
EA=1; //打开总中断
TR1=1; //打开计数器
RS485DIR=0;
}
void main()
{
UsartInit(); // 串口初始化
while(1);
}
void Usart() interrupt 4
{
u8 receiveData;
receiveData=SBUF;//出去接收到的数据
RI = 0;//清除接收中断标志位
delay(100);
RS485DIR=1;
SBUF=receiveData;//将接收到的数据放入到发送寄存器
while(!TI); //等待发送数据完成
TI=0; //清除发送完成标志位
RS485DIR=0;
}