51单片机数码管时钟项目笔记
项目概述
基于51单片机(STC89C52)的数码管显示时钟,具备时间显示、时钟设置、闹钟设置等功能。
头文件声明
#include <REGX52.H>
#include "Key.h"
#include "Seg.h"
全局变量定义
时间相关变量
unsigned char Clock_Disp[3] = {23,59,55}; // 时钟显示数据 [时,分,秒]
unsigned char Clock_Set[3]; // 时钟设置缓存
unsigned char Clock_Set_Index; // 时钟设置索引 0-时,1-分,2-秒
unsigned int Timer_1000ms; // 1000ms计时器
闹钟相关变量
unsigned char Alarm[] = {0,0,0}; // 闹钟时间 [时,分,秒]
unsigned char Alarm_Set[3]; // 闹钟设置缓存
bit Alarm_Flag = 1; // 闹钟开关标志(1-开,0-关)
bit Alarm_Enable_Flag; // 闹钟使能标志
显示相关变量
unsigned char Seg_Buf[6] = {10,10,10,10,10,10}; // 数码管显示缓存
unsigned char Seg_Point[6] = {0,1,0,1,0,1}; // 小数点控制
unsigned char Seg_Disp_Mode; // 显示模式 0-时钟,1-时钟设置,2-闹钟
unsigned char Seg_Pos; // 数码管扫描位置
bit Seg_Flag; // 闪烁标志
unsigned char Led; // LED显示数据
按键相关变量
unsigned char Key_Slow_Down; // 按键减速(10ms)
unsigned int Seg_Slow_Down; // 数码管减速(500ms)
unsigned char Key_Val, Key_Down, Key_Old; // 按键扫描变量
核心功能函数
1. 按键处理函数
void Key_Proc()
{
if(Key_Slow_Down) return; // 10ms减速控制
Key_Slow_Down = 1;
// 读取按键值并检测按键按下
Key_Val = Key_read();
Key_Down = Key_Val & (Key_Val ^ Key_Old);
Key_Old = Key_Val;
if(Key_Down != 0) {
Alarm_Enable_Flag = 0; // 任何按键按下都关闭闹钟
}
switch(Key_Down) {
case 1: // 进入时钟设置模式
Clock_Set_Index = 0;
Clock_Set[0] = Clock_Disp[0];
Clock_Set[1] = Clock_Disp[1];
Clock_Set[2] = Clock_Disp[2];
Seg_Disp_Mode = 1;
break;
case 2: // 进入闹钟设置模式
Clock_Set_Index = 0;
Alarm_Set[0] = Alarm[0];
Alarm_Set[1] = Alarm[1];
Alarm_Set[2] = Alarm[2];
Seg_Disp_Mode = 2;
break;
case 3: // 切换设置项(时→分→秒)
Clock_Set_Index++;
if(Clock_Set_Index == 3) Clock_Set_Index = 0;
break;
case 4: // 闹钟开关切换
Alarm_Flag ^= 1;
break;
case 5: // 加操作
if(Seg_Disp_Mode == 1) { // 时钟设置
Clock_Set[Clock_Set_Index]++;
if(Clock_Set[Clock_Set_Index] == (Clock_Set_Index==0?24:60))
Clock_Set[Clock_Set_Index] = 0;
}
if(Seg_Disp_Mode == 2) { // 闹钟设置
Alarm_Set[Clock_Set_Index]++;
if(Alarm_Set[Clock_Set_Index] == (Clock_Set_Index==0?24:60))
Alarm_Set[Clock_Set_Index] = 0;
}
break;
case 6: // 减操作
if(Seg_Disp_Mode == 1) {
Clock_Set[Clock_Set_Index]--;
if(Clock_Set[Clock_Set_Index] == 255)
Clock_Set[Clock_Set_Index] = Clock_Set_Index==0?23:59;
}
if(Seg_Disp_Mode == 2) {
Alarm_Set[Clock_Set_Index]--;
if(Alarm_Set[Clock_Set_Index] == 255)
Alarm_Set[Clock_Set_Index] = Clock_Set_Index==0?23:59;
}
break;
case 7: // 保存并返回
if(Seg_Disp_Mode == 1) {
Clock_Disp[0] = Clock_Set[0];
Clock_Disp[1] = Clock_Set[1];
Clock_Disp[2] = Clock_Set[2];
}
if(Seg_Disp_Mode == 2) {
Alarm[0] = Alarm_Set[0];
Alarm[1] = Alarm_Set[1];
Alarm[2] = Alarm_Set[2];
}
Seg_Disp_Mode = 0;
break;
case 8: // 取消操作
Seg_Disp_Mode = 0;
break;
}
}
按键功能说明:
- 键1:进入时钟设置
- 键2:进入闹钟设置
- 键3:切换设置项
- 键4:闹钟开关
- 键5:数值加
- 键6:数值减
- 键7:保存返回
- 键8:取消返回
2. 数码管信息处理
void Seg_Proc()
{
unsigned char i;
if(Seg_Slow_Down) return; // 500ms减速控制
Seg_Slow_Down = 1;
switch(Seg_Disp_Mode) {
case 0: // 正常时钟显示
for(i=0; i<3; i++) {
Seg_Buf[0+i*2] = Clock_Disp[i] / 10 % 10;
Seg_Buf[1+i*2] = Clock_Disp[i] % 10;
}
break;
case 1: // 时钟设置界面(带闪烁效果)
Seg_Buf[0] = Clock_Set[0] / 10 % 10;
Seg_Buf[1] = Clock_Set[0] % 10;
Seg_Buf[2] = Clock_Set[1] / 10 % 10;
Seg_Buf[3] = Clock_Set[1] % 10;
Seg_Buf[4] = Clock_Set[2] / 10 % 10;
Seg_Buf[5] = Clock_Set[2] % 10;
// 当前设置项闪烁显示
Seg_Buf[0+Clock_Set_Index*2] = Seg_Flag ? Clock_Set[Clock_Set_Index] / 10 % 10 : 10;
Seg_Buf[1+Clock_Set_Index*2] = Seg_Flag ? Clock_Set[Clock_Set_Index] % 10 : 10;
break;
case 2: // 闹钟设置界面
Seg_Buf[0] = Alarm_Set[0] / 10 % 10;
Seg_Buf[1] = Alarm_Set[0] % 10;
Seg_Buf[2] = Alarm_Set[1] / 10 % 10;
Seg_Buf[3] = Alarm_Set[1] % 10;
Seg_Buf[4] = Alarm_Set[2] / 10 % 10;
Seg_Buf[5] = Alarm_Set[2] % 10;
Seg_Buf[0+Clock_Set_Index*2] = Seg_Flag ? Alarm_Set[Clock_Set_Index] / 10 % 10 : 10;
Seg_Buf[1+Clock_Set_Index*2] = Seg_Flag ? Alarm_Set[Clock_Set_Index] % 10 : 10;
break;
}
}
显示模式说明:
- 模式0:正常显示时间,格式为 HH:MM:SS
- 模式1:时钟设置,当前设置项会闪烁
- 模式2:闹钟设置,当前设置项会闪烁
3. LED显示处理
void Led_Proc()
{
if(Alarm_Flag == 1) { // 闹钟功能开启
// 检测是否到达闹钟时间
if(Clock_Disp[0] == Alarm[0] && Clock_Disp[1] == Alarm[1] && Clock_Disp[2] == Alarm[2])
Alarm_Enable_Flag = 1;
if(Alarm_Enable_Flag == 1) {
P2_3 = 0; // 使能LED显示
P1 = Led; // 输出LED数据
} else {
P2_3 = 1; // 关闭LED显示
P1 = 0xff;
}
} else {
P2_3 = 1; // 闹钟关闭时关闭LED
P1 = 0xff;
}
}
4. 定时器初始化
void Timer0_Init(void) // 1毫秒@12.000MHz
{
TMOD &= 0xF0; // 设置定时器模式
TMOD |= 0x01; // 定时器0工作在模式1
TL0 = 0x18; // 设置定时初始值
TH0 = 0xFC; // 设置定时初始值
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时
ET0 = 1; // 开启定时器0中断
EA = 1; // 开启总中断
}
定时器配置:
- 工作模式:模式1(16位定时器)
- 定时时长:1ms
- 晶振频率:12MHz
5. 定时器中断服务函数
void Timer0Sever() interrupt 1
{
TL0 = 0x18; // 重装定时器初值
TH0 = 0xFC;
// 减速控制
if(++Key_Slow_Down == 10) Key_Slow_Down = 0; // 10ms
if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0; // 500ms
// 数码管扫描显示
if(++Seg_Pos == 6) Seg_Pos = 0;
Seg_Disp(Seg_Pos, Seg_Buf[Seg_Pos], Seg_Point[Seg_Pos]);
// 时钟计时
Timer_1000ms++;
if(Timer_1000ms == 1000) {
Timer_1000ms = 0;
Clock_Disp[2]++; // 秒加1
// 时间进位处理
if(Clock_Disp[2] == 60) {
Clock_Disp[2] = 0;
Clock_Disp[1]++;
if(Clock_Disp[1] == 60) {
Clock_Disp[1] = 0;
Clock_Disp[0]++;
if(Clock_Disp[0] == 24)
Clock_Disp[0] = 0;
}
}
}
// 闪烁控制和LED效果
Timer_500ms++;
if(Timer_500ms == 500) {
Timer_500ms = 0;
Seg_Flag ^= 1; // 切换闪烁状态
// LED显示效果(上午/下午不同效果)
if(Clock_Disp[0] >= 12) {
Led ^= 0xf0; // 下午:高4位闪烁
} else {
Led ^= 0x0f; // 上午:低4位闪烁
}
}
}
中断功能:
- 按键扫描减速(10ms)
- 数码管刷新减速(500ms)
- 数码管动态扫描
- 时钟计时逻辑
- 设置项闪烁控制
- LED显示效果控制
6. 主函数
void main()
{
Timer0_Init(); // 初始化定时器
while(1) {
Key_Proc(); // 按键处理
Seg_Proc(); // 数码管数据处理
Led_Proc(); // LED显示处理
}
}
外设驱动代码
数码管显示驱动 (Seg.h)
// 数码管段码表(0-9,灭)
unsigned char code Seg_Dula[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};
// 数码管位选表(6位数码管)
unsigned char code Seg_Wela[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
void Seg_Disp(unsigned char wela, dula, point)
{
P0 = 0x00; // 消影处理
P2_6 = 1; P2_6 = 0; // 锁存器控制
P0 = Seg_Wela[wela]; // 位选
P2_7 = 1; P2_7 = 0;
if(point == 1)
P0 = Seg_Dula[dula] | 0x80; // 带小数点
else
P0 = Seg_Dula[dula]; // 无小数点
P2_6 = 1; P2_6 = 0; // 输出显示
}
数码管控制引脚:
- P2_6:段码锁存
- P2_7:位选锁存
- P0:数据端口
按键扫描驱动 (Key.h)
unsigned char Key_read()
{
unsigned char temp = 0;
// 矩阵键盘扫描
P3_0 = 0; P3_1 = 1; P3_2 = 1; P3_3 = 1;
if(P3_4 == 0) temp = 1; if(P3_5 == 0) temp = 2;
if(P3_6 == 0) temp = 3; if(P3_7 == 0) temp = 4;
P3_0 = 1; P3_1 = 0; P3_2 = 1; P3_3 = 1;
if(P3_4 == 0) temp = 5; if(P3_5 == 0) temp = 6;
if(P3_6 == 0) temp = 7; if(P3_7 == 0) temp = 8;
P3_0 = 1; P3_1 = 1; P3_2 = 0; P3_3 = 1;
if(P3_4 == 0) temp = 9; if(P3_5 == 0) temp = 10;
if(P3_6 == 0) temp = 11; if(P3_7 == 0) temp = 12;
P3_0 = 1; P3_1 = 1; P3_2 = 1; P3_3 = 0;
if(P3_4 == 0) temp = 13; if(P3_5 == 0) temp = 14;
if(P3_6 == 0) temp = 15; if(P3_7 == 0) temp = 16;
return temp;
}
按键矩阵:
- 4×4矩阵键盘,使用P3端口
- 返回键值1-16对应不同功能