蓝桥杯单片机学习计划之中断
前言
在上一节中,简单介绍了定时器的相关内容,一起学习并使用了定时器实现LED灯闪烁,那么在这一节中,我们就来一起学习一下中断。
一、中断是什么?
STC15系列单片机用户手册中的第六章,介绍了中断系统,里面讲的很详细:
但是理解起来可能比较困难,所以我举一个例子方便大家理解:假如我正在电脑上打游戏,这时候门铃响了,原来是快递小哥来送快递了,那么这时候我就要先暂停游戏,去开门取快递,取完快递再返回来打游戏,其中取快递的过程就是中断过程。我本来在电脑上打游戏打的好好的,但这时候发生了比打游戏更重要的事情——拿快递,那么我就要暂停游戏,先去处理拿快递这个事情,拿完快递再来继续打游戏。即在做一件事情的时候发生了一件更重要的事情去做,这时候就需要先去处理更重要的事情,处理完毕再返回来处理之前的事情,这就是中断。在单片机中,假如此时此刻正在while循环中执行某段程序,这时候发生了中断,有了更重要的程序去执行,那么单片机就会先暂停当前执行的程序,转而去处理更重要的程序,处理完毕后再回来原来被打断的程序中继续执行,这就是单片机的中断过程。
那么单片机要如何触发中断呢?或者说有什么紧急事件能够产生中断呢?
看下面这张图:
可以从上图中看到,STC15F2K60S2一共有14个事件可以触发中断,这一节我们先学习定时器0中断。
二、定时器0中断
1.如何触发中断?
那么定时器0要怎么样才能触发中断呢?很简单,下图中描述了各个中断的触发行为,可以看到定时器0溢出的时候就会触发中断,这里的溢出上一节讲过,就是TH0和TL0两个寄存器都为255时,再加1就会产生溢出,这时候就会触发中断。
2.定时器0中断相关的寄存器
关于定时器0中断相关的寄存器,我们只需要看中断允许寄存器IE其中的两位EA和ET0,先来看ET0,T0的溢出中断允许位,T0就是定时器0,这一位就是控制着定时器0中断的开关,只有这一位为1,定时器0中断才能使用,否则无法使用。再来看EA,这一位是总中断允许位,即如果这一位为0,即使ET0=1定时器0也无法开启中断,相当于是有两个开关来控制定时器0中断,只有EA和ET0这两个开关都打开时(都为1),定时器0才能够触发中断。
再来看下面这张中断结构图,红圈中的就是定时器0的中断结构,我们可以看到定时器0中断结构中有三个开关,第一个ET0、第二个EA,第三个我们暂时不去管它,从结构图中我们能清晰的看出ET0和EA这两个开关的作用,只有这两个开关同时闭合,定时器0才能产生中断,否则只要有一个断开,就无法产生中断。
到这里大家可能会有疑问,那么中断程序怎么进入呢?其实手册中都已经写好了,我们来看下图:
我们暂时不需要去纠结这个中断程序是怎么来的,为什么要这样写,只需要直接照这个敲入代码中就可以了,下面来写程序
三、代码
这个程序中结合了之前写过的数码管和按键程序,数码管扫描和按键消抖都不再使用延时了,而是利用定时器进行处理。实现的功能是按一下S7按键,数码管上显示的数字会加1。
#include <STC15F2K60S2.H>
#include "intrins.h"
sbit S7 = P3^0;
unsigned char SEG_Code[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//0-9段码
unsigned char SEG_Buf[8];//显示数据缓冲区
unsigned int seg_num;
bit timer_flag_1ms;
void Key_scan()
{
static bit key_backup=1;
static unsigned char shake_cnt=0;
if(timer_flag_1ms)//1ms刷新
{
timer_flag_1ms=0;
if(S7 != key_backup ) //当前按键状态和上一次状态不同,说明按键有动作
{
if(++shake_cnt>=50)//50ms消抖
{
shake_cnt=0;
if(S7 != key_backup)//再次判断是否不同
{
if(key_backup==1)//上一次状态为1,说明当前按键是按下
{
seg_num+=1;
}
key_backup=S7;//保存当前按键状态
}
}
}
}
}
void Seg_Display()//数码管扫描函数
{
static unsigned char seg_index=0;//显示数据索引
static unsigned char seg_temp=0x01;//位索引
P2 = (P2&0x1f) | 0xC0; //消隐
P0= 0x00;
P2=0x00;
P2 = (P2&0x1f) | 0xE0;//选通段锁存器
P0=SEG_Code[SEG_Buf[seg_index++]]; //送入段码
P2=0x00;
P2 = (P2&0x1f) | 0xC0;//选通位锁存器
P0= seg_temp;//送入位码
P2=0x00;
seg_temp<<=1;
if(seg_index>7)
{
seg_index=0;
seg_temp=0x01;
}
}
void main()
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;//开启定时器0中断
EA=1;//开启总中断
while(1)
{
Key_scan();
SEG_Buf[4]=seg_num/1000;
SEG_Buf[5]=seg_num%1000/100;
SEG_Buf[6]=seg_num%100/10;
SEG_Buf[7]=seg_num%10;
}
}
void Timer0_Rountine(void) interrupt 1 //定时器0中断,1ms
{
timer_flag_1ms=1;
Seg_Display();//使用定时器进行数码管扫描
}