51单片机应用----时钟

呼延震博
2023-12-01

51单片机应用----时钟

通过从矩阵按键中分离出来的独立按键,蜂鸣器,1602液晶屏,计时器,AT24C02的控制实现时钟。



前言

总结近期51单片机的学习


二、代码讲解

1.头文件

代码如下(示例):

#include<reg52.h>                   //52单片机头文件 
#define uchar unsigned char
#define uint unsigned int
#include"24c02.h"
sbit dula=P2^6;                     //定义锁存器 
sbit wela=P2^7;
sbit rs=P3^5;                       //1602液晶rs端 
sbit lcden=P3^4;                    //lcden端 
sbit s1=P3^0;                       //功能键 
sbit s2=P3^1;                       //增大键 
sbit s3=P3^2;                       //减小键 
sbit rd=P3^7;                       //矩阵第四列 
sbit beep=P2^3;                     //蜂鸣器 
uchar count ,s1num;
char miao,shi,fen;
uchar code table[]="  2023-3-17 FRI"; //日期 

这里是对一些接口的定义,数据的定义以及日期。


2.延时函数

代码如下(示例):


void yanshi(uint max)               //延时函数 
{
		int i,j;
		for(i=max;i>0;i--)
			for(j=110;j>0;j--);
}

对部分操作之间进行延时,以增加程序的稳定性


3.蜂鸣器函数

void di()                            //蜂鸣器函数 
{
		beep=0;
		yanshi(100);
		beep=1;
}

4.液晶屏写命令函数

void write_com(uchar com)           //液晶屏写命令函数 
{
		rs=0;
		lcden=0;                    //选择写命令模式 
		P0=com;                     //将要写的命令送到命令总线上 
		yanshi(5);                  //延时稳定 
		lcden=1;                    //给lcden高脉冲将命令送进液晶控制器 
		yanshi(5);
		lcden=0;
}

此函数主要对液晶屏进行控制。


5.液晶屏数据的写入函数

void write_date(uchar date)         //液晶写数据函数 
{
		rs=1;
		lcden=0;                    //选择模式 
		P0=date;                    //将数据送到数据总线 
		yanshi(5);                  //延时稳定 
		lcden=1;                    //给lcden高脉冲将数据送进液晶控制器 
		yanshi(5);
		lcden=0;
}

此函数主要将数据写入液晶屏。


6.将数据写入液晶屏函数

void write_sfm(uchar add,uchar date)   //写时分秒函数 
{
		uchar shi,ge;
		shi=date/10;                  //分解数据个十位 
		ge=date%10;
		write_com(0x80+0x40+add);     //设置显示位置 
		write_date(0x30+shi);         //十位显示 
		write_date(0x30+ge);          //个位显示 
}

7.初始化函数

void init()                           //初始化函数 
{
		uchar num;
		rd=0;                         //矩阵第四列一端置低,分解独立按键 
		dula=0;                       //关闭锁存器,防止液晶屏乱码 
		wela=0;
		lcden=0;
		fen=0;                         //初始数据 
		miao=0;
		shi=0;
		count=0;
		s1num=0;
		init_24c02();
		write_com(0x38);                //设置16*2显示,5*7点阵,8位数据接口 
		write_com(0x0c);                //设置开显示,不显示光标 
		write_com(0x06);                //写一个字符后,地址指针+1 
		write_com(0x01);                //显示清零,数据指针清零 
		write_com(0x80);
		for(num=0;num<15;num++)
		{
				write_date(table[num]); //显示年月日 
				yanshi(5);
		}
		write_com(0x80+0x40+6);         //显示年月日之间的‘:’ 
		write_date(':');
		yanshi(5);
		write_com(0x80+0x40+9);
		write_date(':');
		yanshi(5);
		miao=read_add(1);               //从AT24C02读取数据 
		fen=read_add(2);
		shi=read_add(3);
		write_sfm(10,miao);             //写入数据 
		write_sfm(7,fen);
		write_sfm(4,shi);
		TMOD=0x01;                       //定时器设置 
		TH0=(65536-50000)/256;
		TL0=(65536-50000)%256;
		EA=1;                            //开总中断 
		ET0=1;                           //开定时器0中断 
		TR0=1;                           //启动定时器0 
}

