(一)功能:
从默认时间7:00:00(24小时制)开始显示时间;使用4*4矩阵键盘(每个键功能如图1)可以设置当前时间、闹铃时间以及当前日期,数码管实时显示设置数据,设置完成后数码管自动显示当前时间并继续运行;如果当前时间与闹铃时间相同,蜂鸣器触发持续报警,按下取消闹铃键,蜂鸣器停止报警,按下显示日期按钮,数码管显示当前日期以及日期对应星期几。

图1 矩阵键盘具体功能
(二)技术指标:
晶体数码管的显示,矩阵键盘与晶体数码管的交互,蜂鸣器的鸣叫,时间与秒数的换算,日期对应星期的计算。
- 设计方案
(一)需要使用的外部硬件:
因51单片机开发箱已含有题目所需的晶体数码管、蜂鸣器和矩阵键盘,从方便开发、节省时间方面考虑,本小组采用51单片机开发箱。
(二)编程部分
本小组使用C语言对单片机编程,开发环境选择Keil uVision4软件,程序下载软件使用普中科技单片机程序下载软件V1.8.2。
- 原理框图

图2 模块相互联系
- 电路连接
P0接数码管段选J12,P1接数码管位选J16,P2^0至P2^6接静态数码管段选JP3,P2^7接蜂鸣器J8,P3接矩阵键盘JP4。
代码部分:
/********************************************************
E题 数码电子表
制作小组成员:宋xy 2017*******8
石yf 2017*******0
********************************************************/
//**********************头文件定义*************************
#include <reg52.h>
//*********************************************************
//********************全局变量定义*************************
//此表为8个数码管位选控制, 共阴数码管 1-8个
unsigned char Disp_Bit[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdF,0xbF,0x7F}; //位选控制 查表的方法控制
//此表为 LED 的字模, 共阴数码管 "0-9" " - "
unsigned char Disp_Code[11] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40}; //共阴数码管段码控制
unsigned long Second=25200;//定义初始时间(7A.M)
unsigned int AlarmSecond=50;//定义闹钟时间
unsigned char Int_Count;
unsigned char temp[8]; //显示时间函数中储存时间的每一位
int i,j; //万用计数变量
unsigned char KEYValue; //按键函数返回的键值
unsigned char KEYValues[15]; //用于储存设置时间,日期的每一位
unsigned char AlarmValue[11];//用于储存设置闹铃时间的每一位
unsigned char Alarm_Flag=0; //闹铃标志位(0为关闭1为打开)
unsigned int Year=2018; //定义初始年份
unsigned char Month=1; //定义初始月份
unsigned char Day=1; //定义初始日期
unsigned int week; //定义星期
unsigned int id[17]={2,1,0,1,2,3,2,1,10,2,1,1,1,3,1,1,0}; //储存作者学号
//*********************************************************
//********************位定义*************************
sbit SPK=P2^7; //声明蜂鸣器端口
//***************************************************
//********************子函数定义*************************
void DisplayTime(); //声明显示时间子函数
void DisplayDate(); //声明显示时间子函数
unsigned int SettingTime();//声明设置时间子函数
unsigned int KeyDown(); //声明按键子函数
unsigned int AlarmSetting();//声明闹钟设置子函数
unsigned int SettingDate();//声明设置日期子函数
void delay(unsigned int i); //声明延时子函数
void Timer_Ini(void); //声明计时器子函数
void Ring(void); //声明蜂鸣器蜂鸣子函数
void SettingTemDisplay(); //声明设置时间零时显示函数
void AlarmTemDisplay(); //声明设置闹铃零时显示函数
void DateTemDisplay(); //声明设置日期零时显示函数
void DisplayID(); //声明显示作者QQ函数
void iWeek(); //声明根据日期计算星期函数
//*******************************************************
void main()
{
Timer_Ini();
while(1)
{
if(Int_Count>=20) //判断秒数增加
{
Second++; //秒数加一
Int_Count = 0; //重置计数
}
KeyDown();
if(KEYValue==10)
{
SettingTime(); //进行时间设置操作
}
if(KEYValue==11)
{
AlarmSetting(); //进行闹钟设置操作
}
if(KEYValue==12)
{
Alarm_Flag = 0; //使得闹铃标志位为0,蜂鸣器停止鸣叫
}
if(KEYValue==13)
{
SettingDate(); //设置日期
}
if(KEYValue==14)
{
DisplayDate(); //显示日期
}
if(KEYValue==15)
{
DisplayID(); //显示作者QQ
}
if(Second==AlarmSecond)
{
Alarm_Flag = 1;
}
if(Alarm_Flag==1)
{
Ring();
}
DisplayTime();
if((Second/3600/10)==2&&(Second/3600%10==4))//判断是否迎来新的一天
{
if((Second%3600/60/10==0)&&(Second%3600/60%10==0))
{
if((Second%60/10==0)&&(Second%60%10==0))
{
Day++;
Second = 0;
}
}
}
}
}
//*******显示时间子函数************//
void DisplayTime()
{
temp[0] = Second/3600/10; // 小时1
temp[1] = Second/3600%10; // 小时2
temp[2] = 10; // -
temp[3] = Second%3600/60/10; // 分钟1
temp[4] = Second%3600/60%10; // 分钟2
temp[5] = 10; // -
temp[6] = Second%60/10; // 秒钟1
temp[7] = Second%60%10; // 秒钟2
for(i=0;i<8;i++)
{
P1 = Disp_Bit[i]; //第i个数码管位选 1011 1111 0xbf
P0 = Disp_Code[temp[i]]; //第i个数码管的编码
delay(2);
}
}
//*******时间设置零时显示子函数(用于设置时实时显示)************//
void SettingTemDisplay()
{
temp[0] = KEYValues[0]; // 小时1
temp[1] = KEYValues[2]; // 小时2
temp[2] = 10; // -
temp[3] = KEYValues[4]; // 分钟1
temp[4] = KEYValues[6]; // 分钟2
temp[5] = 10; // -
temp[6] = KEYValues[8]; // 秒钟1
temp[7] = KEYValues[10]; // 秒钟2
for(j=0;j<8;j++)
{
P1 = Disp_Bit[j]; //第i个数码管位选 1011 1111 0xbf
P0 = Disp_Code[temp[j]]; //第i个数码管的编码
delay(2);
}
}
//*******显示日期子函数************//
void DisplayDate()
{
iWeek();
while(1)
{
temp[0] = Year/1000; // 年份1
temp[1] = Year%1000/100; // 年份2
temp[2] = Year%100/10; // 年份3
temp[3] = Year%10; // 年份4
temp[4] = Month/10; // 月份1
temp[5] = Month%10; // 月份2
temp[6] = Day/10; // 日期1
temp[7] = Day%10; // 日期2
for(i=0;i<8;i++)
{
P1 = Disp_Bit[i]; //第i个数码管位选 1011 1111 0xbf
P0 = Disp_Code[temp[i]]; //第i个数码管的编码
delay(2);
}
P2 = ~Disp_Code[week]; //共阳数码管显示星期
if(P3!=0x0f)//读取按键是否按下
{
break;
}
}
}
//*******闹钟设置零时显示子函数(用于设置时实时显示)************//
void AlarmTemDisplay()
{
temp[0] = AlarmValue[0]; // 小时1
temp[1] = AlarmValue[2]; // 小时2
temp[2] = 10; // -
temp[3] = AlarmValue[4]; // 分钟1
temp[4] = AlarmValue[6]; // 分钟2
temp[5] = 10; // -
temp[6] = AlarmValue[8]; // 秒钟1
temp[7] = AlarmValue[10]; // 秒钟2
for(j=0;j<8;j++)
{
P1 = Disp_Bit[j]; //第i个数码管位选 1011 1111 0xbf
P0 = Disp_Code[temp[j]]; //第i个数码管的编码
delay(2);
}
}
//*******日期设置零时显示子函数(用于设置时实时显示)************//
void DateTemDisplay()
{
temp[0] = KEYValues[0]; // 年份1
temp[1] = KEYValues[2]; // 年份2
temp[2] = KEYValues[4]; // 年份3
temp[3] = KEYValues[6]; // 年份4
temp[4] = KEYValues[8]; // 月份1
temp[5] = KEYValues[10]; // 月份2
temp[6] = KEYValues[12]; // 日期1
temp[7] = KEYValues[14]; // 日期2
for(j=0;j<8;j++)
{
P1 = Disp_Bit[j]; //第i个数码管位选 1011 1111 0xbf
P0 = Disp_Code[temp[j]]; //第i个数码管的编码
delay(2);
}
}
//*******设置时间子函数************//
unsigned int SettingTime()
{
i=-1,j;
while(1)
{
SettingTemDisplay();
if(P3!=0x0f)//读取按键是否按下
{
KEYValue = KeyDown();
KEYValues[i]=KEYValue;
i++;
}
if(i==11)
{
break;
}
}
Second=KEYValues[0]*36000+KEYValues[2]*3600+KEYValues[4]*600+KEYValues[6]*60+KEYValues[8]*10+KEYValues[10];
return(Second);
}
//*******闹铃设置子函数************//
unsigned int AlarmSetting()
{
i=-1;
while(1)
{
AlarmTemDisplay();
if(P3!=0x0f)//读取按键是否按下
{
KEYValue = KeyDown();
AlarmValue[i]=KEYValue;
i++;
}
if(i==11)
{
break;
}
}
AlarmSecond=AlarmValue[0]*36000+AlarmValue[2]*3600+AlarmValue[4]*600+AlarmValue[6]*60+AlarmValue[8]*10+AlarmValue[10];
return(AlarmSecond);
}
//*******设置日期子函数************
unsigned int SettingDate()
{
i=-1;
/* for(i=0;i<15;i++)//清空KeyValues
{
KEYValues[i]=0;
}*/
while(1)
{
DateTemDisplay();
if(P3!=0x0f)//读取按键是否按下
{
KEYValue = KeyDown();
KEYValues[i]=KEYValue;
i++;
}
if(i==15)
{
break;
}
}
Year = KEYValues[0]*1000+KEYValues[2]*100+KEYValues[4]*10+KEYValues[6];
Month = KEYValues[8]*10+KEYValues[10];
Day = KEYValues[12]*10+KEYValues[14];
return(Year);
}
//
//*******按键子函数************//
unsigned int KeyDown()
{
char a=0;
P3=0x0f;
if(P3!=0x0f)//读取按键是否按下
{
delay(10);//延时10ms进行消抖
if(P3!=0x0f)//再次检测键盘是否按下
{
//测试列
P3=0X0F;
switch(P3)
{
case(0X07): KEYValue=0;break;
case(0X0b): KEYValue=1;break;
case(0X0d): KEYValue=2;break;
case(0X0e): KEYValue=3;break;
}
//测试行
P3=0XF0;
switch(P3)
{
case(0X70): KEYValue=KEYValue;break;
case(0Xb0): KEYValue=KEYValue+4;break;
case(0Xd0): KEYValue=KEYValue+8;break;
case(0Xe0): KEYValue=KEYValue+12;break;
}
while((a<50)&&(P3!=0xf0)) //检测按键松手检测
{
delay(10);
a++;
}
}
}
return(KEYValue);
}
void miaobiao(void) interrupt 3
{
TH1 = 0x3C; // (65536 - X)*1us = 50000us x = 15536
TL1 = 0xB0;
Int_Count++; // 50ms +1
}
//*******自定义延时子函数************//
void delay(unsigned int i)
{
unsigned int j;
for(i; i > 0; i--) //循环i*j次 机器在这里执行需要一段时间 也就达到了延时效果
for(j = 100; j > 0; j--);
}
void Timer_Ini(void)
{
TMOD = 0x10;
TH1 = 0x3C; // (65536 - X)*1us = 50000us x = 15536
TL1 = 0xB0;
EA = 1;
ET1 = 1;
TR1 = 1;
}
void Ring(void)
{
SPK = ~SPK;
delay(2);
}
//*******显示作者学号************//
void DisplayID()
{
while(1)
{
temp[0] = id[0];
temp[1] = id[1];
temp[2] = id[2];
temp[3] = id[3];
temp[4] = id[4];
temp[5] = id[5];
temp[6] = id[6];
temp[7] = id[7];
for(i=0;i<8;i++)
{
P1 = Disp_Bit[i]; //第i个数码管位选 1011 1111 0xbf
P0 = Disp_Code[temp[i]]; //第i个数码管的编码
delay(2);
}
if(P3!=0x0f)//读取按键是否按下
{
break;
}
if(Int_Count>=25) //判断秒数增加
{
for(i=0;i<17;i++)
{
id[i]=id[i+1];
}
Int_Count = 0; //重置计数
}
}
}
//*******根据日期计算星期************//
void iWeek()
{
int c,s;
if (Month==1) c=Day;
else if (Month==2) c=31+Day;
else if (Month==3) c=31+28+Day;
else if (Month==4) c=31+28+31+Day;
else if (Month==5) c=31+28+31+30+Day;
else if (Month==6) c=31+28+31+30+31+Day;
else if (Month==7) c=31+28+31+30+31+30+Day;
else if (Month==8) c=31+28+31+30+31+30+31+Day;
else if (Month==9) c=31+28+31+30+31+30+31+31+Day;
else if (Month==10) c=31+28+31+30+31+30+31+31+30+Day;
else if (Month==11) c=31+28+31+30+31+30+31+31+30+31+Day;
else if (Month==12) c=31+28+31+30+31+30+31+31+30+31+30+Day;
if (Month>2)
{if (Year%100==0) {if (Year%400==0) c=c+1;}
else if (Year%4==0) c=c+1;}
s=(Year-1)*365+(Year-1)/4-(Year-1)/100+(Year-1)/400+c+1;
week=s%7;
if(week==0) week=7;
}