用Proteus学习51单片机之中断

最近刚做好一个站,基于rails 3,教程为主,大家捧场看看,谢谢!www.yo945.com

以52单片机来说,一共有6个中断源,其说明如下(序号用于中断程序的编写):

中断源名称 默认级别 序号 说明
INT0 最高 0 外部中断0,由P3.2端口线引入,低电平或下降沿引起
INT1 第3 2 由P3.3端口线引入,低电平或下降沿引起
T0 第2 1 定时器/计数器0中断,由T0计数器计满回零引起
T1 第4 3 定时器/计数器1中断,由T1计数器计满回零引起
T2 最低 5 定时器/计数器2中断,由T2计数器计满回零引起
TI/RI 第5 4 串行口中断,串行端口完成一帧字符发送/接收后引起

中断的允许和关闭,由中断允许寄存器IE控制,而IE又细分为7位,详细控制到每一个中断的开关

位序号 位符号 位地址 说明
D0 EX0 A8H INT0中断允许控制
D1 ET0 A9H T0中断允许控制
D2 EX1 AAH INT1中断允许控制
D3 ET1 ABH T1中断允许控制
D4 ES ACH 串行口中断允许控制
D5 ET2 ADH T2中断允许控制
D6 -- --  
D7 EA AFH 全局中断控制,关闭则所有中断不可用

上表中,都是把某位设置为1,表示开启对应的中断允许,设0表示关闭对应的中断允许,同时,如果要允许任意中断,首先必须设置EA为允许

还允许设置中断的优先级,在中断优先级寄存器IP中设置。不过对我来说,暂时没用,就不记录了,相信到时候要用的时候,很容易就能找到对应的信息。

在《教程》中,这一章只说了定时器中断,所以这里也只写定时器中断,翻了书的目录,其他中断在后面,一起来期待后续吧。

51单片机内部共有两个16位可编程的定时器,即T0和T1,而52单片机又加了一个T2。这些定时器是单片机内部的独立硬件,并不需要耗费额外的CPU时间去帮它们计时。当定时器的计数器计满后,会产生中断,从而通知CPU处理中断。

T0或T1都有两种模式,定时模式和计数模式,对于定时模式来说,中断时即表示定时时间已到,而对于计数模式来说,中断时表示计数值已满。不管是什么模式,其本质都是+1计数器,它的计数脉冲有两个来源,一是系统的时钟振荡器输出脉冲经过12分频后的值;二是由T0或T1的引脚输入。在计数模式时,对于被计数的频率有一定的要求,即被检测的电平,至少要维持一个机器周期(即12个振荡周期),如当晶振频率为12MHZ时,最高计数超过1/2MHZ。

定时器(计数器)主要由两个寄存器控制,即TCON和TMOD。

TMOD各位意义如下:

位序号 D7 D6 D5 D4 D3 D2 D1 D0
位符号 GATE C/T M1 M0 GATE C/T M1 M0
  针对T1 针对T0

说明如下:

GATE:门控制位,为0时表示仅受TCON中的TRX控制,为1时表由TRX和外部中断引脚上电平共同控制

C/T:1时表示计数模式,0时表示定时器模式

M1和M0:工作方式选择,如下:

M1 M0 工作方式
0 0 方式0,13位定时器/计数器
0 1 方式1,16位定时器/计数器
1 0 方式2,8位初值自动重装的8位定时器/计数器
1 1 方式3,仅适用于T0,分成两个8位计数器,T1停止计数

TCON各位意义如下(后面数字为1的针对T1,为0的针对T0):

位序号 D7 D6 D5 D4 D3 D2 D1 D0
位符号 TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0

说明如下:

TFX:定时器X溢出标志位,计时器计数满时,该位置1,并申请中断,进入中断服务程序后,由硬件自动清0.如果使用软件查询方式的话,需要手动清0

TRX:定时器X运行控制位。GATE=1时,TRX=1且INTX为高电平时,启动定时器;GATE=0时,TRX=1时启动定时器

IEX:外部中断X请求标志。

ITX:外部中断X触发方式标志。

------------------------------------------------------------------------

程序以T0的工作方式1,即16位定时器,设置GATE=0,此时计数器的运行由TR0控制,当TR0=1时,计数器开始计数。由于是16位计数器,所以共有65535次计数,当计数满后,还要溢出操作一次,所以当触发中断时,共需要65536次。由于中断需要在计数满时才会执行,所以如果计数不是刚好是65536次的话,必然需要给计数器设置一个初始值 。比如我们希望计数1000次时触发中断,那么,就要把计数器的初始值设置为65536-1000=64536.由于计数器每一个机器周期加1,也即12个时钟周期,因此每一个计数代表的时间是12/f,如12MHZ的晶振,一个计数表示12/12000000=1us。若我们希望产生一次中断的时间为t,晶振的频率为f,则初始值为:

t*f/12    注意单位的一致

比如,我希望1ms产生一次中断,我的晶振为12MHZ,则初始值为(注意,把1ms换算成0.001s)

0.001*12000000/12 = 1000

由于计数器实际分高8位TH0和TL0(对应T1的就是TH1和TL1了),所以要把初始值的高位存储在TH0中,而低位存储在TL0中,仍旧以上面的例子来说,则

TH0 = 1000/256 = 3

TL0 = 1000%256 = 232

中断程序的格式如下:

//using 工作组不是必须的,暂时不使用
//中断号即前面列表中的序号,T0的中断序号是1
void 函数名称 interrupt 中断号 using 工作组
{
//程序主体
}

-----------------------------------------------------------------

前置知道介绍完毕,现在编写程序。本来想显示一个定时器,显示在数码管上,由于在Proteus上实现不了数码管的连续显示(PS:现在我知道了,是可以正常显示的……,不过程序就不改了,反正主要是为了说清楚中断和计时器),所以就简化一下程序,改为LED灯亮一秒钟,灭一秒钟。由于计数器最多能计65535,即60多ms,所以我们让它每50ms触发一次中断,这样20次后,就是一秒了,因此初始值就是15536了。

原理图:

image

源程序:

#include <reg52.h>

#define uchar unsigned char
#define uint unsigned int

uchar total = 0;
sbit led = P2^0;
//中断程序,在中断程序中的操作尽量少,所以把LED之类的操作,放在主函数中
void T0_time() interrupt 1
{
//每次中断都执行一次初始化,保证每次都是50ms
TH0 = 15536/256;
TL0 = 15536%256;

//中断一次即把计数加1
total++;
}

void main()
{
TMOD = 0x01; //设置定时器0,工作方式1
TH0 = 15536/256;//设置初始值高位
TL0 = 15536%256;//设置初始值低位
EA = 1;//允许全局中断
ET0 = 1;//允许定时器0的中断
TR0 = 1; //开启定时器0
while(1)
{
if(total==20)
{
total=0;
led=~led;
}
}
}
原文地址:https://www.cnblogs.com/varlxj/p/1712256.html