数字钟的编写(单片机&LCD的应用)

一、利用单片机如何控制LCD

今天的主题是如何用单片机控制LCD编写数字钟,该数字钟具有校时功能,代码较长,我会仔细为大家讲解的。
当然如果大家对数字钟感兴趣的话可以看我的姐妹篇,如何用74LS192芯片编写数字钟~~~
欢迎“戳一戳”
在写如何用单片机控制LCD的时候,首先需要对LCD的初始化,在初始化中我们需要用到以下几个函数,分别是——LCD初始化函数、检查忙标志函数、写命令函数、写数据函数、显示字符串函数、延时函数、主函数等。接下来我会逐个介绍各个函数。

二、单个函数分解

1. 1、LCD_INITIAL——初始化函数

void lcd_initial(void)
{
	RW=0;
	E=0;
	write_command(0x3C);   
	write_command(0x0C);	//写入0x0C:开整体显示,光标关,无闪烁
	write_command(0x06);	//写入0x06:光标右移,写入一个字符后地址指针加1
	write_command(0x01);	//写入0X01:清屏
	write_command(0x80);	//写入0x80:字符输入地址,字符的第一位
	delay(1);	//延时
}

2、WRITE_COMMAND——写命令函数

void write_command(uchar com)
{
	check_busy();
	E=0;	//规定RS和E同时为0时,才可以写入命令
	RS=0;
	RW=0;
	out=com;	//将命令com写入P0口
	E=1;	//写命令时,E应为正脉冲,即正跳变,所以前面先置E=0
	_nop_();	//空操作一个机器周期,等待硬件反应
	E=0;	//E由高电平变为低电平,LCD开始执行命令
	delay(1);
}

3、CHECK_BUSY——检验忙函数

在执行大多数命令时都需要检测LCD是否为空闲,如果为空闲再进行指令写入,否则等待。

void check_busy(void)
{
	uchar dt;
	do
	{
		dt=0xff;	//dt为变量单元,初值为0xff
		E=0;
		RS=0;		//按照读写规定,RS=0,E=1时才可以读忙标志
		RW=1;
		E=1;
		dt=out;		//out为P0口,P0口的状态送入dt中
	}while(dt&0x80);//如果忙标志BF=1,继续循环检测,等待BF=0
		E=0;
}

4、WRITE_DATA——写数据函数

void write_data(uchar dat)
{
	check_busy();	//检测忙标志BF=1则等待,若BF=0,则可以对LCD1602液
	E=0;				//晶显示模块写入
	RS=1;		//按规定写数据时,E应为正脉冲,所以先置E=0
	RW=0;	//按规定RS=1和RW=0时,才可以写入数据
	out=dat;	//将数据dat从P0口输出,即写入LCD1602液晶显示模块
	E=1;	//E产生正跳变
	_nop_();
	E=0;	//E由高电平变为低电平,写数据操作结束
	delay(1);	
}

三、单片机主程序及其仿真实现

关于如何启动LCD的程序已经介绍完毕,下面是单片机控制的主程序,因为子函数较多,所以我们在开头进行了函数的声明,并且定义了一些全局变量,方便使用。

1、主程序

#include <reg51.h>
#include <intrins.h>		//调用_nop_()函数
#define uchar unsigned char 
#define uint unsigned int 
#define out P2		//定义out为P2
sbit RS=P3^0;
sbit RW=P3^1;
sbit E=P3^2;
uchar code date[]="  H.I.T.CHINA  ";//开始显示的画面
uchar code time[]=" TIME  23:59:55 ";
uchar int_time;		//用作定时器,定时50ms
uchar second;			//50ms*20=1s,即当int_time==20时,second=1s
uchar minute;			//同理second==60s时,minute=1min
uchar hour;				//小时
uchar box=0;
uchar houchange[2];		//无符号字符型转化为字符型的存储数组
uchar minchange[2];		//存放分	
uchar sechange[2];		//存放秒
void lcd_initial(void);
void check_busy(void);
void write_command(uchar com);
void write_data(uchar dat);
void string(uchar ad,uchar *s);
void delay(uint);
void clock_init();
void displaytime();
void write_sfm(uchar ad,uchar *s);

void displaytime()	       	//显示字符函数
{
	houchange[0]=hour/10+0x30;   //hour存放的是无符号字符型,通过除以10可以得到
	houchange[1]=hour%10+0x30;   //hour的十位,再加上0x30变成ASCII码
	minchange[0]=minute/10+0x30; //然后%10获得个位
	minchange[1]=minute%10+0x30;
	sechange[0]=second/10+0x30;
	sechange[1]=second%10+0x30;
	write_sfm(0xC7,houchange);	//将hourchange写入相应的地址
	write_sfm(0xCa,minchange);	
	write_sfm(0xCd,sechange);
}
void clock_init()	//时钟的初始画面
{
	string(0x80,date);	//显示"  H.I.T.CHINA  "
	string(0xc0,time);	//    " TIME  23:59:55 "
}

