赞
踩
51的中断系统共有5个中断源:
外部中断0(INT0):由P3.2引脚输入的中断请求信号。
外部中断1(INT1):由P3.3引脚输入的中断请求信号。
定时器T0中断(T0):由定时器/计数器T0的定时、计数溢出产生的中断请求。
定时器T1中断(T0):由定时器/计数器T1的定时、计数溢出产生的中断请求。
串行口中断(ES):由串行数据发生完成(TI)或串行数据接收完成(RI)产生的中断请求。
中断源 | 中断入口地址 | 中断序号 |
---|---|---|
外部中断0 | 0003H | 0 |
定时器T0 | 000BH | 1 |
外部中断1 | 0013H | 2 |
定时器T1 | 001BH | 3 |
串行口中断ES | 0023H | 4 |
TCON寄存器为定时器/计数器的控制寄存器,字节地址为88H,可位寻址。既包括定时器/计数器T0、T1溢出中断请求标志位TF0和TF1,也包括两个外部中断请求的标志位IE1与IE0,还包括两个外部中断请求源的中断触发方式选择位
TF0——定时器T0的溢出中断请求标志位。溢出时由硬件置1,即开始发送中断请求;单片机响应中断后,重新由硬件清0。
TR0——定时器/计数器0的启动控制。功能详见定时器知识。
IE0——外部中断0的中断请求标志位。触发中断时由硬件置1,即开始发送中断请求;单片机响应中断后,重新由硬件清0。
IT0——外部中断0的触发方式选择位。令它为1,则对应引脚为下降沿触发中断;令它为0,则对应引脚为低电平触发方式。
TF1、TR1,IE1、IT1则为定时器1、外部中断1的相关标志位,功能与上述相同。
SCON寄存器 串行口控制寄存器,字节地址为98H,可位寻址。SCON的低二位锁存串口的发送中断和接收中断的中断请求标志TI和RI
TI——串行口发送完成中断请求标志位。串行口发送数据后,TI位被硬件置1;单片机响应串行口中断后,必须在服务程序中将TI手动清0。
RI——串行口接收完成中断请求标志位。串行口接收数据后,TI位被硬件置1;单片机响应串行口中断后,必须在服务程序中将TI手动清0
EA——总中断允许控制位。用来开启或屏蔽所有的中断请求,令它为1,则开放中断请求,可以响应中断;令它为0,则屏蔽所有中断请求。
ES——串行口中断允许控制位。为1时开放串行口中断请求,为0时屏蔽。
ET1——定时器/计数器T1中断允许控制位。为1时开放TI的中断请求,为0时屏蔽。
EX1——外部中断1中断允许控制位。为1时开放外部中断1的中断请求,为0时屏蔽。
ET0、EX0则为T0、外部中断0的中断允许控制位。功能与上述相同。
要求:在单片机P1口上接有8只LED。在外部中断0输入引脚(P3.2)接一只按钮开关K1。要求将外部中断0设置为电平触发。程序启动时,P1口上的8只LED全亮。每按一次按钮开关K1,使引脚接地,产生一个低电平触发的外中断请求,在中断服务程序中,让低4位的LED与高4位的LED交替闪烁5次。然后从中断返回,控制8只LED再次全亮。
代码如下:
- #include <reg51.h>
- #define uchar unsigned char
- void Delay(unsigned int i) //延时函数Delay( ),i形式参数,不能赋初值
- {
- unsigned int j;
- for(;i > 0;i--)
- for(j=0;j<333;j++) //晶振为12MHz,j选择与晶振频率有关
- {;} //空函数
- }
-
- void main( ) //主函数
- {
- EA=1; //总中断允许
- EX0=1; //允许外部中断0中断
- IT0=1; //选择外部中断0为跳沿触发方式
- while(1) //循环
- { P1=0;} // P1口的8只LED全亮
- }
-
- void int0( ) interrupt 0 using 0 //外中断0的中断服务函数
- {
- uchar m;
- EX0=0; //禁止外部中断0中断
- for(m=0;m<5;m++) //交替闪烁5次
- {
- P1=0x0f; //低4位LED灭,高4位LED亮
- Delay(400) ; //延时
- P1=0xf0; //高4位LED灭,低4位LED亮
- Delay(400); //延时
- EX0=1; //中断返回前,打开外部中断0中断
- }
仿真效果图如下:
一个中断
要求:在单片机P1口上接有8只LED。在外部中断0输入引脚(P3.2)接有一只按钮开关K1。在外部中断1输入引脚(P3.3)接有一只按钮开关K2。要求K1和K2都未按下时,P1口的8只LED呈流水灯显示,仅K1(P3.2)按下再松开时,上下各4只LED交替闪烁10次,然后再回到流水灯显示。如果按下再松开K2(P3.3)时,P1口的8只LED全部闪烁10次,然后再回到流水灯显示。设置两个外中断的优先级相同。
具体代码如下:
- #include <reg51.h>
- #define uchar unsigned char
- void Delay(unsigned int i) //延时函数Delay( ),i为形式参数,不能赋初值
- {
- uchar j;
- for(;i>0;i--)
- for(j=0;j<125;j++)
- {;} //空函数
- }
- void main( ) //主函数
- {
- uchar display[9]={0xff,0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf, 0x7f};
- //流水灯显示数据数组
- unsigned int a;
- for(;;)
- {
- EA=1; //总中断允许
- EX0=1; //允许外部中断0中断
- EX1=1; //允许外部中断1中断
- IT0=1; //选择外部中断0为跳沿触发方式
- IT1=1; //选择外部中断1为跳沿触发方式
-
- IP=0; //两个外部中断均为低优先级
- for(a=0;a<9;a++)
- {
- Delay(500); //延时
- P1=display[a]; //将已经定义的流水灯显示数据送到P1口
- }
- }
- }
- void int0_isr(void) interrupt 0 using 1//外中断0的中断服务函数
- {
- uchar n;
- for(n=0;n<10;n++) //高、低4位显示10次
- {
- P1=0x0f; //低4位LED灭,高4位LED亮
- Delay(500); //延时
- P1=0xf0; //高4位LED灭,低4位LED亮
- Delay(500); //延时
- }
- }
- void int1_isr (void) interrupt 2 using 2//外中断1中断服务函数
- {
- uchar m;
- for(m=0;m<10;m++) //闪烁显示10次
- {
- P1=0xff; //全灭
- Delay(500); //延时
- P1=0; //全亮
- Delay(500); //延时
- }
- }
-
仿真效果图如下:
两个中断
要求:设计一中断嵌套程序:要求K1和K2都未按下时,P1口8只LED呈流水灯显示,当按一下K1时,产生一个低优先级外中断0请求(跳沿触发),进入外中断0中断服务程序,上下4只LED交替闪烁。此时按一下K2时,产生一个高优先级的外中断1请求(跳沿触发),进入外中断1中断服务程序,使8只LED全部闪烁。当显示5次后,再从外中断1返回继续执行外中断0中断服务程序,即P1口控制8只LED,上、下4只LED交替闪烁。设置外中断0为低优先级,外中断1为高优先级。
- #include <reg51.h>
- #define uchar unsigned char
- void Delay(unsigned int i) //延时函数Delay( )
- {
- unsigned int j;
- for(;i > 0;i--)
- for(j=0;j<125;j++)
- {;} //空函数
- }
- void main( ) //主函数
- {
- uchar display[9]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
- //流水灯显示数据组
- uchar a;
- for(;;)
- {
- EA=1; //总中断允许
- EX0=1; //允许外部中断0中断
- EX1=1; //允许外部中断1中断
- IT0=1; //选择外部中断0为跳沿触发方式
- IT1=1; //选择外部中断1为跳沿触发方式
- PX0=0; //外部中断0为低优先级
- PX1=1; //外部中断1为高优先级
- for(a=0;a<9;a++)
- {
- Delay(500); //延时
- P1=display[a]; //流水灯显示数据送到P1口驱动LED显示
- }
-
- }
- }
-
- void int0_isr(void) interrupt 0 using 0 //外中断0中断函数
- {
- for(;;)
- {
- P1=0x0f; //低4位LED灭,高4位LED亮
- Delay(400); //延时
- P1=0xf0; //高4位LED灭,低4位LED亮
- Delay(400); //延时
- }
- }
- void int1_isr (void) interrupt 2 using 1 //外中断1中断函数
- {
- uchar m;
- for(m=0;m<5;m++) //8位LED全亮全灭5次
- {
- P1=0; //8位LED全亮
- Delay(500); //延时
- P1=0xff; //8位LED全灭
- Delay(500); //延时
- }
- }
仿真效果图如下:
嵌套中断
定时器实质上就是一个加1计数器。它随着计数器的输入脉冲进行自加1,也就是每来一个脉冲,计数器就自动加1,,当加到定数器满时,再输入一个脉冲就使定时器回零,且定时器的溢出使相应的中断标志位置1,向CPU发出中断请求。如果定时器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满。
定时器由高8位和低8位两个寄存器 TH0/TH1和TL0/TH1 组成。
51单片机定时/计数器的工作由两个特殊功能寄存器控制。TMOD 是定时器的工作方式寄存器,确定工作方式和功能;TCON 是控制寄存器,控制T0、T1的启动和停止及设置溢出标志。
51单片机有两个定时器,所以 TMOD 里面有两组。
GATE:是门控位, 一般默认GATE=0。
C/T :定时/计数模式选择位。低电平为定时模式;高电平为计数模式。
M1 M0:工作方式设置位。定时/计数器有四种工作方式。
TF1:T1溢出中断请求标志位。T1计数溢出时由硬件自动置TF1为1。CPU响应中断后TF1由硬件自动清0。T1工作时,CPU可随时查询TF1的状态。所以,TF1可用作查询测试的标志。TF1也可以用软件置1或清0,同硬件置1或清0的效果一样。
TR1:T1运行控制位。TR1置1时,T1开始工作;TR1置0时,T1停止工作。TR1由软件置1或清0。所以,用软件可控制定时/计数器的启动与停止。
TF0:T0溢出中断请求标志位,其功能与TF1类同。
TR0:T0运行控制位,其功能与TR1类同。
定时1s,即10000微秒,因此具体方法如下:
T0工作在方式1,应使TMOD寄存器的M1、M0=01;应设置C/T*=0,为定时器模式;对T0的运行控制仅由TR0来控制,应使相应的GATE位为0。定时器T1不使用,各相关位均设为0。所以,TMOD寄存器应初始化为0x01。
设定时时间5ms(即5 000µs),设T0计数初值为X,假设晶振的频率为11.059 2MHz,则定时时间为:
定时时间=(216−X)×12/晶振频率
则 10 000=(216 −X) ×12/11.059 2
X=64614.4
转换成十六进制:0xfc66,其中0xee装入TH0,0x00装入TL0。
由于采用定时器T0中断,因此需将IE寄存器中的EA、ET0位置1。
将定时器控制寄存器TCON中的TR0=1,则启动定时器T0;TR0=0,则停止定时器T0定时。
- #include<reg51.h>
- char i=100;
- void main ()
- {
- TMOD=0x01; //定时器T0为方式1
- TH0=0xfc; //设置定时器初值
- TL0=0x00;
- P1=0x00; //P1口8个LED点亮
- EA=1; //总中断开
- ET0=1; //开T0中断
- TR0=1; //启动T0
- while(1); //循环等待
- {
- ;
- }
- }
- void timer0() interrupt 1 //T0中断程序
- {
- TH0=0xfc; //重新赋初值
- TL0=0x00;
- i--; //循环次数减1
- if(i<=0)
- {
- P1=~P1; //P1口按位取反
- i=100; //重置循环次数
- }
- }
由此可知完成一个周期的时间为1.0019秒,相比软件计时,用定时器更加的精确。
计数器模式时,计数脉冲来自外部输入引脚T0或T1。当输入信号产生负跳变时,计数值增1。每个机器周期S5P2期间,都对外部输入引脚T0或T1进行采样。如在第1个机器周期中采得值为1,而在下一个机器周期中采得的值为0,则在紧跟着的再下一个机器周期S3P1期间,计数器加1。由于确认一次负跳变要花2个机器周期,即24个振荡周期,因此外部输入的计数脉冲的最高频率为系统振荡器频率1/24。
T1工作在方式1,应使TMOD的M1、M0=01;设置C/T*=1,为计数器模式;对T0运行控制仅由TR0来控制,应使GATE0=0。定时器T0不使用,各相关位均设为0。所以,TMOD寄存器应初始化为0x50。
计算定时器T1的计数初值 由于每按1次按钮开关,计数1次,按4次后,P1口的8只LED闪烁不停。因此计数器初值为65 536−4=65 532,将其转换成十六进制后为0xfffc,所以,TH0=0xff,TL0=0xfc。
由于采用T1中断,因此需将IE寄存器的EA、ET1位置1。 (4)启动和停止定时器T1 将寄存器TCON中TR1=1,则启动T1计数;TR1=0,则停止T1计数。
- #include <reg51.h>
- void Delay(unsigned int i) //定义延时函数Delay( ),i是形 //式参数,不能赋初值
- {
- unsigned int j;
- for(;i>0;i--) //变量i由实际参数传入一个值 //因此i不能赋初值
- for(j=0;j<125;j++)
- {;} //空函数
- }
-
- void main( ) //主函数
- {
- TMOD=0x50; //设置定时器T1为方式1计数
- TH1=0xff; //向TH1写入初值的高8位
- TL1=0xfc; //向TL1写入初值的低8位
- EA=1; //总中断允许
- ET1=1; //定时器T1中断允许
- TR1=1; //启动定时器T1
- while(1) ; //无穷循环,等待计数中断
- }
-
- void T1_int(void) interrupt 3 //T1中断函数
- {
- for(;;) //无限循环
- {
- P1=0xff; //8位LED全灭
- Delay(500) ; //延时500ms
- P1=0; //8位LED全亮
- Delay(500); //延时500ms
- }
- }
一般来说,中断函数中要尽量避免使用执行时间较长(耗时)的代码,以避免中断服务影响到主程序代码的执行效率。但是在上面外部中断的实验中,中断函数采用了软件延时函数去控制LED亮灭的间隔周期。我们可以换一种方式,不在中断函数使用延时循环,实现同样的功能。可以在main函数中写好几种亮灯模式代码和对应的模式标志位,按键中断发生后,中断服务程序只改变模式标志位,不做其他处理;由主程序根据模式标志,选择不同的亮灯模式。
具体代码如下:
- #include <REGX52.H>
-
- void Delay(unsigned int xms) //延时函数
- {
- 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;
- }
- }
- }
参考链接:
https://blog.csdn.net/NPNtoPNP/article/details/109540098
https://blog.csdn.net/zhouhaoNB_/article/details/127287647
https://blog.csdn.net/Pijiojio/article/details/137385384
此次实验加深了对中断系统和定时器、计数器的了解,同时动手在开发板上运行代码更是增加了实践经验,为以后的学习打下基础。
文章多有不足,如有错漏请多指教。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。