此函数主要用于实验板刚上电,将变量归零,显示日期,读入上次时钟数据,计时器和中断器的启动,和防止出现乱码的现象。


8.按键扫描函数

void keyscan()                            //按键扫描函数 
{
		if(s1==0)                         //功能键被按下 
		{
				yanshi(5);                //防止抖动 
				if(s1==0)                 //功能键确定被按下 
				{
						s1num++;
						while(!s1);       //等待功能键松开 
						di();
						if(s1num==1)
						{
								TR0=0;    //定时器关闭 
								write_com(0x80+0x40+10);//光标定位到秒的十位 
								write_com(0x0f);        //光标闪烁 
						}
						if(s1num==2)
						{
								write_com(0x80+0x40+7);//光标定位到分钟的十位 
						}
						if(s1num==3)
						{
								write_com(0x80+0x40+4);//光标定位到小时的十位 
						}
						if(s1num==4)
						{
								s1num=0;               //初始功能键计数 
								write_com(0x0c);       //取消光标闪烁 
								TR0=1;				   //定时器开启 
						}
				}
		}
		if(s1num!=0)                                    //秒增加 
		{
				if(s2==0)                               //增加键按下 
				{
						yanshi(5);                      //防止抖动 
						if(s2==0)
						{
								while(!s2);             //增加键释放 
								di();
								if(s1num==1)
								{
										miao++;          
										if(miao==60)
												miao=0;
										write_sfm(10,miao);  //输出秒到液晶屏 
										write_com(0x80+0x40+10); //回调显示位置 
										write_add(1,miao);       //将数据存入AT24C02 
								}
								if(s1num==2)                  //分增加 
									{
										fen++;
										if(fen==60)
										fen=0;
										write_sfm(7,fen);     //输入分到液晶屏 
										write_com(0x80+0x40+7);//回调显示位置 
										write_add(2,fen);     //将数据存入AT24C02 
									}
								if(s1num==3)                 //时增加 
									{
											shi++;
											if(shi==24)
											shi=0;
											write_sfm(4,shi);//输入时到液晶屏 
											write_com(0x80+0x40+4);//回调显示位置 
											write_add(3,shi);  //将数据存到AT24C02 
								  }
							}
					}
								if(s3==0)                //减小键被按下 
								{
										yanshi(5);
										if(s3==0)        //确认减小键被按下 
										{
												while(!s3); //等待减小键松开 
												di();
												if(s1num==1)//秒减小 
												{
														miao--;
														if(miao==-1)
																miao=59;
														write_sfm(10,miao);  //输入秒到显示屏 
														write_com(0x80+0x40+10);//回调显示位置 
														write_add(1,miao);//将数据存入AT24C02 
										    }
										if(s1num==2)//分减小 
										{
												fen--;
												if(fen==-1)
												fen=59;
												write_sfm(7,fen);      //输入分到显示屏  
												write_com(0x80+0x40+7);//回调显示位置 
												write_add(2,fen);//将数据存入AT24C02 
										}
										if(s1num==3)                   //时减小 
										{
												shi--;
												if(shi==-1)
												shi=23;
												write_sfm(4,shi);      //输入到显示屏 
												write_com(0x80+0x40+4);//回调显示位置 
												write_add(3,shi);//将数据存入AT24C02 
										}
								}
						}
				}
		}

此函数赋予51单片机时钟大量的功能,主要有功能键,增加键,减少键的设置,和每次调整时的光标闪动和数据的实时保存。


9.主函数

void main()
{
		init();
		while(1)
		{
				keyscan();//按键持续扫描 
		}
}

使用while循环使键盘一直处于扫描的状态。


10.定时器0的中断