void string(uchar ad,uchar *s)	
{
	write_command(ad);	//写入地址
	while(*s>0)			//在该地址用指针写入数组
	{
	write_data(*s++);	//写函数
	delay(100);
	}
}

void delay(uint j)	//延时函数
{
	uchar i=250;
	for(;j>0;j--)
	{
		while(--i);
		i=249;
		while(--i);
		i=250;
	}
}

void check_busy(void)
{
	uchar dt;
	do
	{
		dt=0xff;
		E=0;
		RS=0;
		RW=1;
		E=1;
		dt=out;
	}while(dt&0x80);
		E=0;
}

void write_command(uchar com)
{
	check_busy();
	E=0;
	RS=0;
	RW=0;
	out=com;
	E=1;
	_nop_();
	E=0;
	delay(1);
}
	
void write_data(uchar dat)
{
	check_busy();
	E=0;
	RS=1;
	RW=0;
	out=dat;
	E=1;
	_nop_();
	E=0;
	delay(1);	
}

void lcd_initial(void)
{
	RW=0;
	E=0;
	write_command(0x3C);
	write_command(0x0C);
	write_command(0x06);
	write_command(0x01);
	write_command(0x80);
	delay(1);
}
		
void write_sfm(uchar ad,uchar *s)	//写入数据函数
{
	write_command(ad);	//先写地址
	for(box=0;box<2;box++)//用for循环将数组的数据写入
	{
		write_data(s[box]);
		delay(100);
	}	
}

void main(void)
{
	lcd_initial();  //LCD初始化
	clock_init();	//设置时钟开机画面
	TMOD=0x01;		//时钟模式选择方式1
	EA=1;			//开启总中断
	ET0=1;			//打开T0定时器的中断
	TH0=0x3C;		//定时50ms
	TL0=0xB0;
	int_time=0;		//初始化秒计时器
	second=55;		//初始化秒
	minute=59;	 	//初始化分
	hour=23;		//初始化时
	TR0=1;			//打开定时器
	while(1) 	    //无限循环
	{
		displaytime();//写入上面初始化的数据
	if((P1&0x80)==0x00)  //修改时间按键
	{					//按下按键一时
		TR0=0;			//定时器停止
	if((P1&0x40)==0x00) //按下按键二
	{
		hour++;			//小时位+1
		displaytime();  //及时显示
		if(hour==24)	//如果满24归0
		{
		hour=0;
		displaytime();  //显示
		while((P1&0x40)==0x00);//如果按键时间过长在这里循环
		}
	}
	if((P1&0x20)==0x00) //按键三控制分
	{
		minute++;
		displaytime();	
		if(minute==60)
		{
			minute=0;
			displaytime();
		}
		while((P1&0x20)==0x00);
	}
	}while((P1&0x10)!=0x00);//按下按键四,时间设置完毕
		TH0=0x3C;	//重装定时器
		TL0=0xB0;
		TR0=1;	   //开始计时
		while((P1&0x80)==0x00);//按键一时间过长在此循环
	}
	}
}

void T0_interserve(void) interrupt 1//中断函数主要用作计时器
{
		int_time++;	
	if(int_time==20) //满1s
	{
		int_time=0;
		second++;
	}
	if(second==60) //满1min
	{
		second=0;
		minute++;
	}
	if(minute==60) //满1hour
	{
		minute=0;
		hour++;
	}
	if(hour==24)
	{
		hour=0;
	}
	TH0=0x3c;	//重新装载时间
	TL0=0xB0;
}

2、运行结果仿真演示

按下按键一——进入校时模式
按下按键二——小时+1
按下按键三——分+1
按下按键四——完成校时,时钟开始显示
数字钟的编写(单片机&LCD的应用)

四、最后说两句

从上一次写数字钟实验到现在,已经有大概四十二天了吧,想起上次在学校发下毒誓说“不行,我一定要用单片机写出来”,到现在终于写了出来,不得不说其中真的学到了很多,支撑我写下去的其实是——无聊哈哈,当然也挺艰难的,还好我在家无聊的很,有的是时间看。哈哈,再来几把平安京快乐一下,反正快乐就完事了~~~~
欢迎大家一起来讨论呀~。

会水的芝麻
在家
2019.1.31