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

embedded/2024/11/25 17:46:50/

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

设计一个基于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。
    进行代码展示:
/*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;     //开放外部中断0IT1=1;    //设置下降沿触发方式EX1=1;     //开放外部中断0EA=1;}/*外部中断0中断函数*/voidInt0_int(void) interrupt 0{alarm=!alarm;          //闹钟的开关切换}/*中断号1是定时器T0中断*/voidTimer0_int(void) interrupt 1{EA=0;                                        //关中断TL0=0xb7;                                 //重装定时初值,在主频12MHZ下,定时50msTH0=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;elseleap=0;return leap;}/*星期的自动运算和处理*//*基姆拉尔森计算公式*/unsignedchar week_proc(){     unsigned char num_leap;      unsigned char c;unsigned char week;//from  http://blog.csdn.net/wylloong/article/details/35818451num_leap=year/4-year/100+year/400;//自00年起到year所经历的闰年数if( leap_year()&& month<=2 )       //既是闰年且是1月和2月     c=5;elsec=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;  //ds1820datavoid 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); //精确延时 大于 480usDQ = 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或0dat|=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/LSBt= 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 intsbit 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

http://www.ppmy.cn/embedded/140443.html

相关文章

Scala全文单词统计

一&#xff1a;方法 package test5 import java.io.PrintWriter import scala.io.Source //可变的Map import scala.collection.mutable object test5_1 {def main(args: Array[String]): Unit { //1.读入文件val content Source.fromFile("1.txt").mkString // …

python中的base64使用小笑话

在使用base64的时候将本地的图片转换为base64 代码如下&#xff0c;代码绝对正确 import base64 def image_to_data_uri(image_path):with open(image_path, rb) as image_file:image_data base64.b64encode(image_file.read()).decode(utf-8)file_extension image_path.sp…

代码随想录1016-Day16

目录 530.二叉搜索树的最小绝对差501.二叉搜索树中的众数105.从中序与前序遍历序列构造二叉树总结 收获 530.二叉搜索树的最小绝对差 文章链接:代码随想录 题目链接&#xff1a;题目 思路&#xff1a;用中序遍历遍历一遍 BST 的所有节点得到有序结果&#xff0c;然后在遍历过程…

STM32的中断(什么是外部中断和其他中断以及中断号是什么)

一、什么是EXTI 和NVIC EXTI&#xff08;External Interrupt/Event Controller&#xff09;EXTI 是外部中断/事件控制器&#xff0c;它负责处理外部信号变化&#xff0c;并将信号传递给中断控制器&#xff08;如 NVIC&#xff09;。主要负责以下功能&#xff1a; 外部事件检测…

[UE5学习] 一、使用源代码安装UE5.4

一、简介 本文介绍了如何使用源代码安装编译UE5.4&#xff0c;并且新建简单的项目&#xff0c;打包成安卓平台下的apk安装包。 二、使用源代码安装UE5.4 注意事项&#xff1a; 请保证可以全程流畅地科学上网。请保证C盘具有充足的空间。请保证接下来安装下载的visual studi…

golang学习-切片

切片并不是数组或数组指针&#xff0c;它通过内部指针和相关属性引用数组片段&#xff0c;以实现变长方案。“ slice 并不是真正意义上的动态数组&#xff0c;而是一个引用类型。slice 总是指向一个底层 array&#xff0c;slice 的声明也可以像array 一样&#xff0c;只是不需要…

C++结构型设计模式之使用抽象工厂来创建和配置桥接模式的例子

下面是一个使用抽象工厂模式来创建和配置桥接模式的示例&#xff0c;场景是创建不同操作系统的窗口&#xff08;Window&#xff09;及其对应的实现&#xff08;WindowImpl&#xff09;。我们将通过抽象工厂来创建不同操作系统下的窗口和实现。 代码示例 #include <iostrea…

第十章 JavaScript的应用

第十章JavaScript的应用 10.1 JavaScript概述 10.1.1 JavaScript简介 10.1.1.1 简单性 JavaScript 的语法相对简单&#xff0c;易于学习和使用。它不需要复杂的编译过程&#xff0c;开发者可以直接在浏览器中编写和调试代码。例如&#xff0c;变量声明可以不指定类型&#xff…