void timer0() interrupt 1                       //定时器 
{
		TH0=(65536-50000)/256;
		TL0=(65536-50000)%256;
		count++;
		if(count==20)         //当count=20,为一秒 
		{
				count=0;
				miao++;
				if(miao==60)                //进制转换 
				{
						miao=0;
						fen++;
						if(fen==60)
						{
								fen=0;
								shi++;
								if(shi==24)
								{
										shi=0;
								}
								write_sfm(4,shi);  //将数据写入显示屏 
								write_add(3,shi);  //将数据存入AT24C02 
						}
						write_sfm(7,fen);
						write_add(2,fen);
				}
				write_sfm(10,miao);
				write_add(1,miao);
		}
}

用于时间的变换和实时的数据处理。


11.AT24C02应用函数

bit write=0;                 //AT24C02的标志 
sbit sda=P2^0;
sbit scl=P2^1;
void yanshi0(){ ;; }
void start()//启动信号 
{
		sda=1;
		yanshi0();
		scl=1;
		yanshi0();
		sda=0;
		yanshi0();
}

void stop()//停止 
{
		sda=0;
		yanshi0();
		scl=1;
		yanshi0();
		sda=1;
		yanshi0();
}

void respons()//应答 
{
		uchar i;
		scl=1;
		yanshi0();
		while((sda==1)&&(i<250))
				i++;
		scl=0;
		yanshi0();
}

void init_24c02()//总线初始化 
{
		sda=1;
		yanshi0();
		scl=1;
		yanshi0();
}

void write_byte(uchar date)//写1字节 
{
		uchar i,temp;
		temp=date;
		for(i=0;i<8;i++)
		{
				temp=temp<<1;
				scl=0;
				yanshi0();
				sda=CY;
				yanshi0();
				scl=1;
				yanshi0();
		}
		scl=0;
		yanshi0();
		sda=1;
		yanshi0();
}

uchar read_byte()//读1字节 
{
		uchar i,k;
		scl=0;
		yanshi0();
		sda=1;
		yanshi0();
		for(i=0;i<8;i++)
		{
				scl=1;
				yanshi0();
				k=(k<<1)|sda;
				scl=0;
				yanshi0();
		}
		return k;
}

void write_add(uchar address,uchar date)//指定地址写1字节 
{
		start();
		write_byte(0xa0);
		respons();
		write_byte(address);
		respons();
		write_byte(date);
		respons();
		stop();
}

char read_add(uchar address)//指定地址读1字节 
{
		uchar date;
		start();
		write_byte(0xa0);
		respons();
		write_byte(address);
		respons();
		start();
		write_byte(0xa1);
		respons();
		date=read_byte();
		stop();
		return date;
}

主要使用数据的实时传输已达到数据存储的目的。


全部代码

#include<reg52.h>                   //52单片机头文件 
#define uchar unsigned char
#define uint unsigned int
#include"24c02.h"
sbit dula=P2^6;                     //定义锁存器 
sbit wela=P2^7;
sbit rs=P3^5;                       //1602液晶rs端 
sbit lcden=P3^4;                    //lcden端 
sbit s1=P3^0;                       //功能键 
sbit s2=P3^1;                       //增大键 
sbit s3=P3^2;                       //减小键 
sbit rd=P3^7;                       //矩阵第四列 
sbit beep=P2^3;                     //蜂鸣器 
uchar count ,s1num;
char miao,shi,fen;
uchar code table[]="  2023-3-17 FRI"; //日期 
void yanshi(uint max)               //延时函数 
{
		int i,j;
		for(i=max;i>0;i--)
			for(j=110;j>0;j--);
}

void di()                            //蜂鸣器函数 
{
		beep=1;
		yanshi(100);
		beep=1;
}

void write_com(uchar com)           //液晶屏写命令函数 
{
		rs=0;
		lcden=0;                    //选择写命令模式 
		P0=com;                     //将要写的命令送到命令总线上 
		yanshi(5);                  //延时稳定 
		lcden=1;                    //给lcden高脉冲将命令送进液晶控制器 
		yanshi(5);
		lcden=0;
}

void write_date(uchar date)         //液晶写数据函数 
{
		rs=1;
		lcden=0;                    //选择模式 
		P0=date;                    //将数据送到数据总线 
		yanshi(5);                  //延时稳定 
		lcden=1;                    //给lcden高脉冲将数据送进液晶控制器 
		yanshi(5);
		lcden=0;
}

