单片机实训之万年历

单片机实训之万年历(具有时分秒,日期,星期调校功能

什么是万年历????

年历是中国古代传说中最古老的一部太阳历。万年历是记录一定时间范围内(比如100年或更多)的具体阳历与阴历的日期的年历,方便有需要的人查询使用。万年只是一种象征,表示时间跨度大。所使用的万年历,即包括若干年或适用于若干年的历书 。现代的万年历能同时显示公历、农历和干支历等多套历法,更能包含黄历相关吉凶宜忌、节假日、提醒等多种功能信息,极为方便人们查询使用。

1. 液晶多功能万年历的设计

该项目以AT89C51单片机为核心,结合数字温度调节器ds18b20、时钟芯片ds1302和液晶显示器lcd1602设计而成的液晶显示的万年历。系统中时钟模块为整个系统提供较为精准的时间数据;温度传感器模块可以获取周围环境的温度值且当温度高于一定的数值时,实现喇叭器报警;液晶显示模块用于显示日期(年,月,日)、时间(时,分,秒)、星期、温度的参数值,也可以通过按键来调整各个参数的数值(具有加、减功能)。

2. 设计主要功能

1.显示年、月、日(含闰年)、时、分、秒、星期(周几)、温度的信息;
2.具有温度显示功能;
3.具有温度报警功能,一旦温度超过35摄氏度,就开启喇叭实现报警;
4.定时开启和关闭(上午8:00-晚上22:00开启,晚上22:00-第二天上午8:00关闭)功能。
5.具有日期(年,月,日)和时间(时,分,秒)、星期、温度的调较功能。
6.具有整点报时功能。

1.2. 设计原理

1)单片机选择:采用传统的AT89C51作为此系统的控制核心,AT89C51是一种带4K字节闪烁可编程可擦除只读存储器的低电压高性能CMOS8位微处理器。
2)时钟芯片选择:DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能。
时钟芯片选择DS1302来实现时钟功能,DS1302芯片是一种高性能芯片,可自动对秒,分,时,月,日,周,年进行计数,而且精度高。
3)显示模块的选择:使用液晶显示屏(LCD1602)显示时间、日期以及星期的信息。
4)按键模块的选择:选择独立式的按钮(button),按键具体设置为三个按键,功能分为设置,加,减,确定修改。

主要电路图如下:

在这里插入图片描述

主要代码如下:

shuzishizhong.c

#include<REG51.H>
#include<stdio.h>
#include<stdlib.h>
#include "LCD1602.H"
#include "DS1302.H"
#include "ds18b20.H"
#define uint unsigned int
#define uchar unsigned char
#define TH 35  //设置温度上限

extern unsigned char week_value[2],TempBuffer[5];   //声明外部变量
extern int temp_value;
char hide_sec,hide_min,hide_hour,hide_day,hide_week,hide_month,hide_year;  //秒,分,时到日,月,年位闪的计数
sbit Set  = P3^0;       //P3.0口控制模式切换键
sbit Up   = P3^1;        //加法按钮
sbit Down = P3^2;      //减法按钮
sbit out  = P3^3;       //立刻跳出调整模式按钮
sbit deng = P1^1; 
sbit bee=P1^3;
sbit key_set_alarm=P3^7;
char done,count,temp,flag,up_flag,down_flag;
char  SD=1;

int  a,b,c;
int b=1;

void show_time();   //液晶显示函数声明
void close_time();  //液晶屏关闭函数声明
/**************************************************************
延时子程序
**************************************************************/
void mdelay(uint delay)
{
    
     uint i;
  for(;delay>0;delay--)
    {
    
    for(i=0;i<62;i++) //1ms延时.
        {
    
    ;}
    }
}
/**************************************************************
用于液晶屏显示关闭
**************************************************************/
void close_time(){
    
    
    LCD_Write(0,0x01); 
    mdelay(1);
}
/**************************************************************
用于液晶屏显示开起		 
**************************************************************/
//void open_time(){
    
    
//   LCD_SetDisplay(0x04|0x02); 
   //mdelay(1000);
