在keil中利用有6位14引脚数码管,设计一款基于89c51单片机的数字钟,要求能显示24小时制时间,并可随时进行时间校对调整,具体要求如下: 1. 正常显示:单片机中装入程序后,接通电源即数码管显示屏显示000000,开始计时,右边二位数码管显示为秒,每1秒加1,加到60秒进1,右边二位数码管显示回零,继续从0开始计时,中间二位数码管显示为分钟,分加到60时进1,中间二位数码管显示回零,继续从0开始计时,左边二位数码管为小时,加到24回零,继续按规则计时 2. 按键调整时间:按下K1键为调整秒钟的调整,每按下一次加一,按到60归零。按下第K2键为调整分钟的调整,每按下一次加一,按到60归零。按下K6键为调整时钟的调整,每按下一次加一,按到24归零。按下K3,K4,K5键,分别对秒分时进行清零。按下K7让数码管每0.5秒加1,按下K8为让数码管每2秒加1。
时间: 2023-09-25 16:08:11 浏览: 90
51单片机数码管时钟设计
5星 · 资源好评率100%
这是一道典型的单片机应用题,需要设计时钟模块、按键模块、数码管驱动模块等,以下是具体实现步骤:
1. 时钟模块:利用定时器中断来实现计时功能。初始化定时器,在定时器中断服务函数中对秒、分、时进行累加,并将计数值转换为BCD码后显示在数码管上。
2. 按键模块:利用外部中断和按键消抖算法来实现按键功能。定义按键对应的外部中断号和响应函数,在响应函数中进行按键消抖判断和相应的时间调整操作。
3. 数码管驱动模块:利用端口输出来实现数码管的驱动。定义数码管显示函数和扫描函数,在显示函数中将要显示的数值转换为数码管对应的段码,保存到一个数组中,然后在扫描函数中循环显示每个数码管。
完整的程序框架如下:
```
#include <reg51.h> // 包含单片机寄存器的头文件
#define uchar unsigned char
#define uint unsigned int
uchar sec, min, hr; // 秒、分、时的计数值
uchar sec_disp[2], min_disp[2], hr_disp[2]; // 分别用于存储BCD码转换后的秒、分、时的数码管显示值
// 数码管段码表
uchar code seg_table[] = {
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71
};
// 数码管位码表
uchar code pos_table[] = {
0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf
};
// 初始化定时器
void init_timer() {
TMOD = 0x01; // 定时器0,模式1
TH0 = 0x3c; // 定时1ms,初值
TL0 = 0xb0;
ET0 = 1; // 开启定时器0中断
TR0 = 1; // 启动定时器0
EA = 1; // 开启总中断
}
// 定时器0中断服务函数
void timer0_isr() interrupt 1 {
static uchar cnt_500ms = 0, cnt_2s = 0;
TH0 = 0x3c; // 定时1ms,重置初值
TL0 = 0xb0;
// 秒计数
sec++;
if (sec >= 60) {
sec = 0;
// 分计数
min++;
if (min >= 60) {
min = 0;
// 时计数
hr++;
if (hr >= 24) {
hr = 0;
}
}
}
// 数码管显示
sec_disp[0] = seg_table[sec % 10]; // 秒个位
sec_disp[1] = seg_table[sec / 10];
min_disp[0] = seg_table[min % 10]; // 分个位
min_disp[1] = seg_table[min / 10];
hr_disp[0] = seg_table[hr % 10]; // 时个位
hr_disp[1] = seg_table[hr / 10];
// 数码管扫描
P2 = pos_table[0]; // 选中第一位数码管
P0 = sec_disp[0]; // 显示秒个位
delay(1); // 延时1ms
P0 = 0x00; // 关闭数码管
P2 = pos_table[1]; // 选中第二位数码管
P0 = sec_disp[1]; // 显示秒十位
delay(1);
P0 = 0x00;
P2 = pos_table[2]; // 选中第三位数码管
P0 = min_disp[0]; // 显示分个位
delay(1);
P0 = 0x00;
P2 = pos_table[3]; // 选中第四位数码管
P0 = min_disp[1]; // 显示分十位
delay(1);
P0 = 0x00;
P2 = pos_table[4]; // 选中第五位数码管
P0 = hr_disp[0]; // 显示时个位
delay(1);
P0 = 0x00;
P2 = pos_table[5]; // 选中第六位数码管
P0 = hr_disp[1]; // 显示时十位
delay(1);
P0 = 0x00;
// 中断响应时间检测,用于按键消抖
cnt_500ms++;
if (cnt_500ms >= 500) {
cnt_500ms = 0;
// 按键检测
check_key();
}
cnt_2s++;
if (cnt_2s >= 2000) {
cnt_2s = 0;
// 计时调整
if (P3_7 == 0) { // K8按下,每2秒加1
sec += 2;
if (sec >= 60) {
sec -= 60;
min++;
if (min >= 60) {
min = 0;
hr++;
if (hr >= 24) {
hr = 0;
}
}
}
}
else if (P3_6 == 0) { // K7按下,每0.5秒加1
sec += 1;
if (sec >= 60) {
sec -= 60;
min++;
if (min >= 60) {
min = 0;
hr++;
if (hr >= 24) {
hr = 0;
}
}
}
delay(500); // 延时500ms,相当于每0.5秒加1
}
}
}
// 按键检测函数
void check_key() {
static uchar key_state = 0xFF; // 按键状态,初始置为高电平
uchar tmp;
tmp = P3 & 0xC0; // 读取K6、K5、K4键
if (tmp != 0xC0) { // 有按键按下
delay(10); // 延时10ms,用于消抖
tmp = P3 & 0xC0; // 再次读取键值
if (tmp != 0xC0) { // 仍有按键按下
if (tmp != key_state) { // 按键状态改变
key_state = tmp;
switch (tmp) {
case 0x80: // K6,调整小时
hr++;
if (hr >= 24) {
hr = 0;
}
break;
case 0x40: // K5,清零小时
hr = 0;
break;
case 0x20: // K4,清零分
min = 0;
break;
}
}
}
}
tmp = P3 & 0x18; // 读取K3、K2键
if (tmp != 0x18) {
delay(10);
tmp = P3 & 0x18;
if (tmp != 0x18) {
if (tmp != key_state) {
key_state = tmp;
switch (tmp) {
case 0x10: // K2,调整分
min++;
if (min >= 60) {
min = 0;
}
break;
case 0x08: // K3,清零秒
sec = 0;
break;
}
}
}
}
tmp = P3 & 0x04; // 读取K1键
if (tmp == 0) { // 按键按下
delay(10);
tmp = P3 & 0x04;
if (tmp == 0) {
sec++;
if (sec >= 60) {
sec = 0;
min++;
if (min >= 60) {
min = 0;
hr++;
if (hr >= 24) {
hr = 0;
}
}
}
}
}
}
// 延时函数,用于按键消抖和数码管扫描
void delay(uint ms) {
uint i, j;
for (i = ms; i > 0; i--) {
for (j = 110; j > 0; j--);
}
}
void main() {
init_timer(); // 初始化定时器
while (1);
}
```
需要注意的是,本程序没有加入RTC时钟芯片,所以在掉电后重新上电时,时间会重置为0。如果需要实现断电记忆功能,可以采用EEPROM存储当前时间,在上电时从EEPROM中读取并初始化计时值。
阅读全文