void write_sfm(uchar add,uchar date)   //写时分秒函数 
{
		uchar shi,ge;
		shi=date/10;                  //分解数据个十位 
		ge=date%10;
		write_com(0x80+0x40+add);     //设置显示位置 
		write_date(0x30+shi);         //十位显示 
		write_date(0x30+ge);          //个位显示 
}


void init()                           //初始化函数 
{
		uchar num;
		rd=0;                         //矩阵第四列一端置低,分解独立按键 
		dula=0;                       //关闭锁存器,防止液晶屏乱码 
		wela=0;
		lcden=0;
		fen=0;                         //初始数据 
		miao=0;
		shi=0;
		count=0;
		s1num=0;
		init_24c02();
		write_com(0x38);                //设置16*2显示,5*7点阵,8位数据接口 
		write_com(0x0c);                //设置开显示,不显示光标 
		write_com(0x06);                //写一个字符后,地址指针+1 
		write_com(0x01);                //显示清零,数据指针清零 
		write_com(0x80);
		for(num=0;num<15;num++)
		{
				write_date(table[num]); //显示年月日 
				yanshi(5);
		}
		write_com(0x80+0x40+6);         //显示年月日之间的‘:’ 
		write_date(':');
		yanshi(5);
		write_com(0x80+0x40+9);
		write_date(':');
		yanshi(5);
		miao=read_add(1);               //从AT24C02读取数据 
		fen=read_add(2);
		shi=read_add(3);
		write_sfm(10,miao);             //写入数据 
		write_sfm(7,fen);
		write_sfm(4,shi);
		TMOD=0x01;                       //定时器设置 
		TH0=(65536-50000)/256;
		TL0=(65536-50000)%256;
		EA=1;                            //开总中断 
		ET0=1;                           //开定时器0中断 
		TR0=1;                           //启动定时器0 
}

void keyscan()                            //按键扫描函数 
{
		if(s1==0)                         //功能键被按下 
		{
				yanshi(5);                //防止抖动 
				if(s1==0)                 //功能键确定被按下 
				{
						s1num++;
						while(!s1);       //等待功能键松开 
						di();
						if(s1num==1)
						{
								TR0=0;    //定时器关闭 
								write_com(0x80+0x40+10);//光标定位到秒的十位 
								write_com(0x0f);        //光标闪烁 
						}
						if(s1num==2)
						{
								write_com(0x80+0x40+7);//光标定位到分钟的十位 
						}
						if(s1num==3)
						{
								write_com(0x80+0x40+4);//光标定位到小时的十位 
						}
						if(s1num==4)
						{
								s1num=0;               //初始功能键计数 
								write_com(0x0c);       //取消光标闪烁 
								TR0=1;				   //定时器开启 
						}
				}
		}
		if(s1num!=0)                                    //秒增加 
		{
				if(s2==0)                               //增加键按下 
				{
						yanshi(5);                      //防止抖动 
						if(s2==0)
						{
								while(!s2);             //增加键释放 
								di();
								if(s1num==1)
								{
										miao++;          
										if(miao==60)
												miao=0;
										write_sfm(10,miao);  //输出秒到液晶屏 
										write_com(0x80+0x40+10); //回调显示位置 
										write_add(1,miao);       //将数据存入AT24C02 
								}
								if(s1num==2)                  //分增加 
									{
										fen++;
										if(fen==60)
										fen=0;
										write_sfm(7,fen);     //输入分到液晶屏 
										write_com(0x80+0x40+7);//回调显示位置 
										write_add(2,fen);     //将数据存入AT24C02 
									}
								if(s1num==3)                 //时增加 
									{
											shi++;
											if(shi==24)
											shi=0;
											write_sfm(4,shi);//输入时到液晶屏 
											write_com(0x80+0x40+4);//回调显示位置 
											write_add(3,shi);  //将数据存到AT24C02 
								  }
							}
					}
								if(s3==0)                //减小键被按下 
								{
										yanshi(5);
										if(s3==0)        //确认减小键被按下 
										{
												while(!s3); //等待减小键松开 
												di();
												if(s1num==1)//秒减小 
												{
														miao--;
														if(miao==-1)
																miao=59;
														write_sfm(10,miao);  //输入秒到显示屏 
														write_com(0x80+0x40+10);//回调显示位置 
														write_add(1,miao);//将数据存入AT24C02 
										    }
										if(s1num==2)//分减小 
										{
												fen--;
												if(fen==-1)
												fen=59;
												write_sfm(7,fen);      //输入分到显示屏  
												write_com(0x80+0x40+7);//回调显示位置 
												write_add(2,fen);//将数据存入AT24C02 
										}
										if(s1num==3)                   //时减小 
										{
												shi--;
												if(shi==-1)
												shi=23;
												write_sfm(4,shi);      //输入到显示屏 
												write_com(0x80+0x40+4);//回调显示位置 
												write_add(3,shi);//将数据存入AT24C02 
										}
								}
						}
				}
		}