//}
/**************************************************************
升序按键
**************************************************************/
void Upkey()
{
    
        
Up=1;
if(Up==0){
    
    
   mdelay(8);
       switch(count)
          {
    
    
		  case 1:
             temp=Read1302(DS1302_SECOND);  //读取秒数
		     temp=temp+1;  //秒数加1
             up_flag=1;    //数据调整后更新标志
             if((temp&0x7f)>0x59)   //超过59秒,清零
                 temp=0;   
             break;
           case 2:
             temp=Read1302(DS1302_MINUTE);  //读取分数
             temp=temp+1;  //分数加1
             up_flag=1;
             if(temp>0x59)          //超过59分,清零
                 temp=0;
             break;
           case 3:
             temp=Read1302(DS1302_HOUR);  //读取小时数
             temp=temp+1;  //小时数加1
             up_flag=1;
             if(temp>0x23)   //超过23小时,清零
                 temp=0;
             break;
           case 4:
             temp=Read1302(DS1302_WEEK);  //读取星期数
             temp=temp+1;  //星期数加1
             up_flag=1;
             if(temp>0x7)  //大于周1-周7清零
                 temp=1;
             break; 
           case 5:
             temp=Read1302(DS1302_DAY);  //读取日数
             temp=temp+1;  //日数加1
             up_flag=1;
             if(temp>0x31) //大于31天清零
                temp=1;
             break;
           case 6:
             temp=Read1302(DS1302_MONTH);  //读取月数
             temp=temp+1;  //月数加1
             up_flag=1;
             if(temp>0x12) //大于12个月清零
                temp=1;
             break;
           case 7:
             temp=Read1302(DS1302_YEAR);  //读取年数
             temp=temp+1;  //年数加1
             up_flag=1;
             if(temp>0x85) //大于85年清零
               temp=0;
             break;
           default:break;
          }
     while(Up==0);
    }
}
/**************************************************************
降序按键
**************************************************************/
void Downkey()
{
    
         
    Down=1;
    if(Down==0){
    
    
     mdelay(8);
     switch(count){
    
    
		  case 1:
             temp=Read1302(DS1302_SECOND);  //读取秒数
             temp=temp-1;     //秒数减1
             down_flag=1;       //数据调整后更新标志
             if(temp==0x7f){
    
         //小于0秒,返回59秒
                 temp=0x59;
			 }
             break;
           case 2:
             temp=Read1302(DS1302_MINUTE);  //读取分数
             temp=temp-1;  //分数减1
             down_flag=1;
             if(temp==-1){
    
    
                temp=0x59; 
			 }	      //小于0分,返回59分
             break;
           case 3:
             temp=Read1302(DS1302_HOUR);  //读取小时数
             temp=temp-1;  //小时数减1
             down_flag=1;
             if(temp==-1)
                temp=0x23;
             break;
           case 4:
             temp=Read1302(DS1302_WEEK);  //读取星期数
             temp=temp-1;  //星期数减1
             down_flag=1;
             if(temp==0)
                temp=0x07;
             break; 
           case 5:
             temp=Read1302(DS1302_DAY);  //读取日数
             temp=temp-1;  //日数减1
             down_flag=1;
             if(temp==0)
                temp=0x31;
             break;
           case 6:
             temp=Read1302(DS1302_MONTH);  //读取月数
             temp=temp-1;  //月数减1
             down_flag=1;
             if(temp==0)
                temp=0x12;
             break;
           case 7:
             temp=Read1302(DS1302_YEAR);  //读取年数
             temp=temp-1;  //年数减1
             down_flag=1;
             if(temp==-1)
                temp=0x85;
             break;
      default:break;
         }
   while(Down==0);  
  }
}
/**************************************************************
模式选择按键
**************************************************************/
void Setkey()
{
    
    
Set=1;
if(Set==0)
{
    
    
           mdelay(8);
           count=count+1; //Setkey按一次,count就加1
           done=1; //进入调整模式
           while(Set==0);
           
}

}
/**************************************************************
跳出调整模式,返回默认显示
**************************************************************/
void outkey()                    
{
    
     uchar Second;
  out=1;
  if(out==0)         
  {
    
      
       count=0;
       hide_sec=0,hide_min=0,hide_hour=0,hide_day=0,hide_week=0,hide_month=0,hide_year=0;
       Second=Read1302(DS1302_SECOND);
       Write1302(0x8e,0x00);          //写入允许
       Write1302(0x80,Second&0x7f);
       Write1302(0x8E,0x80);          //禁止写入
       done=0;           
       while(out==0);
  }
}
/**************************************************************
按键功能执行
**************************************************************/
void keydone(){
    
            
uchar Second;
if(flag==0)    //关闭时钟,停止计时
{
    
    
    Write1302(0x8e,0x00); //写入允许
    temp=Read1302(0x80);
    Write1302(0x80,temp|0x80);
    Write1302(0x8e,0x80); //禁止写入
    flag=1;
}
Setkey();             //扫描模式切换按键
switch(count){
    
    
    case 1:do{
    
             //count=1,调整秒
       outkey();    //扫描跳出按钮
       Upkey();                //扫描加按钮
       Downkey();              //扫描减按钮
   if(up_flag==1||down_flag==1)  //数据更新,重新写入新的数据
   {
    
    
   Write1302(0x8e,0x00); //写入允许
   Write1302(0x80,temp|0x80); //写入新的秒数
   Write1302(0x8e,0x80); //禁止写入
   up_flag=0;
   down_flag=0;
   }

   hide_sec++;          //位闪计数
   if(hide_sec>3)
     hide_sec=0;
     show_time();         //液晶显示数据
  }while(count==2);break;  
  case 2:do{
    
             //count=2,调整分
   hide_sec=0;
   outkey();
   Upkey();
   Downkey();
   if(temp>0x60)
     temp=0;
   if(up_flag==1||down_flag==1)
   {
    
    
   Write1302(0x8e,0x00); //写入允许
   Write1302(0x82,temp); //写入新的分数
   Write1302(0x8e,0x80); //禁止写入
   up_flag=0;
   down_flag=0;
   }
   hide_min++;
   if(hide_min>3)
     hide_min=0;
                   show_time();
  }while(count==3);break;
  case 3:do{
    
             //count=3,调整小时
   hide_min=0; 
   outkey();
   Upkey();
   Downkey();
   if(up_flag==1||down_flag==1)
   {
    
    
   Write1302(0x8e,0x00); //写入允许
   Write1302(0x84,temp); //写入新的小时数
   Write1302(0x8e,0x80); //禁止写入
   up_flag=0;
   down_flag=0;
   }
   hide_hour++;
   if(hide_hour>3)
     hide_hour=0;
     show_time();
  }while(count==4);break;
case 4:do{
    
             //count=4,调整星期
   hide_hour=0; 
   outkey();
   Upkey();
   Downkey();
   if(up_flag==1||down_flag==1)
   {
    
    
   Write1302(0x8e,0x00); //写入允许
   Write1302(0x8a,temp); //写入新的星期数
   Write1302(0x8e,0x80); //禁止写入
   up_flag=0;
   down_flag=0;
   }
   hide_week++;
   if(hide_week>3)
     hide_week=0;
     show_time();
  }while(count==5);break;  
  case 5:do{
    
             //count=5,调整日
   hide_week=0; 
   outkey();
   Upkey();
   Downkey();
   if(up_flag==1||down_flag==1)
   {
    
    
   Write1302(0x8e,0x00); //写入允许
   Write1302(0x86,temp); //写入新的日数
   Write1302(0x8e,0x80); //禁止写入
   up_flag=0;
   down_flag=0;
   }
   hide_day++;
   if(hide_day>3)
     hide_day=0;
     show_time();
  }while(count==6);break;
  case 6:do{
    
             //count=6,调整月
   hide_day=0; 
   outkey();
   Upkey();
   Downkey();
   if(up_flag==1||down_flag==1)
   {
    
    
   Write1302(0x8e,0x00); //写入允许
   Write1302(0x88,temp); //写入新的月数
   Write1302(0x8e,0x80); //禁止写入
   up_flag=0;
   down_flag=0;
   }
   hide_month++;
   if(hide_month>3)
     hide_month=0;
     show_time();
  }while(count==7);break;
  case 7:do{
    
             //count=7,调整年     
   hide_month=0; 
   outkey();
   Upkey();
   Downkey();
   if(up_flag==1||down_flag==1)
   {
    
    
   Write1302(0x8e,0x00); //写入允许
   Write1302(0x8c,temp); //写入新的年数
   Write1302(0x8e,0x80); //禁止写入
   up_flag=0;
   down_flag=0;
   }
   hide_year++;
   if(hide_year>3)
     hide_year=0;
                   show_time();
  }while(count==8);break;
  case 8: 
     count=0;hide_year=0;  //count8, 跳出调整模式,返回默认显示状态
     Second=Read1302(DS1302_SECOND);
     Write1302(0x8e,0x00); //写入允许
     Write1302(0x80,Second&0x7f);
     Write1302(0x8E,0x80);          //禁止写入
  done=0;
  break; //count=7,开启中断,标志位置0并退出
  default:break;

}

}
/**************************************************************
液晶显示程序
**************************************************************/
void show_time()   
{
    
    
//while(1)
//{
    
    
  DS1302_GetTime(&CurrentTime);  //获取时钟芯片的时间数据
  TimeToStr(&CurrentTime);       //时间数据转换液晶字符
  DateToStr(&CurrentTime);       //日期数据转换液晶字符
  ReadTemp();                    //开启温度采集程序
  temp_to_str();                 //温度数据转换成液晶字符
  GotoXY(12,1);                  //液晶字符显示位置
  Print(TempBuffer);             //显示温度
  GotoXY(0,1);
  Print(CurrentTime.TimeString); //显示时间
  GotoXY(0,0);
  Print(CurrentTime.DateString); //显示日期
  GotoXY(15,0);
  Print(week_value);             //显示星期
  GotoXY(11,0);
  Print("Week"); //在液晶上显示 字母 week 
  mdelay(50);                 //扫描延时 
//}  
}
/**************************************************************
报警检测与取消
**************************************************************/
void warming()
{
    
    
  if(temp_value>=TH)
     {
    
    if(SD==1&&SD!=0)ET0=1;}
 else {
    
    deng=0;ET0=0;SD=1;}
 out=1;
if(out==0){
    
    ET0=0;SD=0;}
}
/**************************************************************
主程序
**************************************************************/
void main()
{
    
    
    TMOD=0x02;              //设置模式为定时器T0的模式2 (8位自动重装计数初值的计数值)        
    TH0=0x03;               //设置计数器初值,靠TH0存储重装的计数值
    TL0=0x03;
    TR0=1;                  //启动T0
    ET0=0;                  //关定时器T0中断
    EA=1;
    P1=0;
    flag=1;           //时钟停止标志
    LCD_Initial();    //液晶初始化
    Initial_DS1302(); //时钟芯片初始化
    up_flag=0;   //调整标志位置零
    down_flag=0;  
    done=0;           //进入默认液晶显示
while(1)
{
    
       
   while(done==1){
    
    
       keydone(); //进入调整模式
       warming();
   }   
while(done==0)    
{
    
      
      show_time();
	  warming();
	  b=1;
	  while(b)							   //b为标志量
	  {
    
    
		   a=(int)Read1302(DS1302_HOUR);   //获取小时数	0x0x
		   b=(int)Read1302(DS1302_MINUTE); //获取分钟数	0x0x
	       c=(int)Read1302(DS1302_SECOND); //获取秒钟数	0x0x
		   warming();
		   if(b==0x00 && c==0x00){
    
    		   //实现整点报时
	              bee=0;				   
				  warming();
	       }else{
    
    
		          bee=1;
				  warming();
		   }    
		   if(a<0x08 || a>0x21){
    
    
		      b=1;
			  warming();
			  close_time(); //清屏 
		   }else{
    
     
		      b=0;
			  warming();	 
		   }
	  }

     flag=0;                  
     Setkey(); //扫描各功能键
	 warming();
/*   while(1){
	 b=(int)Read1302(DS1302_MINUTE);
	 c=(int)Read1302(DS1302_SECOND);
	 if(b==0x00 && c==0x00){
	      bee=0;
	 }
   }*/
   }   
}
warming();   
}
//}
/**************************************************************
定时器中断
**************************************************************/
void t0 (void) interrupt 1 using 1 
{
    
    
deng=!deng;
}

