1. 51-microcontroler

51-microcontroler-第七话-中断

一.写在前面

本次测试的为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的中断允许

对于默认情况,其优先级为

  1. 编号0;中断源INT0;入口地址:0003H
  2. 编号1;中断源T0;入口地址:000BH
  3. 编号2;中断源INT1;入口地址:0013H
  4. 编号3;中断源T1;入口地址:001BH
  5. 编号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。

效果图如下: