C51数字时钟/日历---LCD1602液晶显示屏

题目要求: 数字电子日历/时钟设计

设计一个基于MCS51的电子日历和时钟。

  • 基本要求

(1) 可通过按键在日历和时间之间切换显示;

(2) 可由按键调整日期和时间

(3) 可整点报时("嘟、嘟"声)

(4) 可设定时,定时时间到发出"嘟、嘟"声

(5) 具有秒表功能

课程设计电路图:

该课程设计采用ATMEL公司的FLASH 型经典芯片------AT89C51系列单片机 作为时钟的控制核心,用于自动显示当前时间、日期、星期、温度 。利用单片机定时器T0的中断程序设计出一秒钟的精确定时,通过单一按钮实现了当前时间、日期、秒表、设定闹钟、温度测量等功能的切换,通过矩阵键盘编程实现设置年、月、日、时、分、秒的功能,也可以设定闹钟时间。闹钟可以自定义开关,并具有贪睡功能,闹钟到达预设的时间时,利用扬声器发出"嘟、嘟"声音。系统具有实现整点报时功能。最后在设计中时附加了测温功能,利用DS18B20实时测试环境的温度。 该设计实现功能:

(1) 可通过按键在日历和时间之间切换显示;

(2) 可由按键调整日期和时间;

(3) 可整点报时("嘟、嘟"声);

(4) 可设定时,定时时间到发出"嘟、嘟"声,并具有贪睡功能和开关功能;

(5) 具有秒表功能;

(6) 具有实时温度测量功能;

(7) 具有星期查询功能;

由于C语言程序设计较汇编可读性强,可移植性,且可以大大降低编程的难度和缩短开发周期,本系统程序采用C语言设计。

  • 软件设计思想:

(1)单片机控制模块:单片机控制模块在系统中处于核心地位,其工作包括读取并处理键盘输入、显示模块控制、显示模块切换、闹钟控制等任务。

(2)按键输入模块:此模块完成对各种功能的控制,功能的切换在硬件上通过此部分来操作完成。

(3)温度传感器模块:此模块完成测温功能,通过温度传度器对外部温度的读取,并将信号输入单片机,单片机将此信号进行处理,并在LCD1602上显示。

(4)闹钟模块:此模块实现时钟的整点报时,定时响铃。系统正常工作后,每到整点或闹钟设定时间时,扬声器会发出"嘟、嘟"声。

(1) 可通过按键在日历和时间之间切换显示;

(2) 可由按键调整日期和时间;

(3) 可整点报时("嘟、嘟"声);

(4) 可设定时,定时时间到发出"嘟、嘟"声,并具有贪睡功能和开关功能;

(5) 具有秒表功能;

(6) 具有实时温度测量功能;

(7) 具有星期查询功能;

由于C语言程序设计较汇编可读性强,可移植性,且可以大大降低编程的难度和缩短开发周期,本系统程序采用C语言设计。

  • 软件设计思想:
    (1)单片机控制模块:单片机控制模块在系统中处于核心地位,其工作包括读取并处理键盘输入、显示模块控制、显示模块切换、闹钟控制等任务。
    (2)按键输入模块:此模块完成对各种功能的控制,功能的切换在硬件上通过此部分来操作完成。
    (3)温度传感器模块:此模块完成测温功能,通过温度传度器对外部温度的读取,并将信号输入单片机,单片机将此信号进行处理,并在LCD1602上显示。
    (4)闹钟模块:此模块实现时钟的整点报时,定时响铃。系统正常工作后,每到整点或闹钟设定时间时,扬声器会发出"嘟、嘟"声。
    使用定时器T0定时50ms用于时钟计时,定时器T1定时10ms用于秒表计时,外部中断0用于设定定时开关,外部中断1用于设定秒表的计时和清零。
    初值计算:单片机主频12MHz,机器周期为1微妙,定时器T0使用方式1定时50ms,则计数初值为216-(50ms/1us)=65536-50000=0x3cb0;定时器T1使用方式1定时10ms,则计数初值为216-(10ms/1us)=65536-10000=0xd8f0。
    进行代码展示:
javascript 复制代码
/*Main.c*/

#include<reg51.h>

#include"LCD1602.h"

#include"KeyScan.h"

#include"SetValue.h"

#include"Sounder.h"

#include"Ds18b20.h"

intIntCount=0,StopCount=0,StopMin=0;

ucharSec=0,Min=0,Hour=0,Date=1,month=1,SetMin=1,SetHour=0,NUM1,CountWeek;

intyear=2014,Qiehuan;

char KEY;

unsignedchar code dis_week[]={"SUN,MON,TUE,WED,THU,FRI,SAT"};

unsignedchar code para_month[13]={0,0,3,3,6,1,4,6,2,5,0,3,5};      //星期月参变数

ucharcode Timetable1[]="  CurrentTime  ";

ucharcode Datetable1[]="  CurrentDate  ";

ucharcode Settable1[]=" Set RingTime ";

ucharcode CurrentTime[]="   00:00:00    ";

ucharcode SetTime[]="     00:00      ";

ucharcode CurrentDate[]="  2014-01-01   ";

ucharcode CurrentTemp[]=" Temperature  ";

ucharcode Temptable[]="     00.0'C     ";

ucharcode Stopwatch[]="    StopWatch   ";

ucharcode Stoptable[]="    000.00s     ";

bitalarm;

/*定时器0、1初始化*/

voidTimer_Init(void)

{  

       TMOD=0x11;                 /*定时器T0、T1初始化为方式1*/

       TL0=0xb0;                                 /*装入定时初值,在主频12MHZ下,定时50ms*/

       TH0=0x3c;

    TL1=0xf0;                                  /*装入定时初值,在主频12MHZ下,定时10ms*/

       TH1=0xd8;

       ET0=1;                      /*开启定时器T0中断*/

       ET1=1;                      /*开启定时器T1中断*/

       TR0=1;                                      /*启动定时器T0定时*/

TR1=0;

}

/*外部中断0初始化*/

voidInt_Init(void)

{  

       IT0=1;    //设置下降沿触发方式

       EX0=1;     //开放外部中断0

     IT1=1;    //设置下降沿触发方式

       EX1=1;     //开放外部中断0

       EA=1;

}

/*外部中断0中断函数*/

voidInt0_int(void) interrupt 0

{

alarm=!alarm;          //闹钟的开关切换

}

/*中断号1是定时器T0中断*/

voidTimer0_int(void) interrupt 1

{

    EA=0;                                        //关中断

     TL0=0xb7;                                 //重装定时初值,在主频12MHZ下,定时50ms

       TH0=0x3c;                                 //修正量为7个机器周期

       IntCount++;

       if(IntCount==20)

{

       Sec++;                    //秒位加一

       IntCount=0;

}

       EA=1;                                 //开中断

}

/*外部中断1的中断函数,设定秒表的起始*/

voidInt1_int(void) interrupt 2

{

   TR1=!TR1;

   if(TR1)

   StopMin=0;

}

/*中断号3是定时器T1中断*/

 void Timer1_int(void) interrupt 3

{

    EA=0;                          /*关中断*/

    TL1=0xf0;                    /*装入定时初值,在主频12MHZ下,定时10ms*/

       TH1=0xd8;

       StopMin++;

       EA=1;

}

/*按键处理程序*/

voidKey_Process(uchar Qkeynum)

{

 if(Qkeynum=='1')  //按键一用于显示切换

 {

     Qiehuan++;

       if(Qiehuan==1)          //显示当前时间

   {

     uchar num;

     write_com(0x01);  //显示清屏

      write_com(0x80);

        for(num=0;num<15;num++)

       {

        write_data(Timetable1[num]);

        delay(5);

       }

     write_com(0x80+0x40);

        for(num=0;num<16;num++)

       {

       write_data(CurrentTime[num]);

       delay(5);

       }    

   }

    else if(Qiehuan==2)       //显示当前日期

    {

        uchar num;

     write_com(0x01);//显示清屏

      write_com(0x80);

        for(num=0;num<15;num++)

{

       write_data(Datetable1[num]);

       delay(5);

       }

        write_com(0x80+0x40);

       for(num=0;num<16;num++)

       {

       write_data(CurrentDate[num]);

       delay(5);

       }    

   }

    else if(Qiehuan==3)              //显示当前定时

    {

        uchar num;

     write_com(0x01);//显示清屏

      write_com(0x80);

        for(num=0;num<15;num++)

       {

       write_data(Settable1[num]);

       delay(5);

       }

       write_com(0x80+0x40);

       for(num=0;num<16;num++)

       {

       write_data(SetTime[num]);

       delay(5);

       }    

   }

    else if(Qiehuan==4)              //显示当前温度

     {

        uchar num;

     write_com(0x01);//显示清屏

      write_com(0x80);

        for(num=0;num<15;num++)

       {

        write_data(CurrentTemp[num]);

       delay(5);

       }

       write_com(0x80+0x40);

       for(num=0;num<16;num++)

       {

       write_data(Temptable[num]);

       delay(5);

              }    

   }

   else if(Qiehuan==5)        //显示秒表

     {

        uchar num;

     write_com(0x01);//显示清屏

      write_com(0x80);

        for(num=0;num<15;num++)

       {

       write_data(Stopwatch[num]);

       delay(5);

       }

       write_com(0x80+0x40);

       for(num=0;num<16;num++)

       {

       write_data(Stoptable[num]);

       delay(5);

       }    

       Qiehuan=0;

   }

}

elseif(Qkeynum=='2')

{

  Sec++;

  if(Sec>59)

  Sec=0;

}

if(Qkeynum=='3')

{

  Min++;

  if(Min>59)

  Min=0;

}

if(Qkeynum=='4')

{

  Hour++;

  {

  if(Hour>23)

  Hour=0;

  }

}

if(Qkeynum=='5')

{

  Date++;

 if(month==1||month==3||month==5||month==7||month==8||month==10||month==12)

if (Date>31)

       {

       Date=1;

       }                                 //大月31天

   else if(month==4||month==6||month==9||month==11)        

   if (Date>30) {Date=1;}                               //小月30天

}

if(Qkeynum=='6')

{

  month++;

  if (month>12)

 month=1;

}

if(Qkeynum=='7')

{

  year++;

  if(year>2099)

  year=2000;

}

if(Qkeynum=='8')

{

SetMin++;

if(SetMin>59)

SetMin=0;

}

if(Qkeynum=='9')

{

SetHour++;

if(SetHour>23)

SetHour=0;

}

if(Qkeynum=='A') //设置贪睡功能

{

SetMin=SetMin+2;

if(SetMin>60)

{

SetMin=SetMin-60;

SetHour++;

if(SetHour>23)

SetHour=0;

 }

}

}

 

/*闰年的计算*/

bitleap_year()

{

       bit leap;

       if((year%4==0&&year%100!=0)||year%400==0)//闰年的条件

              leap=1;

       else

              leap=0;

       return leap;

}

 

/*星期的自动运算和处理*/

/*基姆拉尔森计算公式*/

unsignedchar week_proc()

{     unsigned char num_leap;      

       unsigned char c;

       unsigned char week;

       //from  http://blog.csdn.net/wylloong/article/details/35818451

       num_leap=year/4-year/100+year/400;//自00年起到year所经历的闰年数

       if( leap_year()&& month<=2 )       //既是闰年且是1月和2月     

              c=5;

       else

              c=6;

       week=(year+para_month[month]+Date+num_leap+c+4)%7;//计算对应的星期

       return week;

}

/*显示当前时间*/

voidShowTime(void)   

{

  write_com(0x80+0x40);

  write_Dec(4,Hour);

  write_Dec(7,Min);

  write_Dec(10,Sec);

}

/*显示闹钟设定时间*/

voidShowSetTime(void)

{

  write_com(0x80+0x40);

  write_Dec(5,SetHour);

  write_Dec(8,SetMin);

}

/*显示日期*/

voidShowDate(void)

{      

  write_com(0x80+0x40);

  write_Dec(2,20);

  write_Dec(4,(year%100));

  write_Dec(7,month);

  write_Dec(10,Date);

}

/*显示秒表时间*/

voidShowStopWatch()

{

  write_com(0x80+0x40+4);

  write_data(StopMin/10000+0x30);

  write_data((StopMin%10000)/1000+0x30);

  write_data((StopMin%1000)/100+0x30);

  write_com(0x80+0x40+8);

  write_data((StopMin%100)/10+0x30);

  write_data((StopMin%10)+0x30);

 }

voidCalcu_time(void)

{

               if(Sec>59)

               {

                 Sec=0;

              Min++;  

                 if(Min>59)

                   {

                       Min=0;

                    Hour++;

                      if(Hour>23)  //*24H从零点开始

                        {

                        Date++;

                        Hour=0;

                if(month==1||month==3||month==5||month==7||month==8||month==10||month==12)

                              if (Date>31)

                              {Date=1;month++;

                              }                              //大月31天

                        else if(month==4||month==6||month==9||month==11)        

                              if (Date>30) {Date=1;month++;}                                 //小月30天

                        else if (month==2)                

                              {

                               if(leap_year())                                                   //闰年的条件

                               {

                               if (Date>29)

                                {

                                   Date=1;month++;

                                   }

                               }                     //闰年2月为29天

                               else

                                {

                                    if (Date>28) {Date=1;month++;}            //平年2月为28天

                                    }          

                                    }    //if (month==2)                  

                               else if (month>12)

                               {

                               month=1;

                               year++;

                                if(year>2099)

                                   year=2000;

                               }   //if (month>12)

                            }     //if(Hour==24)

                      }           //if(Min==60)

               }  //if(Sec==60)

      

       if(Qiehuan==2)        //日期和星期同时显示

       {

        CountWeek=week_proc();

        write_com(0x80+0x40+13);

        write_data(dis_week[4*CountWeek]);

        write_data(dis_week[4*CountWeek+1]);

        write_data(dis_week[4*CountWeek+2]);

       }

 

       if((Min==SetMin)&&(Hour==SetHour))     //闹钟定时到达后响铃               

              {

              if (alarm)

           BellRing();

              else Sounder=0;

              }

          else Sounder=0;           //扬声器关闭

       if (alarm&&(Qiehuan==1))     //闹钟开关标志和时间同时显示

        {

        write_com(0x80+0x40);

        write_data('O');

        write_data('N');

        }

        else if((!alarm)&&(Qiehuan==1))

        {

        write_com(0x80+0x40);

        write_data('O');

        write_data('F');

        }

}

void main()

{

    LCD_init();       //LCD1602初始化

       Timer_Init();  //定时器初始化

    Int_Init();          //外部中断初始化

       Qiehuan=1;

    while(1)

    {

        Calcu_time();         //时间累计及显示

       if(Qiehuan==1)

        {

         ShowTime();  //在LCD1602上显示时间

        }

        else if(Qiehuan==2)

        {

        ShowDate();

        }          

        else if(Qiehuan==3)

         {

         ShowSetTime();

         }

        elseif(Qiehuan==4)

         {

         ds18b20Process();  //显示温度

         }

        else if (Qiehuan==0)

         {   

        ShowStopWatch();   //显示秒表

         }

         WholePoint();   //整点报时函数         

       KEY=keynum();        //按键检测及扫描

         if(KEY!=0)

              {    

              NUM1=coding(KEY);

              Key_Process(NUM1);

              }           

       }

}

/*LCD1602.c*/

#include"LCD1602.h"

#include"SetValue.h"

 

ucharcode Timetable[]="  CurrentTime  ";

ucharcode Datetable[]="  CurrentDate  ";

ucharcode Currenttable[]="   00:00:00    ";

 

voidLCD_init(void)

{    

    uchar num;

    rw=0;

       write_com(0x38);//显示模式设置

       write_com(0x0c); //开显示,不显示光标

       write_com(0x06); //地址指针加一,光标加一

       write_com(0x01);//显示清屏

       write_com(0x80);

 

       for(num=0;num<15;num++)

              {

              write_data(Timetable[num]);

              delay(5);

              }

       write_com(0x80+0x40);

       for(num=0;num<16;num++)

              {

              write_data(Currenttable[num]);

              delay(5);

              }

             

}

voiddelay(uint z)

{

       uint x,y;

       for(x=z;x>0;x--)

       for(y=110;y>0;y--);

}

/*写指令:RS=L,RW=L,D0~D7=指令码,E=高脉冲*/

voidwrite_com(unsigned char com)

{

       rs=0;

       lcden=0;

       P0=com;

       delay(5);

       lcden=1;

       delay(5);

       lcden=0; 

}

 

 /*写数据:RS=H,RW=L,D0~D7=指令码,E=高脉冲*/

voidwrite_data(unsigned char Data)

{

       rs=1;

       lcden=0;

       P0=Data;

       delay(5);

       lcden=1;

       delay(5);

       lcden=0; 

}

/*显示两位数,add为第几列,date为数据*/

voidwrite_Dec(uchar add,uchar date)

{

       uchar decade,unit;

       decade=date/10;

       unit=date%10;

       write_com(0x80+0x40+add);

       write_data(0x30+decade);

       write_data(0x30+unit);

}

/*KeyScan.c*/

#include<KeyScan.h>  

#include<reg51.h>

#include"SetValue.h"

#include"LCD1602.h"

unsigned char code a[]={0xFE,0xFD,0xFB,0xF7};

/*获得按键对应的键值*/

unsigned char coding(unsigned char m)  

{

    unsigned char k;

    switch(m)

    {

        case (0x18):k='1';break;

        case (0x28):k='2';break;

        case (0x48):k='3';break;

        case (0x88):k='4';break;

        case (0x14):k='5';break;

        case (0x24):k='6';break;

        case (0x44):k='7';break;

        case (0x84):k='8';break;

        case (0x12):k='9';break;

        case (0x22):k='A';break;

        case (0x42):k='9';break;

        case (0x82):k='C';break;

        case (0x11):k='*';break;

        case (0x21):k='0';break;

        case (0x41):k='#';break;

        case (0x81):k='D';break;

    }

    return(k);

}

 

//=====================按键检测并返回按键值==========================

unsigned char keynum(void)

{

    unsigned char row,col,i;

    P1=0xf0;   //低四位输出低电平

    if((P1&0xf0)!=0xf0)   //P1口输入值是否为0xf0,不是则有键按下

    {

       delay(10);        //延时按键消抖

        if((P1&0xf0)!=0xf0)

        {

            row=P1^0xf0;          //异或确定哪列有键按下

            i=0;

            P1=a[i];             //精确定位某列的按键

            while(i<4)

            {

                if((P1&0xf0)!=0xf0)

                {

                    col=~(P1&0xff);   //确定行线

                    break;            //已定位后提前退出  

                }

                else

                {

                    i++;

                    P1=a[i];

                }

            }

        }

        else

        {

            return 0;

        }

   

        while((P1&0xf0)!=0xf0);   //等待按键释放

 

        return (row|col);          //行线与列线组合后返回

    }

    else return 0;                  //无键按下时返回0

}

 

/*Sounder.c*/

#include"SetValue.h"

void Soundelay(uint z)

{

    uint x,y;

    for(x=z;x>0;x--)

        for(y=110;y>0;y--);

}

/*引脚取反产生高低电平使扬声器发声*/

void BellRing(void)

{

Sounder=!Sounder;

   Soundelay(50);

}

 

/*整点报时函数*/

void WholePoint(void)

{

if((Min==0)&&(Sec<3))

  BellRing();

else Sounder=0;

}

/*ds18b20.c*/

#include<reg51.h>

#include"LCD1602.h"

sbit DQ        = P2 ^4;  //ds1820data

void DSDelay(int num)//延时函数

{

    while(num--) ;

}

/*初始化ds18b20,参见datasheet初始化时序图*/

void Init_DS18B20(void)//

{

    unsigned char x=0;

    DQ = 1;    //DQ复位

    DSDelay(8);  //稍做延时

    DQ = 0;    //单片机将DQ拉低

    DSDelay(80); //精确延时 大于 480us

    DQ = 1;    //拉高总线

    DSDelay(14);

    x=DQ; //稍做延时后ds18b20发出存在脉冲,若x=0则初始化成功 x=1则初始化失败

    DSDelay(20);

}

 

/**********************************************************************/

unsigned char ReadOneChar(void)//读一个字节

{

    unsigned char i=0;

    unsigned char dat = 0;

    for (i=8;i>0;i--)

    {

    /*当总线控制器把数据线从高电平拉到低电平,读时序开始*/

    DQ = 0; // 给脉冲信号

    dat>>=1;

    DQ = 1; // 给脉冲信号

    if(DQ)//ds18b20通过拉高或拉低总线来传递1或0

    dat|=0x80;

    DSDelay(4);

    }

    return(dat);

}

 

/***********************************************************/

void WriteOneChar(unsigned char dat)//写一个字节

{

    unsigned char i=0;

    for (i=8; i>0; i--)

    {

    DQ = 0;   //数据线拉低再释放,写时序开始

    DQ = dat&0x01;  //写0时序或写1时序

    DSDelay(5);

    DQ = 1;

    dat>>=1;

    }

}

 

/*读取温度*/

unsigned int ReadTemperature(void)

 {

    unsigned char a=0;

    unsigned char b=0;

    unsigned int t=0;

    float tt=0;

    Init_DS18B20();

    WriteOneChar(0xCC); //跳过读序号列号的操作

    WriteOneChar(0x44); //启动温度转换

    Init_DS18B20();

    WriteOneChar(0xCC); //跳过读序号列号的操作

    WriteOneChar(0xBE); //读取温度寄存器

    a=ReadOneChar();  //读低8位

    b=ReadOneChar(); //读高8位

    t=b;

    t<<=8;

    t=t|a;

    tt=t*0.0625;//0.0625/LSB

    t= tt*10+0.5; //放大10倍输出并四舍五入

    return(t);

}

 

void ds18b20Process(void)

{

    unsigned int i=0;

    unsigned chara=0,b=0,c=0,f=0;

    i=ReadTemperature();//读温度并送显

    a=i/100;

    b=i/10-a*10;

    c=i-a*100-b*10;

   write_com(0x80+0x40+5);

    write_data(0x30+a);

    write_data(0x30+b);

    write_com(0x80+0x40+8);

    write_data(0x30+c);

}

 

/*SetValue.h*/

#ifndef _SetValue_H_

#define _SetValue_H_

#include<reg51.h>

#define uchar unsigned char

#define uint unsigned int

sbit rs=P2^0;

sbit rw=P2^1;

sbit lcden=P2^2;

sbit Sounder=P2^5;

extern uchar SetHour;

extern uchar SetMin;

extern int IntCount;

extern uchar Sec;

extern uchar Min;

extern uchar Hour;

extern uchar Date;

extern uchar month;

extern int year;

#endif

 

/*1602.h*/

#ifndef _LCD1602_H_

#define _LCD1602_H_

void LCD_init(void);

void delay(unsigned int z);

void write_com(unsigned char com);

void write_data(unsigned char Data);

void write_Dec(unsigned char add,unsigned char date);

#endif

 

/*Sounder.h*/

void BellRing(void);

void WholePoint(void);

 

/*KeyScan.h*/

#ifndef _KeyScan_H_

#define _KeyScan_H_

unsigned char coding(unsigned char m);

unsigned char keynum(void);

#endif
相关推荐
呼啦啦呼啦啦啦啦啦啦1 小时前
在win10环境部署opengauss数据库(包含各种可能遇到的问题解决)
数据库
m0_748230212 小时前
mysql约束和高级sql
数据库·sql·mysql
刘艳兵的学习博客2 小时前
刘艳兵-DBA046-ASSM表空间的全表扫描范围由哪些因素综合确定?
数据库·sql·oracle·刘艳兵
2401_857636392 小时前
实验室管理技术革新:Spring Boot系统
数据库·spring boot·后端
YONG823_API2 小时前
1688商品数据采集API的测试对接步骤分享(提供免费测试key)
开发语言·数据库·爬虫·python·数据挖掘
码上一元3 小时前
掌握 Spring 事务管理:深入理解 @Transactional 注解
数据库·spring
程序猿毕设源码分享网3 小时前
基于springboot停车场管理系统源码和论文
数据库·spring boot·后端
YiSLWLL3 小时前
Django+Nginx+uwsgi网站使用Channels+redis+daphne实现简单的多人在线聊天及消息存储功能
服务器·数据库·redis·python·nginx·django
.生产的驴3 小时前
Docker Seata分布式事务保护搭建 DB数据源版搭建 结合Nacos服务注册
数据库·分布式·后端·spring cloud·docker·容器·负载均衡