LCD1602.h

#ifndef LCD_CHAR_1602
#define LCD_CHAR_1602
#include <intrins.h>
sbit LcdRs = P2^0; //定义端口
sbit LcdRw = P2^1;
sbit LcdEn  = P2^2;
sfr  DBPort = 0x80; //P0=0x80,P1=0x90,P2=0xA0,P3=0xB0.数据端口

/********************************************************
内部等待函数
********************************************************/
unsigned char LCD_Wait(void)
{
    
    
LcdRs=0;
LcdRw=1; _nop_();
LcdEn=1; _nop_();
 
LcdEn=0;
return DBPort;
}
/*******************************************************
向LCD写入命令或数据
*******************************************************/
#define LCD_COMMAND 0      // Command
#define LCD_DATA 1      // Data
#define LCD_CLEAR_SCREEN 0x01      // 清屏
#define LCD_HOMING  0x02      // 光标返回原点
void LCD_Write(bit style, unsigned char input)
{
    
    
LcdEn=0;
LcdRs=style;
LcdRw=0; _nop_();
DBPort=input; _nop_();//注意顺序
LcdEn=1; _nop_();//注意顺序
LcdEn=0; _nop_();
LCD_Wait();
}

/*****************************************************
设置显示模式
*****************************************************/
#define LCD_SHOW 0x04    //显示开
#define LCD_HIDE 0x00    //显示关   

