使用51单片机LCD1602实现年月日时分秒的定时器显示

实现功能:利用51单片机的定时器原理制作时钟,显示使用LCD1602,可以显示时分秒年月日和星期 ,识别平年闰年,按键可调整时间日期,整点报时。

1、定时器中断实现时钟基本原理   

   若系统晶振为12MHZ,定时器工作在mode1模式时,每次最多可计数至65536,花费时间约为65ms。若只计数至50000,需要时间50ms,实现1s的定时需要重复上述过程20次。如此就利用

定时器实现了一秒钟的产生。我们仅需对定时器赋初值TH0=(65536-50000)/256;TL0=(65536-50000)%256;代码段如下

   TH0 = 0x4c;  //高八位赋初值   晶振位11.0592,赋初值位0x4c00
	TL0 = 0x00;	 //低八位赋初值     
	 i++;

	 if(i == 20){
	 	i = 0;  //满一秒清零
	 	miao++;	//i = 100 为一,秒标志位加 1

		if(miao == 60){
			miao = 0;  //满一分清零
			fen++;


			if(fen == 60){
				fen = 0;  //满一时清零
				shi++;
                mybeeplongplus();  //整点报时

				if(shi == 24){
					shi = 0;  //24小时制
					fen = 0;
					miao = 0;

2、LCD1602基本原理介绍

关于lcd1602的介绍网上资源实在太多可以参考这位博主的文章LCD1602介绍。他讲解的挺详细的,其中有关于LCD1602自定义字符的介绍与使用,我在这里推荐一款字符取模软件zimo221,百度直接可以搜索下载。

 

3、C语言如何利用数组存储“变量”

c语言的数组时不能够直接储存变量的,但是却可以储存变量的地址,再利用指针来间接储存变量。此处引用c语言指针数组的使用介绍

4、protues仿真图

此处未添加蜂鸣器,需要的可以直接接在P1^5口。

5、完整程序代码

main.c文件 

//设计思路 1、利用定时器中断0二十次产生1s时间变量miao增加到60变量分+1并把miao清零,shi同理
//         2、采用4个按键 长按key4进入设置模式后, key1选择调整对象 key2k3加减 
//         

#include"LCD1602.h"
#include<reg52.h>
void LCD_display(void);
void keykan(void);
int num[10]={0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39};  // lcd1602 0~9字码


uchar miao=0, fen=0, shi=0 , zhou=1 , yue=7 , nian=21 ;  //进位标志位  秒 分 时 周 月 年 
uchar ri=9;                                              //进位标志位 日 不知道为啥放在上面会报错,单独定义就可以
uint i=0;       //定时器计时标志位

uchar *time[]={&miao , &fen , &shi, &zhou ,&ri ,&yue , &nian};  //指针变量time存放进位标志位的地址,由于数组不能存放标量,但是却可以存放标量的地址,采用指针的方式来间接修改变量的值
                                                                //
uchar   SetPlace=0;     //功能选择标志
bit mod =0;            //设置模式标志位
sbit key1=P3^1;         //定义四个按键 key1选择需要调整的位
sbit key2=P3^0;         //key2加   
sbit key3=P3^2;         //key3减
sbit key4=P3^3;         //长按两秒钟进入或退出设置模式,前面三个按键需要进入设置模式才能使用


void main(void)
{
    T0_init();  //定时器初始化
    LCD_init();  //lcd1602初始化
    IT1=1;      //外部中断1初始化  下降沿触发
    EX1=1;      //启用外部中断1

    LCD_disp_char(2,5,0x00);        //显示自定义字符“周”
    delay(50);
        
       
    while(1)    //死循环                 
    {  
         
        keykan();
        LCD_display();     //lcd1602显示时分秒年月日周
    }

    
}
void keykan(void)
{

        if (mod==0)                     //如果没有进入设置模式 调整提示区域显示字符串time
        {
            LCD_row(2);
            LCD_DispString("TIME");
            delay(50);
        }
        
        
        LCD_disp_char(1,14,' ');        //清空第一行第15位的显示内容
        delay(50);


        if (mod==1)                     //如果进入设置模式显示一个1602字符库的一个特定字符
        {
          LCD_disp_char(1,14,0xef);
           delay(50);  
        
        
        if(key1==0)		//检测按键key1是否按下
			{
				delay(10);	//消除抖动
				if(key1==0)
				{                            //按一下功能选择位就+1,加到7就清零
					SetPlace++;
					if(SetPlace>=7)
						SetPlace=0;					
				}
                if (SetPlace==0)              //功能选择位为0就显示“miao”  为1就显示“fen” 以此类推,shi zhou ri yue nian
                {
                    LCD_row(2);
                    LCD_DispString("miao");
                    delay(50);
                }
                if (SetPlace==1)
                {
                    LCD_row(2);
                    LCD_DispString(" fen");
                    delay(50);
                }
                if (SetPlace==2)
                {
                    LCD_row(2);
                    LCD_DispString(" shi");
                    delay(50);
                }
                if (SetPlace==3)
                {
                    LCD_row(2);
                    LCD_DispString("zhou");
                    delay(50);
                }
                if (SetPlace==4)
                {
                    LCD_row(2);
                    LCD_DispString("  ri");
                    delay(50);
                }
                if (SetPlace==5)
                {
                    LCD_row(2);
                    LCD_DispString(" yue");
                    delay(50);
                }
                if (SetPlace==6)
                {
                    LCD_row(2);
                    LCD_DispString("nian");
                    delay(50);
                }
                
                mybeep();                   //蜂鸣器发声
				while(key1==0)	 //检测按键是否松开
				{
					delay(10);	
				}
				
			}
            if(key2==0)		//检测按键key2是否按下
			{
				delay(10);	//消除抖动
				if(key2==0)
				{
                    
                    (*time[SetPlace])++;                            //按键按一次 指针数组*time[SetPlace]里对应的地址的数据值就+1,也就是说进位标志位年月日时分秒的值就会+1
                    if ((((*time[SetPlace]))>=60) && (SetPlace==0))
                    {
                        (*time[SetPlace])=0;                            //秒只能加dao59
                    }
                    if ((((*time[SetPlace]))>=60) && (SetPlace==1))
                    {
                        (*time[SetPlace])=0;                            //分只能加到59
                    }
                    if ((((*time[SetPlace]))>=24) && (SetPlace==2))
                    {
                        (*time[SetPlace])=0;                            //时只能加到23
                    }
                    if ((((*time[SetPlace]))>=8) && (SetPlace==3))       //周只能加到7
                    {
                        (*time[SetPlace])=1;	
                    }
                    if ((((*time[SetPlace]))>=29) && (SetPlace==4) && (yue==2) && ((nian%4)!=0))
                    {                                                                                   //平年的2月只能加到28
                        (*time[SetPlace])=1;
                    }
                    if ((((*time[SetPlace]))>=30) && (SetPlace==4) && (yue==2) && ((nian%4)==0))            //闰年的2月29天
                    {
                        (*time[SetPlace])=1;
                    }
                    if ((((*time[SetPlace]))>=31) && (SetPlace==4) && ((yue==4)||(yue==6)||(yue==9)||(yue==11)))        //4 6 9 11 月只有30天
                    {
                        (*time[SetPlace])=1;
                    }
                    if ((((*time[SetPlace]))>=32) && (SetPlace==4) && ((yue==1)||(yue==3)||(yue==5)||(yue==7)||(yue==8)||(yue==10)||(yue==12)))     
                    {
                        (*time[SetPlace])=1;                                                                            //1 3 5 7 8 10 12月有31天
                    }

                    if ((((*time[SetPlace]))>=13) && (SetPlace==5))         //月份只能加到12
                    {
                        (*time[SetPlace])=1;

                    }   
                    mybeep();    //蜂鸣器响
                        while(key2==0)	 //检测按键是否松开
                    {
                        delay(10);	
                    }
                }
            }
            if(key3==0)		//检测按键key3是否按下
			{                                                  //key3与key2按钮基本原理相同,key3表示对应值减1
				delay(10);	//消除抖动
				if(key3==0)
				{
                    
                    if ((((*time[SetPlace]))<=0) && (SetPlace==0))
                    {
                        (*time[SetPlace])=60;
                    }
                    if ((((*time[SetPlace]))<=0) && (SetPlace==1))
                    {
                        (*time[SetPlace])=60;
                    }
                    if ((((*time[SetPlace]))<=0) && (SetPlace==2))
                    {
                        (*time[SetPlace])=24;
                    }

                    if ((((*time[SetPlace]))<=1) && (SetPlace==3))
                        {
                            (*time[SetPlace])=8;	
                        }
                    if ((((*time[SetPlace]))<=1) && (SetPlace==4) && (yue==2) && ((nian%4)!=0))
                    {
                        (*time[SetPlace])=29;
                    }
                    if ((((*time[SetPlace]))<=1) && (SetPlace==4) && (yue==2) && ((nian%4)==0))
                    {
                        (*time[SetPlace])=30;
                    }
                    if ((((*time[SetPlace]))<=1) && (SetPlace==4)&&((yue==4)||(yue==6)||(yue==9)||(yue==11)))
                        {
                            (*time[SetPlace])=31;
                        }
                    if ((((*time[SetPlace]))<=1) && (SetPlace==4)&&((yue==1)||(yue==3)||(yue==5)||(yue==7)||(yue==8)||(yue==10)||(yue==12)))
                        {
                            (*time[SetPlace])=32;
                        }

                    if ((((*time[SetPlace]))<=1) && (SetPlace==5))
                    {
                        (*time[SetPlace])=13;   
                
                    }
                    (*time[SetPlace])--;
				}
                mybeep();
                    while(key3==0)	 //检测按键是否松开
				{
					delay(10);	
				}
            }

        }
}
void timer0_int() interrupt 1       //定时器中断服务函数
{ 
 	 
    TH0 = 0x4c;  //高八位赋初值  晶振11.0592MHZ 赋初值
	TL0 = 0x2c;	 //低八位赋初值      
	 i++;

	 if(i == 20){
	 	i = 0;  //满一秒清零
	 	miao++;	//i = 100 为一,秒标志位加 1

		if(miao == 60){
			miao = 0;  //满一分清零
			fen++;


			if(fen == 60){
				fen = 0;  //满一时清零
				shi++;
                mybeeplongplus();  //整点报时

				if(shi == 24){
					shi = 0;  //24小时制
					fen = 0;
					miao = 0;
                    zhou++;
                    ri++;
                    if ((ri==31)&&((yue==1)||(yue==3)||(yue==5)||(yue==7)||(yue==8)||(yue==10)||(yue==12)))
                    {
                        yue++;              //  满31号回到1号
                        ri=1;
                    }
                    
                    if ((ri==28)&&(yue==2) && ((nian%4)!=0))
                    {
                        yue++;              //平年28天回到一号
                        ri=1;
                    }

                    if ((ri==29)&&(yue==2) && ((nian%4)==0))
                    {
                        yue++;                  //闰年29天
                        ri=1;
                    }
                    
                    if ((ri==30)&&((yue==4)||(yue==6)||(yue==9)||(yue==11)))
                    {
                        yue++;                  //4 6 9 11月每月30天                         
                        ri=1;
                    }
                    if (zhou==7)  //满一周回到周一
                    {
                       zhou=1;
                    }
                        if (yue==12)            //满12个月, 年份加1
                        {
                            nian++;
                            yue=1;
                        }
                        
                    
				}
			}
		}
     }
 }
      void LCD_display(void)      //lcd1602显示年月日时分秒周
      {  
        LCD_disp_char(2,6,*time[3]);   //显示周几 此处显示的为自定义字符 当zhou=1的时候即*time[3]=1  对应地址0x01的字符为 一 ;以此类推
        delay(50);

        LCD_disp_char(2,15,num[*time[0]%10]);     //显示秒的个位 当miao%10的值为0时即(*time[0])==0, 此时num[0]=0x30 对应1602字符库中的0 ,lcd1602对应第2行第16位显示0
        delay(50);                                  //当miao%10的值为1时即(*time[1])==1, 此时num[1]=0x31 对应1602字符库中的1

        LCD_disp_char(2,14,num[*time[0]/10]);       //秒的十位
        delay(50);

        LCD_disp_char(2,13,'-');
        delay(50);

        LCD_disp_char(2,12,num[*time[1]%10]);            //分的各位
        delay(50);

        LCD_disp_char(2,11,num[*time[1]/10]);               //分的十位
        delay(50);

        LCD_disp_char(2,10,'-');                            //字符‘-’
        delay(50);

        LCD_disp_char(2,9,num[*time[2]%10]);                //时的个位
        delay(50);

        LCD_disp_char(2,8,num[*time[2]/10]);                //时的十位
        delay(50);

        LCD_disp_char(1,0,'2');
        delay(50);
                                                        //显示字符 2 0 表示21世纪
        LCD_disp_char(1,1,'0');
        delay(50);

        LCD_disp_char(1,2,num[*time[6]/10]);              //年的个位
        delay(50);

        LCD_disp_char(1,3,num[*time[6]%10]);                //年的十位
        delay(50);

        LCD_disp_char(1,4,'-');
        delay(50);

        LCD_disp_char(1,5,num[*time[5]/10]);                //月的个位
        delay(50);

        LCD_disp_char(1,6,num[*time[5]%10]);                //月的十位
        delay(50);

        LCD_disp_char(1,7,'-');
        delay(50);

        LCD_disp_char(1,8,num[*time[4]/10]);                    //日的个位
        delay(50);

        LCD_disp_char(1,9,num[*time[4]%10]);                    //日的十位
        delay(50);
      }



      void my_int1(void)interrupt 2                 //外部中断1服务程序
      {
          delay_n40us(50000);               //消抖
          if (key4==0)
          {
            mod=~mod;        //进入或退出设置
            mybeeplong();    //蜂鸣器
            TR0=~TR0;       //打开或关闭定时器中断
		    SetPlace=0;     //模式选择位清零

          }
            while (key4==!key4);     
      }

lcd1602.h文件 

 #ifndef _LCD1602_H          //相当于if not define 防止程序中头文件被多次编译
#define _LCD1602_H
#include<reg52.h>
#define uchar unsigned char  
#define uint unsigned int
//lcd 1602 硬件接线端口
#define LCD_DB P0
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_E=P2^7;
sbit  BEEP=P1^5;   //蜂鸣器
void LCD_init(void);   //lcd初始化函数
void LCD_write_command(uchar command);    // 写指令函数
void LCD_write_data(uchar dat);             //写数据函数
void LCD_disp_char(uchar x , uchar y, char dat);  //在某一位置显示单个字符
void LCD_check_busy(void);              //检查忙函数
void LCD_row(uchar row);                //选择显示行数
void LCD_DispString(char str[]);            //显示字符串
void delay_n40us( uint n);                  //延时函数
void T0_init(void);                         //定时器初始化
void delay(uint x);                            //延时
void CgramWrite(void);                          //自定义字符装载
void mybeep(void);                               //蜂鸣器用于key123
void mybeeplong(void);                              //用于key4
void mybeeplongplus(void);                            //用于整点报时
#endif

lcd1602.c文件 

 #include"LCD1602.h"

uint code Xword[]={
    0x1F,0x15,0x1D,0x17,0x11,0x15,0x01,0x00,  //周,代码 0x00(由于这个lcd1602像素太小了5*8,这个周字属实有点认不出)
    0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,  //一,代码 0x01
    0x00,0x00,0x00,0x0e,0x00,0xff,0x00,0x00,  //二,代码 0x02
    0x00,0x00,0xff,0x00,0x0e,0x00,0xff,0x00,  //三,代码 0x03
    0x00,0x00,0xff,0xf5,0xfb,0xf1,0xff,0x00,  //四,代码 0x04
    0x00,0xfe,0x08,0xfe,0x0a,0x0a,0xff,0x00,  //五,代码 0x05
    0x00,0x04,0x00,0xff,0x00,0x0a,0x11,0x00,  //六,代码 0x06
    0x00,0x1f,0x11,0x1f,0x11,0x11,0x1f,0x00,  //日,代码 0x07
};


void CgramWrite(void) { // 装入CGRAM //
    uint i;
    LCD_write_command(0x06);   // CGRAM地址自动加1
    LCD_write_command(0x40);   // CGRAM地址设为00处
    for(i=0;i<64;i++) {
    LCD_write_data(Xword[i]);// 按数组写入数据
    }
}

void LCD_init(void)             
{
    LCD_write_command(0x38);
    delay_n40us(300);
    LCD_write_command(0x38);

    LCD_write_command(0X0c);   //开显示 关光标 不闪烁
    LCD_write_command(0x06);        //设定输入方式
    LCD_write_command(0X01);        //清除显示
    delay_n40us(300);
    CgramWrite();               //装载自定义字符
}

void LCD_write_command(uchar dat){
    
    LCD_DB=dat;
    LCD_RS=0;          //指令
    LCD_RW=0;         //写入
    LCD_E=1;          //允许
    LCD_E=0;
    delay_n40us(2);
}

void LCD_write_data(uchar dat ){
    
    LCD_DB=dat;
    LCD_RS=1;           //数据
    LCD_RW=0;                   //写入
    LCD_E=1;                //允许
    LCD_E=0;
    delay_n40us(2);
}

void LCD_disp_char(uchar y , uchar  x , uchar dat ){    //x为列  y为行
    uchar address;
    if (y==1)                   //传入参数为1 使用第一行显示
         address=0x80+x;            //将lcd地址确定为第一行起始地址加上x为最终地址
    else
   address=0xc0+x;                //参数不为1则显示第二行
   LCD_write_command(address);     //写入地址 
   LCD_write_data(dat);        //写入数据
   delay_n40us(2);
}

void LCD_check_busy(){
    do
    {
        LCD_E=0;
        LCD_RS=0;
        LCD_RW=1;
        LCD_DB=0Xff;
        LCD_E=1;
    } while (LCD_DB^7==1);
    
}

void LCD_row(uchar row){
    uchar pos;
    if (row==1)
        pos=0x00;
    else
        pos=0x40;
    LCD_write_command(pos | 0x80);  
}


void LCD_DispString(char str[]){
    uchar i =0;
    while (str[i] !='\0')
    {
        LCD_write_data(str[i]);
        delay_n40us(2);
        ++i;
    }
}


void delay_n40us(uint n){
    uint i;
    uchar j;
    for (i=n ; i>0; i--)
        for(j=0 ; j<2 ; j++);
}


void T0_init(){                     //定时器 0 初始化 
	TMOD = 0x01; //工作方式 1
	TH0 = 0x4c;  //高八位赋初值    
	TL0 = 0x00;	 //低八位赋初值
	EA = 1;	     //开总中断
	ET0 = 1;     //开 T0 中断
	TR0 = 1;     //启动 T0
}


void delay(uint x){  //延时函数 
	while(x--);
}

void mybeep(void){
    uint n;
    for ( n = 0; n < 100; n++)
    {
        BEEP=~BEEP;
        delay(10);
    }
    
}

void mybeeplong(void){
    uint n;
    for (n = 0; n < 1000; n++)
    {
        BEEP=~BEEP;
        delay(10);
    }
    
}

void mybeeplongplus(void){
    uint n;
    for (n = 0; n < 5000; n++)
    {
        BEEP=~BEEP;
        delay(10);
    }
    
}

 

物联沃分享整理
物联沃-IOTWORD物联网 » 使用51单片机LCD1602实现年月日时分秒的定时器显示

发表评论