一.写在前面
本次测试的为89c51的五个中断源(两个定时器/计数器中断,两个外部中断,一个串口中断)。
本次测试用到了89c51的以下4个特殊功能寄存器
定时器控制寄存器TCON(用6位);
串行口控制寄存器SCON(用2位);
中断允许寄存器IE;
中断优先级寄存器IP。
本次测试主要测试定时器中断和外部中断,至于SCON的串行口中断将在下一话中说。
1.TCON寄存器

TF1 : 定时/计数器1的中断请求标志位,当定时/计数器溢出时,该位自动置1,并向CPU发出中断请求,当CPU响应中断时,硬件会自动对该位清0。当然,你也可以用“位操作指令”对TF0进行置“1”或清“0”操作。
TF0 :定时/计数器0的中断请求标志位,与TF1原理相同。
IE1 : 外部中断1的中断请求标志位,当检测到外部中断引脚上存在有效的中断请求信号时,由硬件自动使IE1置1,当CPU响应该中断请求时,由硬件自动使IE1清0。
IT1 : 外部中断1的中断触发方式控制位
IT1 = 0 时,外部中断1为电平触发方式。CPU在每一个机器周期采样外部中断1请求引脚的输入电平,若外部中断1请求引脚为低电平,则使IE1位置1,若为高电平,则IE1清0。
IT1 = 1 时,外部中断1为边沿触发方式。CPU如果在两个连续的机器周期采样过程中,一个为高电平。接着下一个为低电平,那么IE1则置1,直到CPU响应该中断时,才由硬件使IE1位清0。
IE0 : 外部中断0的中断请求标志位,与IE1原理相同。
IT0 : 外部中断0的中断触发方式控制位,与IT1原理相同。
2.IP—–中断优先级控制寄存器
可按位寻址,地址位B8H
| IP 中断优先级控制寄存器 | |||||||
| B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | 
| – | – | PT2 | PS | PT1 | PX1 | PT0 | PX0 | 
- – (IP.7):保留
- – (IP.6):保留
- PT2(IP.5):定时2中断优先(8052用)
- PS (IP.4):串行口中断优先
- PT1(IP.3):定时1中断优先
- PX1(IP.2):外中断INT1中断优先
- PT0(IP.1):定时器0中断优先
- PX0(IP.0):外部中断INT0的中断优先
3.IE—–中断充许寄存器
可按位寻址,地址:A8H
| IE 中断充许寄存器 | |||||||
| B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | 
| EA | – | ET2 | ES | ET1 | EX1 | ET0 | EX0 | 
- EA (IE.7):EA=0时,所有中断禁止(即不产生中断);EA=1时,各中断的产生由个别的允许位决定
- – (IE.6):保留
- ET2(IE.5):定时2溢出中断充许(8052用)
- ES (IE.4):串行口中断充许(ES=1充许,ES=0禁止)
- ET1(IE.3):定时1中断充许
- EX1(IE.2):外中断INT1中断充许
- ET0(IE.1):定时器0中断充许
- EX0(IE.0):外部中断INT0的中断允许
对于默认情况,其优先级为
- 编号0;中断源INT0;入口地址:0003H
- 编号1;中断源T0;入口地址:000BH
- 编号2;中断源INT1;入口地址:0013H
- 编号3;中断源T1;入口地址:001BH
- 编号4;中断源R1/T1;入口地址:0023H
二.代码
1.定时器中断
/* Main.c file generated by New Project wizard
 *
 * Created:   周六 6月 22 2019
 * Processor: AT89C51
 * Compiler:  Keil for 8051
 */
#include <reg51.h>
#include <stdio.h>
#define SEG P2
#define SEG2 P3
typedef unsigned char uint8_t;
uint8_t cnt = 0;
uint8_t nums = 0;
// 0~9 数字字模
const uint8_t n[10] = {0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x2, 0x78, 0x0, 0x10};
void sleep(int t){
    uint8_t j = 120;
    for(; t>0; t--)
        for(; j>0; j--);
}
void updateSEG() interrupt 1{
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;
    cnt++;
    if(cnt == 20){
        SEG = n[nums];
        nums++;
        nums = nums==10?0:nums;
        cnt = 0;
    }
}
void main(void)
{ 
    // Write your code here
    uint8_t i;
    TMOD = 0x01;
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;
    EA = 1;//打开中断总开关
    ET0 = 1;//允许T0中断
    TR0 = 1;
    while (1){
        for(i = 0; i < 10; i++){
            SEG2 = n[i];
            sleep(20000);
        }
    }
}本次测试主要实现了在main中的SEG2一直循环显示0-9;计数器计时满50ms后触发T0中断当中断20次后即记满一秒钟时SEG数码管秒数加一。
编译的时候遇到了一个很奇怪的错误,百度了一下才解决(这里记录一下防止忘记)
"..\..\..\..\..\..\..\..\..\Program Files (x86)\Labcenter Electronics\Proteus 8 Professional\Tools\MAKE\RunTool.exe" --good-exits=0,1 --executable=C51.exe "..\main.c" ROM(SMALL) BROWSE DEBUG CODE OBJECTEXTEND PREPRINT  OBJECT("main.OBJ")
C51 COMPILER V9.56.0.0 - SN: Eval Version
COPYRIGHT Copyright (C) 2012 - 2016 ARM Ltd and ARM Germany GmbH. All rights reserved.
C51 COMPILATION COMPLETE.  0 WARNING(S),  0 ERROR(S)
"..\..\..\..\..\..\..\..\..\Program Files (x86)\Labcenter Electronics\Proteus 8 Professional\Tools\MAKE\RunTool.exe" --good-exits=0,1 --executable=BL51.exe "main.OBJ" TO "Debug.OMF"  
BL51 BANKED LINKER/LOCATER V6.22 - SN: Eval Version
COPYRIGHT KEIL ELEKTRONIK GmbH 1987 - 2009
make: *** [Debug.OMF] Error 1
*** ERROR L121: IMPROPER FIXUP
    MODULE:  MAIN.OBJ (MAIN)
    SEGMENT: ABSOLUTE
    OFFSET:  000BH