#define LCD_CURSOR 0x02 //显示光标
#define LCD_NO_CURSOR 0x00    //无光标      

#define LCD_FLASH 0x01    //光标闪动
#define LCD_NO_FLASH 0x00    //光标不闪动

void LCD_SetDisplay(unsigned char DisplayMode)
{
    
    
LCD_Write(LCD_COMMAND, 0x08|DisplayMode);
}

/***************************************************
设置输入模式
***************************************************/
#define LCD_AC_UP 0x02
#define LCD_AC_DOWN 0x00      // default

#define LCD_MOVE 0x01      // 画面可平移
#define LCD_NO_MOVE 0x00      //default

void LCD_SetInput(unsigned char InputMode)
{
    
    
LCD_Write(LCD_COMMAND, 0x04|InputMode);
}

/***************************************************
初始化LCD
***************************************************/
void LCD_Initial()
{
    
    
LcdEn=0;
LCD_Write(LCD_COMMAND,0x38);           //8位数据端口,2行显示,5*7点阵
LCD_Write(LCD_COMMAND,0x38);
LCD_SetDisplay(LCD_SHOW|LCD_NO_CURSOR);    //开启显示, 无光标
LCD_Write(LCD_COMMAND,LCD_CLEAR_SCREEN);   //清屏
LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE);       //AC递增, 画面不动
}

