实现功能:
1、时间和日期的加减调节
2、秒表开始、停止、继续、复位计时
3、闹钟设置
4、整点闹铃
仿真原理图

实现代码
#include <REGX51.H>
//命名LCD引脚
#define RS P3_5 //1602液晶的RS脚接在P3.5口上
#define RW P3_6 //1602液晶的RW脚接在P3.6口上
#define E P3_7 //1602液晶的E脚接在P3.7口上
//按键变量定义
#define key0 P1_0 //模式键,点击可以转化不同的模式
#define key1 P1_1 //开始键,加减时间用的
#define key2 P1_2 //用于转换下一个修改参数
enum key{KEY_no,KEY_mode,KEY_start,KEY_rest};//无按键操作,模式键按下,开始键按下,切换键按下
unsigned key_flag = 0;
//模式变量定义
enum mode{time,cout,alarm,set_time};//时间显示模式,计时模式,设置闹钟模式,调整时间模式
enum dis{start_count,stop_count,dis_data,dis_alarm};//开始计时,停止计时,显示日期
//日期时间变量
bit Runnian_flag = 0;
unsigned int year = 2024;//年计数
unsigned char month = 11;//月计数
unsigned char day = 18;//日计数
unsigned char second = 0;//秒计数
unsigned char minute = 0;//分计数
unsigned char hour = 0;//小时计数
unsigned char count = 0;//计数变量
//延时函数定义
void delay1ms(unsigned int n) //误差 0us ,延时n毫秒
{
unsigned int a,b,c;
for(c=n;c>0;c--)
for(b=199;b>0;b--)
for(a=1;a>0;a--);
}
/LCD相关函数///
//---------------------------------------
//名称:等待1602之前的写操作已经完成
//---------------------------------------
void lcd_wait(void)
{
RS=0; //选择数据状态寄存器
RW=1; //选择读入状态
P0=0xff; //P0口输出高电平,准备输入
while (1) {
E=1; //E高电平使能1602读
if(!P0_7) break; //写操作已经完成
E=0; //读完以后,恢复E的电平
}
E=0; //读完以后,恢复E的电平
}
//---------------------------------------
//名称:1602写命令函数
//---------------------------------------
void lcd_cmd(unsigned char cmd)
{
lcd_wait(); //等待1602空闲
RS=0; //选择指令寄存器
RW=0; //选择写状态
P0=cmd; //将命令字通过P0口送至DB
E=1; //E高电平将命令字写入1602
E=0; //写完以后,恢复E的电平
}
//---------------------------------------
//名称:1602写数据函数
//---------------------------------------
void lcd_data(unsigned char buf)
{
lcd_wait(); //等待1602空闲
RS=1; //选择数据寄存器
RW=0; //选择写状态
P0=buf; //将数据通过P0口送至DB
E=1; //E高电平将命令字写入1602液晶
E=0; //写完以后,恢复E的电平
}
//---------------------------------------
//名称:1602液晶初始化函数
//---------------------------------------
void lcd_init(void)
{
lcd_cmd(0x38); //8位数据总线,两行显示模式,5*7点阵显示
lcd_cmd(0x0C); //显示功能开,无光标
lcd_cmd(0x01); //清屏
lcd_cmd(0x06); //写入新的数据后,光标右移,显示屏不移动
}
void lcd_string(unsigned char x, unsigned char y, char *str)
{
int i = 0;
lcd_cmd(0x80 + x + y*0x40); // 设置起始位置(行和列)
for (i = 0; str[i] != '\0'; i++) {
lcd_data(str[i]); // 逐个字符写入数据
}
}
const unsigned char code HEX[]="0123456789ABCDEF"; //16进制字符表,用于数字到显示字符转换
按键检测函数///
int key_in(void)
{
if(P1 != 0xFF)
{
delay1ms(5);//延时5ms,进行按键消抖
switch(P1)
{
case 0xFE : return KEY_mode; //切换模式键按下
case 0xFD : return KEY_start;//开始键按下
case 0xFB : return KEY_rest; //切换修改参数键按下
default:return KEY_no;
}
}
else
{
return KEY_no;
}
}
主函数/
void main(void)
{
//unsigned char mode_flag;
unsigned char mode_display = 0;//显示模式
bit count_flag = 0;
/按键IO口读模式
P1 = 0xFF;
/lcd初始化
lcd_init();
/时间定时器:定时器0的配置
TMOD |= 0x01;//定时器工作方式1;
//定时10ms
TH0 = (65536 - 10000)/256;
TL0 = (65536 - 10000)%256;
TR0 = 1;//开始计时
ET0 = 1;//允许定时器0产生中断
EA = 1;//打开总中断
/显示闪烁定时器,定时器1的配置
while(1)
{
//设置光标位置为第1行,1列,显示年
lcd_cmd(0x80);
lcd_data(HEX[year/1000]);
lcd_data(HEX[(year/100)%10]);
lcd_data(HEX[(year/10)%10]);
lcd_data(HEX[year%10]);
//设置光标位置为第1行,6列,显示月
lcd_cmd(0x85);
lcd_data(HEX[month/10]);
lcd_data(HEX[month%10]);
//设置光标位置为第1行,6列,显示月
lcd_cmd(0x88);
lcd_data(HEX[day/10]);
lcd_data(HEX[day%10]);
//设置光标位置为第2行,1列,显示小时
lcd_cmd(0xC0);
lcd_data(HEX[hour/10]);
lcd_data(HEX[hour%10]);
lcd_data(':');
lcd_data(HEX[minute/10]);
lcd_data(HEX[minute%10]);
lcd_data(':');
lcd_data(HEX[second/10]);
lcd_data(HEX[second%10]);
}
}
//定时器0中断服务函数
void T0_ISR(void) interrupt 1
{
//重装定时器0的初始值
TH0 = (65536 - 10000)/256;
TL0 = (65536 - 10000)%256;
//对计数变量进行赋值
if(count==99)
{
count = 0;
if(second == 59)
{
second = 0;
if(minute == 59)
{
minute = 0;
if(hour == 23)
{
hour = 0;
if(Runnian_flag)
{
if((day == 28)&&(month==2))//闰年闰月
{
day = 1;
month++;
}
}
else
{
if((day == 29)&&(month==2))
{
day = 1;
month++;
}
}
if((day == 30)&&((month==2)|(month==4)|(month==6)|(month==9)|(month==11)))
{
day = 1;
month++;
}
else if(day == 31)
{
day = 1;
if(month == 12)
{
month = 1;
year++;
Runnian_flag = ((year%4==0) && (year%100!=0)) || (year%4000);//计数是否是闰年
}
else
{
month++;
}
}
}
else
{
hour++;
}
}
else
{
minute++;
}
}
else
{
second++;
}
}
else
{
count = 0;
}
}