51单片机外中断0接一个按键Key,Key的次数最大次数为50。当按键按下时,串口输出当前次数“Current Value is XX”,同时用两个动态数码管显示当前次数。当次数等于学号后两位(若学号小于10,则学号后两位加10)时,串口输出当前次数“The Last Value is XX”,此后按键再按下则次数不变,串口无任何信息输出。
时间: 2023-11-22 14:55:09 浏览: 23
好的,这是一个比较典型的外部中断应用。具体实现的步骤如下:
1. 初始化串口和动态数码管。
2. 初始化外部中断0,使其可以检测到按键触发。
3. 编写外部中断0的中断服务函数,在其中实现计数器的自增、数码管的显示和串口的输出。
4. 在主函数中不断轮询计数器的值,当计数器达到学号后两位时,输出特定信息并停止计数。
下面是具体的代码实现:
```c
#include <reg52.h>
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
sbit D1 = P1^0; // 数码管1的数据线
sbit D2 = P1^1; // 数码管2的数据线
sbit R1 = P2^4; // 数码管1的位选线
sbit R2 = P2^5; // 数码管2的位选线
uchar count = 0; // 计数器
// 数码管显示表,0~9的BCD码对应的数码管段亮灭情况
uchar code segTable[] = {
0x3f, // 0
0x06, // 1
0x5b, // 2
0x4f, // 3
0x66, // 4
0x6d, // 5
0x7d, // 6
0x07, // 7
0x7f, // 8
0x6f // 9
};
// 数码管扫描函数,用于在两个数码管中交替显示计数器的值
void display() {
static bit flag = 0; // 用于交替显示的标志位
uchar digit1, digit2;
if (count >= 50) { // 超过最大次数时,直接显示 EE
digit1 = digit2 = 0x79;
} else { // 否则将计数器的值转换为BCD码,并分别显示在两个数码管上
digit1 = segTable[count / 10];
digit2 = segTable[count % 10];
}
if (flag) { // 显示数码管2
D1 = 0;
D2 = digit2;
R1 = 1;
R2 = 0;
} else { // 显示数码管1
D1 = digit1;
D2 = 0;
R1 = 0;
R2 = 1;
}
flag = !flag; // 切换标志位
}
// 外部中断0的中断服务函数,每次按下按键计数器加1,同时更新数码管和串口输出
void keyInterrupt() interrupt 0 {
count++;
display();
printf("Current Value is %d\n", count);
if (count == 10 + 8) { // 学号后两位为18,输出特定信息并停止计数
printf("The Last Value is %d\n", count);
IE = 0x81; // 禁止所有中断,除了定时器0中断
}
}
void main() {
TMOD = 0x21; // 定时器0为模式1,定时器1为模式2
TH1 = 0xfd; // 波特率为9600bps
TL1 = 0xfd;
TR1 = 1; // 启动定时器1
SCON = 0x50; // 设置串口为模式1,允许接收和发送
ES = 1; // 允许串口中断
P0 = 0xff; // 设置P0口为上拉输入
IT0 = 1; // 设置外部中断0为下降沿触发
EX0 = 1; // 允许外部中断0
while (1) { // 不断轮询计数器的值
display();
}
}
```
其中,串口的输出使用了标准库函数 `printf`,需要在编译器的选项中开启对标准库的支持。
注意,由于外部中断0使用了 P3.2 口,而 P3.2 口默认为复位口,需要在程序开头加入以下语句以禁用复位功能:
```c
void disable_reset() __naked {
AUXR |= 0x04;
_push_(P_SW1);
P_SW1 |= 0x80;
_push_(P_SW2);
P_SW2 |= 0x80;
_pop_(P_SW2);
_pop_(P_SW1);
_nop_();
_nop_();
AUXR &= ~0x04;
_ret_();
}
```