/***********************************************
液晶字符输入的位置
************************************************/
void GotoXY(unsigned char x, unsigned char y)
{
    
    
if(y==0)
LCD_Write(LCD_COMMAND,0x80|x);
if(y==1)
LCD_Write(LCD_COMMAND,0x80|(x-0x40));
}

/************************************************
将字符输出到液晶显示
************************************************/
void Print(unsigned char *str)
{
    
    
while(*str!='\0')
{
    
    
LCD_Write(LCD_DATA,*str);
str++;
}
}
#endif

DS1302.h

#ifndef _REAL_TIMER_DS1302_2009_6_28_
#define _REAL_TIMER_DS1302_2009_6_28_

sbit  DS1302_CLK = P1^6;              //实时时钟时钟线引脚 
sbit  DS1302_IO  = P1^7;              //实时时钟数据线引脚 
sbit  DS1302_RST = P1^5;              //实时时钟复位线引脚
sbit  ACC0 = ACC^0;
sbit  ACC7 = ACC^7;
unsigned char week_value[2];
char hide_sec,hide_min,hide_hour,hide_day,hide_week,hide_month,hide_year;  //秒,分,时到日,月,年位闪的计数

typedef struct __SYSTEMTIME__
{
    
    
unsigned char Second;
unsigned char Minute;
unsigned char Hour;
unsigned char Week;
unsigned char Day;
unsigned char Month;
unsigned char  Year;
unsigned char DateString[11];
unsigned char TimeString[9];
}SYSTEMTIME; //定义的时间类型
SYSTEMTIME CurrentTime;


#define AM(X) X
#define PM(X) (X+12)              // 转成24小时制
#define DS1302_SECOND 0x80          //时钟芯片的寄存器位置,存放时间
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84 
#define DS1302_WEEK 0x8A
#define DS1302_DAY 0x86
#define DS1302_MONTH 0x88
#define DS1302_YEAR 0x8C 

void DS1302InputByte(unsigned char d) //实时时钟写入一字节(内部函数)
{
    
     
    unsigned char i;
    ACC = d;
    for(i=8; i>0; i--)
    {
    
    
        DS1302_IO = ACC0;            //相当于汇编中的 RRC
        DS1302_CLK = 1;
        DS1302_CLK = 0;
        ACC = ACC >> 1; 
    } 
}

unsigned char DS1302OutputByte(void) //实时时钟读取一字节(内部函数)
{
    
     
    unsigned char i;
    for(i=8; i>0; i--)
    {
    
    
        ACC = ACC >>1;          //相当于汇编中的 RRC 
        ACC7 = DS1302_IO;
        DS1302_CLK = 1;
        DS1302_CLK = 0;
    } 
    return(ACC); 
}

void Write1302(unsigned char ucAddr, unsigned char ucDa) //ucAddr: DS1302地址, ucData: 要写的数据
{
    
    
    DS1302_RST = 0;
    DS1302_CLK = 0;
    DS1302_RST = 1;
    DS1302InputByte(ucAddr);        // 地址,命令 
    DS1302InputByte(ucDa);        // 写1Byte数据
    DS1302_CLK = 1;
    DS1302_RST = 0;
} 

unsigned char Read1302(unsigned char ucAddr) //读取DS1302某地址的数据
{
    
    
    unsigned char ucData;
    DS1302_RST = 0;
    DS1302_CLK = 0;
    DS1302_RST = 1;
    DS1302InputByte(ucAddr|0x01);        // 地址,命令 
    ucData = DS1302OutputByte();         // 读1Byte数据
    DS1302_CLK = 1;
    DS1302_RST = 0;
    return(ucData);
}



void DS1302_GetTime(SYSTEMTIME *Time)  //获取时钟芯片的时钟数据到自定义的结构型数组
{
    
    
unsigned char ReadValue;
ReadValue = Read1302(DS1302_SECOND);
Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_MINUTE);
Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_HOUR);
Time->Hour = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_DAY);
Time->Day = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_WEEK);
Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_MONTH);
Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_YEAR);
Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
}

void DateToStr(SYSTEMTIME *Time)    //将时间年,月,日,星期数据转换成液晶显示字符串,放到数组里DateString[]
{
    
       if(hide_year<2)                 //这里的if,else语句都是判断位闪烁,<2显示数据,>2就不显示,输出字符串为 2013-01-02
    {
    
                                   
  Time->DateString[0] = '2';
  Time->DateString[1] = '0';  
  Time->DateString[2] = Time->Year/10 + '0';
  Time->DateString[3] = Time->Year%10 + '0';
}
  else
    {
    
     
      Time->DateString[0] = ' ';
      Time->DateString[1] = ' ';  
      Time->DateString[2] = ' ';
      Time->DateString[3] = ' ';
}
Time->DateString[4] = '-';
if(hide_month<2)
{
    
    
  Time->DateString[5] = Time->Month/10 + '0';
  Time->DateString[6] = Time->Month%10 + '0';
}
  else
  {
    
    
    Time->DateString[5] = ' ';
    Time->DateString[6] = ' ';
  }
Time->DateString[7] = '-';
if(hide_day<2)
{
    
    
  Time->DateString[8] = Time->Day/10 + '0';
  Time->DateString[9] = Time->Day%10 + '0';
}
  else
  {
    
    
    Time->DateString[8] = ' ';
    Time->DateString[9] = ' ';     
  }
/* if(hide_week<2)
{
  week_value[0] = Time->Week%10+'0';  //星期的数据另外放到 week_value[]数组里,跟年,月,日的分开存放,因为等一下要在最后显示
}
  else
  {
    week_value[0] = ' ';
  }
  week_value[1] = '\0';

Time->DateString[10] = '\0'; //字符串末尾加 '\0' ,判断结束字符 */
}

void TimeToStr(SYSTEMTIME *Time)  //将时,分,秒数据转换成液晶显示字符放到数组 TimeString[];
{
    
       if(hide_hour<2)
    {
    
    
  Time->TimeString[0] = Time->Hour/10 + '0';
  Time->TimeString[1] = Time->Hour%10 + '0';
}
  else
    {
    
    
      Time->TimeString[0] = ' ';
      Time->TimeString[1] = ' ';
}
Time->TimeString[2] = ':';
    if(hide_min<2)
{
    
    
  Time->TimeString[3] = Time->Minute/10 + '0';
  Time->TimeString[4] = Time->Minute%10 + '0';
}
  else
    {
    
    
      Time->TimeString[3] = ' ';
      Time->TimeString[4] = ' ';
        }
Time->TimeString[5] = ':';
    if(hide_sec<2)
    {
    
    
  Time->TimeString[6] = Time->Second/10 + '0';
  Time->TimeString[7] = Time->Second%10 + '0';
    }
      else
       {
    
    
         Time->TimeString[6] = ' ';
     Time->TimeString[7] = ' ';
       }
Time->DateString[8] = '\0';
}


void Initial_DS1302(void)   //时钟芯片初始化
{
    
       
unsigned char Second=Read1302(DS1302_SECOND);
if(Second&0x80)       //判断时钟芯片是否关闭   
    {
    
    
Write1302(0x8e,0x00); //写入允许
Write1302(0x8c,0x13); //以下写入初始化时间 日期:13-01-02.星期: 3. 时间: 14:03:00
Write1302(0x88,0x01);
Write1302(0x86,0x02);
Write1302(0x8a,0x07);
Write1302(0x84,0x14);
Write1302(0x82,0x03);
Write1302(0x80,0x00);
Write1302(0x8e,0x80); //禁止写入
}

}
#endif

DS18B20.h

#ifndef DS18B20_H_2009_6_28
#define DS18B20_H_2009_6_28

#define uint unsigned int
#define uchar unsigned char

sbit DQ = P1^0;        //温度传送数据IO口
int temp_value;      //温度值
unsigned char TempBuffer[5];  // 存放温度的各个位的值

/***********ds18b20延迟子函数(晶振12MHz )*******/ 

void delay_18B20(unsigned int i)
{
    
    
while(i--);
}

/**********ds18b20初始化函数**********************/

void Init_DS18B20(void) 
{
    
    
unsigned char x=0;
DQ = 1;          //DQ复位
delay_18B20(8);  //稍做延时
DQ = 0;          //单片机将DQ拉低
delay_18B20(80); //精确延时 大于 480us
DQ = 1;          //拉高总线
delay_18B20(14);
x=DQ;            //稍做延时后 如果x=0则初始化成功 x=1则初始化失败
delay_18B20(20);
}

/***********ds18b20读一个字节**************/  

unsigned char ReadOneChar(void)
{
    
    
uchar i=0;
uchar dat = 0;
for (i=8;i>0;i--)
{
    
    
  DQ = 0; // 给脉冲信号
  dat>>=1;
  DQ = 1; // 给脉冲信号
  if(DQ)
  dat|=0x80;
  delay_18B20(4);
}
  return(dat);
}