void main()
{
		init();
		while(1)
		{
				keyscan();//按键持续扫描 
		}
}

void timer0() interrupt 1                       //定时器 
{
		TH0=(65536-50000)/256;
		TL0=(65536-50000)%256;
		count++;
		if(count==20)         //当count=20,为一秒 
		{
				count=0;
				miao++;
				if(miao==60)                //进制转换 
				{
						miao=0;
						fen++;
						if(fen==60)
						{
								fen=0;
								shi++;
								if(shi==24)
								{
										shi=0;
								}
								write_sfm(4,shi);  //将数据写入显示屏 
								write_add(3,shi);  //将数据存入AT24C02 
						}
						write_sfm(7,fen);
						write_add(2,fen);
				}
				write_sfm(10,miao);
				write_add(1,miao);
		}
}

bit write=0;                 //AT24C02的标志 
sbit sda=P2^0;
sbit scl=P2^1;
void yanshi0(){ ;; }
void start()//启动信号 
{
		sda=1;
		yanshi0();
		scl=1;
		yanshi0();
		sda=0;
		yanshi0();
}

void stop()//停止 
{
		sda=0;
		yanshi0();
		scl=1;
		yanshi0();
		sda=1;
		yanshi0();
}

void respons()//应答 
{
		uchar i;
		scl=1;
		yanshi0();
		while((sda==1)&&(i<250))
				i++;
		scl=0;
		yanshi0();
}

void init_24c02()//总线初始化 
{
		sda=1;
		yanshi0();
		scl=1;
		yanshi0();
}

void write_byte(uchar date)//写1字节 
{
		uchar i,temp;
		temp=date;
		for(i=0;i<8;i++)
		{
				temp=temp<<1;
				scl=0;
				yanshi0();
				sda=CY;
				yanshi0();
				scl=1;
				yanshi0();
		}
		scl=0;
		yanshi0();
		sda=1;
		yanshi0();
}

uchar read_byte()//读1字节 
{
		uchar i,k;
		scl=0;
		yanshi0();
		sda=1;
		yanshi0();
		for(i=0;i<8;i++)
		{
				scl=1;
				yanshi0();
				k=(k<<1)|sda;
				scl=0;
				yanshi0();
		}
		return k;
}

void write_add(uchar address,uchar date)//指定地址写1字节 
{
		start();
		write_byte(0xa0);
		respons();
		write_byte(address);
		respons();
		write_byte(date);
		respons();
		stop();
}

char read_add(uchar address)//指定地址读1字节 
{
		uchar date;
		start();
		write_byte(0xa0);
		respons();
		write_byte(address);
		respons();
		start();
		write_byte(0xa1);
		respons();
		date=read_byte();
		stop();
		return date;
}

总结

此代码是我参考51单片机教材书,经过不断的调试,也对程序的功能有了一个详细的理解,也把程序中的注释进行了完善。在编写过程中,有很多的难点,比如在I^2C的部分中,学的还是不是很熟。在调试阶段,经常会出现乱码的现象,不过好在最后都纠正了。

 类似资料: