一、C51中断系统
定时器一直是单片机比较难且重要的一部分,刚学51单片机时对定时器中断等部分学的一知半解,过了很长一段时间再回去理解了一遍方才恍然大悟,在此写下自己的拙见,欢迎指正。
STC89C52RC
单片机的中断系统分为三大类共五个中断(串口收发算1个):
本文主要讲外部中断
和定时器中断
。
二、晶振频率与周期
51单片机外部有一个晶振
,就是金属壳的两只脚的上面标着11.0592
,代表的就是单片机主震频率:$11.0592MHz=11.0592 \times 10^6Hz$。$HZ$是频率单位,它是每秒钟的周期性变动重复次数的计量。换个角度想知道了频率就等于知道了周期。即,$时钟周期=\frac {1} {11.0592 \times 10^6Hz}(s)$。对于定时
模式,是对机器周期
计数。51单片机一个机器周期
是12个时钟周期
(查数据手册),即:
$机器周期=12\times 时钟周期T=\frac {12} {11.0592 \times 10^6}(s)=\frac {12} {11.0592 }(us)$
51单片机有两个定时器
和两个外部中断
口,T0、T1、INT0、INT1。定时器我们就挑一个讲,定时器0内部有两个寄存器TH0和TL0,都是一字节的,分别是定时器0高位寄存器(TH0),定时器0低位寄存器(TL0), 我们知道2字节=16位,T0最大能存65535。
每过一个机器周期$\frac {12} {11.0592 }$(us),寄存器的值+1
,当加到溢出后发出一个溢出中断
,我们的程序可以捕获到这个中断,就可以知道此时经历了n个机器周期
,根据公式[所需时间t=n×机器周期]
就可以知道经历多长时间。但是,当我们要指定一定时间产生中断时,可以通过事先设定初始值达到指定时间产生中断。我们拿定时器工作模式1来说,模式1时TH、TL两个寄存器的8位同时使用(后面会解释为什么),两个8位叠加一起就是16位
。众所周知16位的最大值就是65535。就好像本来我们让水一滴一滴的滴入一个只能装载65535滴水的容器里,当超过则溢出,倒掉重新收集水。但是我们可以先在这个容器里填了与10000滴水等体积的水泥,然后让水再滴,这时候只需要滴入[(65535+1)-10000]
滴水就会溢出了,之后就是像上面那样倒掉再滴水,但是却缩短了集满水的时间。
三、定时器的配置
知道了这些我们可以做什么呢,我们要做的事情就是拿定时器,去得到我们想要的时间。要做的第一步事情,就是配置寄存器
,也叫定时器的初始化
。
定时器的设置需要参考单片机的使用手册:
TMOD
寄存器是特殊功能寄存器,它是一个8位的寄存器,8个位对半分,高4位用来控制定时器1(T1
),低4位用来控制定时器0(T0
)。我们拿T0来说。
先说第0和1位(寄存器从右到左代表从低到高位)的M0、M1,M
是模式的意思,用来设定定时器的工作模式。比如说模式0,13位定时器,二进制的13位
最大可以存8191,最后到溢出后发出一个溢出中断
。模式1是16位定时器,同理16位,最大能存65535。
定时器工作模式0:
当GATE=0
时或门必定输出1
,且TR=1
时则定时器开始
计数,因此我们也可以用TR
来控制定时器计数的启动
和停止
。
TR:定时器运行控制器。
标志位:
TF:定时器溢出标志。
IE:外部中断请求标志位。
IT:外部中断控制位。
EA:总允许开关。
源允许开关:
EX:外部中断源允许开关
ET:定时器中断源允许开关。
ES:串口中断源允许开关。
断优先级$^{[注2]}$设置: PX、PT、PS
=1:高优先级
=0:低优先级
$^{[注2]}$高优先级的程序可以打断低优先级的程序。
当其中一整条路上的开关都导通
了,相当于从这条路发出了个中断指令
。那么要启用指定的定时器的初始化程序就要设置
相对应这条路上的各个开关变量。
下面用定时器0
配置一个1毫秒
中断一次的定时器。
/*定时器0初始化*/
void timer0_init(){
TMOD |=0x01; //定时器0为模式1,16位定时器,当
/*1ms=1000us,由公式1得:n=所需时间t/机器周期
=1000/(12/11.0592)=922*/
TH0=(65535-922)/255;
TL0=(65535-922)%255+1;
TR0=1; //定时器运行控制器。
ET0=1; //打开定时器0中断
EA=1; //总中断
}
当TH0
、TL0
寄存器和运行到溢出则中断标志位TF0=1
(被硬件置1,当单片机接收到中断指令后又被硬件置0),整条路导通产生中断。
中断向量
中断的工作原理就是本来CPU在运行一条主程序(main
函数)时,出现了个中断指令,让CPU先去处理另一个
程序,那么CPU该怎么走到那个程序去,就需要一个向量
给它指路。执行完后又循着那个向量的反方向回去原来
的主程序。中断系统中,每个中断都配有一个向量,我们给每个向量编个号,让它们以次序排列。
中断向量表:
承接上面那个程序,来写中断服务函数
。已经知道,定时器1ms
发生一次中断,就要执行一次下面这个程序,那么如果想要它计时1秒然后显示出来,其实就是1000
个定时器1的中断。可以在中断服务函数里对全局变量
进行操作。
int s=ms=0;//定义全局变量
/*中断服务函数*/
void timer0(void) interrupt 1 //这个(interrupt 1)定时器0的中断向量次序
{
TH0=(65535-922)/255;//重新装载进去
TL0=(65535-922)%255+1;//
ms++;
if(ms==1000) {s++;ms=0;}
}
void mian()
{
while(1) display(s);//显示出来,不再做展示
}