/*************ds18b20写一个字节****************/  

void WriteOneChar(uchar dat)
{
    
    
  unsigned char i=0;
  for (i=8; i>0; i--)
  {
    
    
  DQ = 0;
  DQ = dat&0x01;
    delay_18B20(5);
  DQ = 1;
    dat>>=1;
 }
}

/**************读取ds18b20当前温度************/

void ReadTemp(void)
{
    
    
unsigned char a=0;
unsigned char b=0;
unsigned char t=0;

Init_DS18B20();
WriteOneChar(0xCC);    // 跳过读序号列号的操作
WriteOneChar(0x44); // 启动温度转换

delay_18B20(100);       // this message is wery important

Init_DS18B20();
WriteOneChar(0xCC); //跳过读序号列号的操作
WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度

delay_18B20(100);

a=ReadOneChar();    //读取温度值低位
b=ReadOneChar();    //读取温度值高位
temp_value=b<<4;
temp_value+=(a&0xf0)>>4;               
}
void temp_to_str()   //温度数据转换成液晶字符显示
{
    
    
  TempBuffer[0]=temp_value/10+'0';  //十位
  TempBuffer[1]=temp_value%10+'0';  //个位
  TempBuffer[2]=0xdf;   //温度符号
  TempBuffer[3]='C';
  TempBuffer[4]='\0';
}
#endif

1.6.2 上电后显示屏
通电后的LCD可以显示出现在的日期,包括年月日星期还有时分秒,另外还有温度的显示。
在这里插入图片描述

1-11 通电后LCD显示屏
1.6.3 按键设置时间
按下设置按键时可对显示屏的时间进行调整。(加操作)
在这里插入图片描述

按下左边第一个键,开启调较按钮,按下次数与对应关系如下:
1->秒调校
2->分调较
3->时调校
4->星期调校
5->日调校
6->月调校
7->年调校
8->确定的修改时间
 秒调校:
在这里插入图片描述

调教后的结果:
在这里插入图片描述

 分调节:
在这里插入图片描述

调校后的结果:
在这里插入图片描述

 时调校:
在这里插入图片描述

调较后:
在这里插入图片描述
在这里插入图片描述

1-12 按键设置
 星期修改:
在这里插入图片描述

修改星期后:

在这里插入图片描述
在这里插入图片描述

 日修改:
在这里插入图片描述

修改日后:

在这里插入图片描述
在这里插入图片描述

 月修改:
在这里插入图片描述

修改月份后:

在这里插入图片描述
在这里插入图片描述

 年修改:
在这里插入图片描述

修改年份好:
在这里插入图片描述

在这里插入图片描述

1.6.4 小灯到指定时间闪烁,表示闹钟在指定时间(整点)响起。(从20:59——21:00实现报时)
在这里插入图片描述
在这里插入图片描述

1-14 小灯闪烁(闹铃响起)
1.6.5 顺利完成定时显示(早上8:00-晚上22:00开启,晚上22:00-第二天早上8:00关闭)
 早上7:59-8:00实现跳转

在这里插入图片描述

在这里插入图片描述

 晚上21:59-22:00实现跳转
在这里插入图片描述
在这里插入图片描述

1.6.6 DS1302获取温度传入显示屏,并设置温度当温度超过35摄氏度时实现报警
在这里插入图片描述

温度>35摄氏度时,喇叭响。

在这里插入图片描述

1.4. 调试中遇到的问题和解决方法
(1)在调节时间和日期之后,单片机上电更新的是PC的时间,后来查资料,是设置DS1302的问题,且存在一定的误差且误差在2-3s内,随着运行的时间不断增加,误差会不断地加大。
(2)按键操作时有时会出现功能不稳定,这是由于按键存在抖动,后来加个延时函数就可以去抖动,实现按键按下后马上得到相应。
(3)调整日期时,使得日期与星期无法联动。后期仍需要改进和修改。

猜你喜欢

转载自blog.csdn.net/weixin_45790490/article/details/109608252