前言
中断: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;
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"
void Int0_Init(const unsigned char *mode){
EA=1;
EX0=1;
IT0=*mode;
}
void Int1_Init(const unsigned char *mode){
EA=1;
EX1=1;
IT1=*mode;
}
void Timer0_Init(const unsigned char *mode ,const unsigned char *HighVal , const unsigned char *LowVal ){
EA=1;
ET0=1;
TR0=1;
TMOD |= *mode;
TH0 = *HighVal;
TL0 = *LowVal;
}
void Timer1_Init(const unsigned char *mode ,const unsigned char *HighVal , const unsigned char *LowVal ){
EA=1;
ET1=1;
TR1=1;
TMOD |= *mode;
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);
Timer0_Init(0x01 , 0x4c , 0x00);
while(1){
Show();
}
}
void Int0_Routine(void) interrupt 0{
while(1){
Show();
if(k1==0){
ClearBuffer();
break;
}
}
}
void Timer0_Routine(void) interrupt 1{
++_50ms;
if(_50ms==20){
BEEP = !BEEP;
++Second;
_50ms =0;
if(Second==60){
++Min;
Second=0;
if(Min==60){
++Hour;
Min=0;
}
}
}
}
总结
关键在于看懂相关手册,结合原理图理解各个寄存器的含义,作用及工作方式。
|