一.写在前面
本次测试的为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。
效果图如下: