小白向单片机笔记六:中断
前言
中断:CPU在处理事件A的过程中,中断源事件B产生中断请求,CPU暂停事件A的处理,CPU处理事件B,事件B处理完成后继续处理事件A。
51单片机仅包含:两个外部中断(INT0,INT1),两个定时器中断(T0 , T1),一个串口中断(UART)。
虽然STC89Cxx中文手册中说有8个中断,但在C51和C52芯片类型中只有5个中断,看CPU原理图可以找到。
大部分芯片都有四个中断优先级,且可自行定义每个中断的优先级(具体看芯片手册)
。C51类型只有两个中断优先级,在IP寄存器中可自行设置优先级
。
- 高优先级中断可以打断低优先级中断,低优先级中断不能打断高优先级中断
- 同优先级中断先到先处理
一、中断原理及初始化
CPU结构图
中断结构图
中断优先级
C51或C52的中断优先级只有两级,可由IP优先级寄存器对相应的中断设置优先级。同一优先级的中断请求,按时间先后顺序处理。
同一优先级,同一时刻中断请求,由硬件系统硬件确定的自然优先级形成,如下。
中断允许寄存器IE及优先级设置
要使用中断,首先要将相应中断允许寄存器置为1。由中断结构原理图可知,其中EA为总允许位,EX0到ES为相应中断的允许标志位
例如要启用定时器中断0(T0),并设置其优先级为高级:
EA=1;//总允许位
ET0=1;//定时计数器允许位
PT0=1;//设置为高优先级
中断请求标志TCON
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:88H | TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 | TCON |
定时器1中断请求标志位 | 定时器中断1运行控制位:同TR0 | 定时器0中断请求标志位 | 定时器中断0运行控制位:置1运行,置0停止 | 外部中断1请求标志位 | 外部中断1触发方式控制位:同IT0 | 外部中断0请求标志位 | 外部中断0触发方式控制位:置1下降沿触发;置0低电平触发 | – |
表格中请求标志位(黑色部分),当满足中断产生条件时,由硬件自动置1,处理完成后由硬件自动置0。亦可编程置位,一般编程中可不管
红色部分需要进行设置,以启用定时器中断0(T0),并设置其优先级为高级
为例,除了要开启相关中断允许位,还要使定时器中断0开始工作。
EA=1;//总允许位
ET0=1;//定时计数器允许位
TR0=1;//定时器中断0开始运行
PT0=1;//设置为高优先级
定时器工作方式寄存器TMOD
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:89H | GATE | C/ T ‾ \overline{\text{T}} T | M1 | M0 | GATE | C/ T ‾ \overline{\text{T}} T | M1 | M0 | TMOD |
GATE=0,定时器1溢出即可产生中断;GATE=1 , 定时器1溢出且P3^3引脚产生高电平才能产生中断 | C/ T ‾ \overline{\text{T}} T=0,定时模式;C/ T ‾ \overline{\text{T}} T=1,计数模式 | 工作方式设置位 | 工作方式设置位 | GATE=0,定时器0溢出即可产生中断;GATE=1 , 定时器0溢出且P3^2引脚产生高电平才能产生中断 | =0,定时模式;=1,计数模式 | 工作方式设置位 | 工作方式设置位 | – |
红色部分用于设置定时器0的工作方式,黑色部分用于设置定时器1工作方式。
M1M0 | 工作方式 | 说明 | 计数方式 |
---|---|---|---|
00 | 方式0 | 13位定时/计数器,TH0八位及TL0低五位参与计数,TL0低五位溢出向TH0进位,TH0溢出产生中断 | X= 2 13 2^{13} 213-N,X为初值,N计数个数 |
01 | 方式1 | 16位定时计数器,TH0和TL0参与计数 | X= 2 16 2^{16} 216-N |
10 | 方式2 | 8位自动重装定时计数器,TL0计数溢出产生中断后,TH0自动将其数值赋值给TL0, TL0从该值开始计数,TH0值不变 | X= 2 8 2^{8} 28-N |
11 | 方式3(仅对计数器T0有效) | T0分为独立的8位计数器TL0和TH0,TL0由T0状态控制位控制,TL0可以为定时器或计数器。TH0由T1状态控制位控制,同时占用定时器T1的中断请求源TF1,并只能作为定时器使用。所以工作在方式3下时,T1无法产生中断,因为T1的中断请求源TF1被T0占用了。 T0处于工作方式3时,T1可定为方式0、方式1和方式2,用来作为串行口的波特率发生器,或不需要中断的场合 |
定时计数器的初值计算
当外部晶振频率为 11.0592 M H Z 11.0592MHZ 11.0592MHZ,工作于方式1时,定时50ms, 需要设置的TH0,TL0初值计算如下:
- 51单片机内部时钟频率是外部时钟频率的12分频,即机器周期为: 12 11.0592 \frac{12}{11.0592} 11.059212=1.085069 , 即一个机器周期要1.085069us(微秒)
- 定时50ms(毫秒)需要多少个机器周期: 50000 1.085069 \frac{50000}{1.085069} 1.08506950000=46,080.01
- 工作于方式三,则初值为: 2 16 − 46080 = 19456 2^{16}-46080=19456 216−46080=19456
- 19456 19456 19456转换为16进制为:4c00
- 则 T H 0 = 0 x 4 c TH0=0x4c TH0=0x4c , T L 0 = 0 x 00 TL0=0x00 TL0=0x00
中断号
中断函数格式
void 自定义函数名(void) interrupt 中断号{
中断处理代码;
}
二、秒表计时代码
1.数码管显示工具代码Digit_utils.h
#include "reg52.h"
typedef unsigned int uint;
sbit P22=P2^2;
sbit P23=P2^3;
sbit P24=P2^4;
uint display[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
void Choose_Digit(int i){
switch(i){
case 1: P24=1;P23=1;P22=1;break;
case 2: P24=1;P23=1;P22=0;break;
case 3: P24=1;P23=0;P22=1;break;
case 4: P24=1;P23=0;P22=0;break;
case 5: P24=0;P23=1;P22=1;break;
case 6: P24=0;P23=1;P22=0;break;
case 7: P24=0;P23=0;P22=1;break;
case 8: P24=0;P23=0;P22=0;break;
}
}
#endif
2.秒表处理代码StopWatch.h
#ifndef _STOPWATCH_H_
#define _STOPWATCH_H_
#include "Digit_utils.h"
/*缓存显示内容*/
int Second=0;
int Min=0;
int Hour=0;
int ShowBuffer[] = {0 , 0 , 0 , 0 ,0 , 0 };
void ClearBuffer(){
int i;
for(i=0 ; i<6;++i){
ShowBuffer[i]=0;
}
Hour=0;
Min=0;
Second=0;
}
void Split_Time(){
ShowBuffer[0] = Hour / 10;
ShowBuffer[1] = Hour %10;
ShowBuffer[2] = Min / 10;
ShowBuffer[3] = Min % 10;
ShowBuffer[4] = Second /10;
ShowBuffer[5] = Second %10;
}
void Show(){
int i;
Split_Time();
for(i=0; i<6 ; i+=2){
Choose_Digit(i+1);
P0 = display[ShowBuffer[i]];
P0=0x00;
Choose_Digit(i+2);
P0= display[ShowBuffer[i+1]] | 0x80;//按位或是为了增加小数点
P0=0x00;
}
}
#endif
3.中断初始化代码Interrupt_utils.h
#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_
#include "reg52.h"
/*中断初始化*/
/*外部中断0*/
void Int0_Init(const unsigned char *mode){//外部中断0的触发方式,0低电平触发,1下降沿触发
EA=1;//总中断允许位
EX0=1;//外部中断0允许位
IT0=*mode;
}
/*外部中断1*/
void Int1_Init(const unsigned char *mode){//外部中断1的触发方式,0低电平触发,1下降沿触发
EA=1;//总中断允许位
EX1=1;//外部中断0允许位
IT1=*mode;
}
void Timer0_Init(const unsigned char *mode ,const unsigned char *HighVal , const unsigned char *LowVal ){
EA=1;//打开中断总允许位
ET0=1;//打开T0中断允许位
TR0=1;//定时/计数器中断0开始工作
TMOD |= *mode;//设置T0工作模式
TH0 = *HighVal; //高八位寄存器初值
TL0 = *LowVal; //第八位寄存器初值
}
void Timer1_Init(const unsigned char *mode ,const unsigned char *HighVal , const unsigned char *LowVal ){
EA=1;//打开中断总允许位
ET1=1;//打开T0中断允许位
TR1=1;//定时/计数器中断0开始工作
TMOD |= *mode;//设置T0工作模式
TH1 = *HighVal; //高八位寄存器初值
TL1 = *LowVal; //第八位寄存器初值
}
#endif
4. 主函数 main.c
#include "interrupt_utils.h"
#include "StopWatch.h"
sbit k1=P3^1;
sbit k3=P3^2;
sbit BEEP = P2^5 ;
static int _50ms=0;
void main(){
Int0_Init(1);//外部中断0初始化
Timer0_Init(0x01 , 0x4c , 0x00);//定时器中断0初始化
while(1){
Show();
}
}
void Int0_Routine(void) interrupt 0{//K3外部中断,终止计数,显示数值
while(1){
Show();
if(k1==0){//外部按键清0
ClearBuffer();
break;
}
}
}
void Timer0_Routine(void) interrupt 1{ //定时器中断0,秒表定时
++_50ms;
if(_50ms==20){//中断一次50ms,中断20次1s
BEEP = !BEEP;//中断同时蜂鸣器响应
++Second;
_50ms =0;
if(Second==60){
++Min;
Second=0;
if(Min==60){
++Hour;
Min=0;
}
}
}
}
总结
关键在于看懂相关手册,结合原理图理解各个寄存器的含义,作用及工作方式。