单片机中断系统

中断的概念:

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用于设置定时/计数器的工作方式,低四位用于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的低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;

}