******************************************************************************
* RESTRICTED VERSION WITH 0800H BYTE CODE SIZE LIMIT; USED: 0092H BYTE ( 7%) *
******************************************************************************
Program Size: data=21.0 xdata=0 code=290
LINK/LOCATE RUN COMPLETE.  0 WARNING(S),  1 ERROR(S)
Error code 2
原因:默认的ROM给小了。
解决方法:


效果如下:
额………这Windows1903也太坑了吧?我的录屏工具ScreenToGif直接凉了。

换了一个GifCam好像也挺好用的(滑稽

2.外部中断
/* Main.c file generated by New Project wizard
 *
 * Created:   周六 6月 22 2019
 * Processor: AT89C51
 * Compiler:  Keil for 8051
 */
#include <reg51.h>
#include <stdio.h>
#define SEG P2
#define SEG2 P1
typedef unsigned char uint8_t;
uint8_t nums = 0;
// 0~9 数字字模
const uint8_t n[10] = {0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x2, 0x78, 0x0, 0x10};
void sleep(int t){
    uint8_t j = 120;
    for(; t>0; t--)
        for(; j>0; j--);
}
void int0() interrupt 0{
    uint8_t i;
    for(i = 0; i < 10; i++){
        SEG = n[i];
        sleep(10000);
    }
    SEG = 0xFF;
}
void main(void)
{ 
    // Write your code here
    uint8_t i;
    
    EA = 1;//打开中断总开关
    IT0 = 0;//电平触发(默认)
    EX0 = 1;//允许INT0中断
    while (1){
        for(i = 0; i < 10; i++){
            SEG2 = n[i];
            sleep(10000);
        }
    }
}main中循环跑SEG2显示0-9 当按钮按下时触发int0中断,main函数暂停执行,此时SEG数码管从0显示到9后结束中断子程序返回main函数,SEG2从暂停的位置继续执行。
效果图如下:

3.中断优先级
/* Main.c file generated by New Project wizard
 *
 * Created:   周六 6月 22 2019
 * Processor: AT89C51
 * Compiler:  Keil for 8051
 */
#include <reg51.h>
#include <stdio.h>
#define SEG P2
#define SEG2 P1
#define SEG3 P0
typedef unsigned char uint8_t;
uint8_t nums = 0;
// 0~9 数字字模
const uint8_t n[10] = {0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x2, 0x78, 0x0, 0x10};
void sleep(int t){
    uint8_t j = 120;
    for(; t>0; t--)
        for(; j>0; j--);
}
void int0() interrupt 0{
    uint8_t i;
    for(i = 0; i < 10; i++){
        SEG = n[i];
        sleep(10000);
    }
    SEG = 0xFF;
}
void int1() interrupt 2{
    uint8_t i;
    for(i = 0; i < 10; i++){
        SEG2 = n[i];
        sleep(10000);
    }
    SEG2 = 0xFF;
}
void main(void)
{ 
    // Write your code here
    uint8_t i;
    
    EA = 1;//打开中断总开关
    IT0 = 0;//电平触发(默认)
    EX0 = 1;//允许INT0中断
    IT1 = 0;//电平触发(默认)
    EX1 = 1;//允许INT1中断
    PX0 = 1;//INT0优先
    while (1){
        for(i = 0; i < 10; i++){
            SEG3 = n[i];
            sleep(10000);
        }
    }
}将INT0和INT1两个外部中断都启用;此时我们还需要设置IP(中断优先级寄存器)如果不设置IP的话,INT0和INT1的优先级相同,所以我们设置IP寄存器中的PX0为1即外部中断INT0的优先级高于INT1。
效果图如下:
