您的当前位置:首页51单片机时钟显示+温度显示

51单片机时钟显示+温度显示

2023-07-01 来源:小侦探旅游网


南 华 大 学 计算机技术综合课程

设计报告

学院: 湘南学院

题目: 单片机LCD时钟显示和温度测量 学生姓名: 专业班级: 指导老师: 教研室主任: 2012年6月29日 一、概述: 本次设计的LCD时钟显示和温度测量系统是由中央控制器、温度检测器、时钟系统、显示器及键盘部分组成。控制器采用单片机STC89C51RC,温度检测部分采用DS18B20温度传感器,时钟系统用时钟芯片DS1302,用LCD液晶1602作为显示器,用蜂鸣器构成闹铃模块。单片机通过时钟芯片DS1302获取时间数据,对数据处理后显示时间;温度传感器DS18B20采集温度信号送该给单片机处理;单片机再把时间数据和温度数据送液晶显示器1602显示;键盘是用来调整时间的。

二、方案设计及方案论证 1. 时钟显示和温度测量的总体设计思路 按照系统的设计功能要求,本时钟显示和温度测量系统的设计必须采用单片机软件系统实现,用单片机的自动控制能力配合按键控制,来控制时钟、温度的调整及显示。获得时钟显示和温度测量数据信息,单片机对其进行一系列的处理,最后通过液晶显示出来。

2.时钟显示和温度测量系统方案论证

2.1时钟系统方案选择

方案1:通过单片机内部的定时器/计数器,用软件实现,直接用单片机的定时器编程以实现时钟;

方案2:用专门的时钟芯片实现时钟的记时,再把时间数据送入单片机,由单片机控制显示。 虽然用软件实现时钟硬件线路简单,但是程序运行的每一步都需要时间,多一步或少一步程序

——仅供参考

都会影响记时的准确度,对定时器定时也不是十分准确,时钟精度很低,对于我们实现所需要的功能造成软件编程非常复杂。用专用时钟芯片硬件成本相对较高,但它的精度很高,软件编程很简单。综上所述,选择方案2。

本次设计采用具有涓细电流充电能力的低功耗实时时钟电路DS1302。它可以对年、月、日、周日、时、分、秒进行计时,且具有闰年补偿等多种功能。它采用主电源和备用电源双电源供电。它的工作电压范围2.0~5.5V,在2.2V时,小于300nA。它内部含有31个字节的静态RAM,可提供用户访问。?

DS1302可以对年、月、日、周日、时、分、秒进行计时,可以达到我们设计的基本的要求。内部的寄存器为我们调时,闹钟定时提供了寄存空间。备用用电源也实现了当系统断电后,时钟仍然可以保持。而且它是串行接口,与单片机通信所需要的接口少。 2.2 显示系统的方案比较 方案1:用数码管或LED显示。 方案2:用液晶1602显示。 方案3:用液晶12864显示。 时钟和温度的显示可以用数码管或LED,而且价格便宜。但是数码管的只能显示简单的设计的系统,与我们设计要求也不相符。有很多东西需要显示,还是用显示功能更好的液晶显示器比较好,它能显示更多的数据,用12864显示略想复杂,用1602液晶显示可以完全满足本次设计的数据显示要求。 2.3 温度系统方案选择 方案1:用热敏电阻等测温元件测出电压,再转换成对应的温度。需要比较多的外部元件支持,且硬件电路复杂,制作成本相对较高。 方案2:用DS18B20直接测温。DS18B20温度传感器是美国DALLAS半导体公司最新推出的一种改进型智能温度传感器,它能直接读出被测温度,并且可根据实际要求通过简单的编程实现9~12位的数字值读数方式。 经比较,我们选择方案2。 温度实现只能通过外部的温度传感器来实现。经上网查阅,DS18b20体积小,只有3只脚,电路接法简单。精度为0.5°C,也符合我们设计的要求。DS18B20也是我们通常使用的型号,因此温度传感器用DS18B20。 2.4 键盘控制方案选择

方案1:购买集成键盘,采用矩阵形式连接。 方案2:购买单个复位开关做成键盘。

虽然集成键盘美观,与单片机的接口少,但是它的成本比较高。单片机的IO口对于我们的设计绰绰有余。通常我们选用价格便宜单个复位开关做成键盘。

3.时钟显示和温度测量系统总体设计

初步确定设计系统由单片机主控模块、时钟模块、测温模块、闹铃模块、显示模块、键盘接口 ——仅供参考

模块共6个模块组成,电路系统框图如下图所示。

主 控 器 件STC89C51 1602 显示系统 DS18B20 温度采集系统 蜂鸣器 闹铃系统 DS1320 时间采集系统 键盘 控制系统 图1:电路系统框图 三、硬件电路单元设计 1、单片机最小系统设计 1.1复位电路: 复位电路有两种方式:上电复位和按钮复位,我们主要用按钮复位方式。如下图所示: 图2:复位电路 1.2 晶振电路 晶振电路选取条件:晶振12M,电容20pF到30pF,本电路选取20pF。晶振电路原理如下图:

图3:晶振电路 2、时钟系统设计 DS1302时钟电路晶振选用3.2678KHZ,原理图如下: 图4:基于ds1302时钟系统设计电路 3、温度系统设计 DS18B20温度传感DQ端接单片机P1.3口,电路原理图如下: 图5:基于DS18b20温度系统设计电路 4、液晶显示系统设计 1602液晶显示电路由1K电阻和10K的精密电阻组成,控制端接P1.0、P1.1、P1.2口,数据端接P2口,电路图如下: 图6:液晶1602显示电路

把15脚的10K电阻改成1K电阻

5、蜂鸣器系统设计

蜂鸣器闹铃电路由蜂鸣器跟1K限流电阻组成,电路图如下: 图7:蜂鸣器电路

6、电源模块

本系统采用USB供电,用发光二极管显示通电状况,电源电源电路如下: ——仅供参考

图8:电源电路

7、时间调节模块

本系统采用3个按键调节时间。S1是菜单键,选择要调节的位;S2按一下表示加一;S3按一下表示减一。电路如下:

图9:时钟调节电路

四、软件调试

1、程序流程图如下:

图10:程序流程图

2、程序见附录 五、硬件调试 1、原理图如下: 图11:总原理图 2、PCB图如下: 图12:PCB图 其中红色部分为跳线; 3、实物测试 板子制作完成后上电测试发现电源灯没有显示,用万用表测量之后发现精密电阻接线错误,导致VCC与GND直接连通。修正之后发现电源指示灯还是没有亮,测量USB两端电压为-4.88V,卸掉USB口,插上排针用杜邦线连接单片机的VCC和GND之后,电源灯能够点亮,液晶能够驱动,但是亮度不够。测量液晶两端的电压发现只有2.5V,用导线将液晶背光的VCC直接接上电源VCC后,液晶亮度正常。将程序下载之后经过几次调试和修改,最终实现了想要达到的效果。 六、总结与体会 在开始本次课程设计前花费了十多天的时间认真学习了单片机,通过书本和视频的学习,对单片机有了进一步的掌握并学会C51编程的一些方法。这对于完成本次课程设计提供了很大的帮助。

从这次的课程设计中,我真真正正的意识到,在写程序时,逻辑思维要清晰,要有耐心。同时要巧妙的利用C语言的可移植性,这对写程序有很大的帮助。 参考文献: 1.郭天祥单片机学习板配套资料 2.《51单片机C语言教程》 郭天祥主编 电子工业出版社 3.《C语言程序设计》 尹业安,白燕主编 国防科技大学出版社

4.《单片机原理与应用及C51程序设计》 谢维成 杨加国 主编 清华大学出版社 程序如下:

#include #include

#define uchar unsigned char #define uint unsigned int

#define LCDIO P2

sbit DQ=P1^3;//ds18b20与单片机连接口 sbit rs=P1^0; ——仅供参考

sbit rd=P1^1; sbit lcden=P1^2;

sbit acc0=ACC^0; //移位时的第0位 sbit acc7=ACC^7; //移位时用的第7位

uchar second,minute,hour,day,month,year,week,count=0; uchar ReadValue,num,time; uint tvalue;//温度值 uchar tflag;

uchar code table[]={\" 2012-06-29 FIR\uchar code table1[]={\" 08:45:00 000.0C\

uchar code table2[]= \"FRISATSUNMONTUEWESTUR\"; uchar data disdata[5]; sbit DATA=P0^1; //时钟数据接口 sbit RST=P0^2; sbit SCLK=P0^0; sbit menu=P3^5; //菜单 sbit add=P3^6; //加一 sbit dec=P3^7; //减一 void delay(uint z) {

uint x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); }

void delay1(uint z) {

for(;z>0;z--); }

void write_com(uchar com) //lcd写命令指令:RS=L,RW=L, D0~D7=指令码,E=高脉冲 {

rs=0; rd=0; lcden=0; P2=com; delay(5); lcden=1; delay(5); lcden=0; }

void write_date(uchar date) //lcd写数据指令:RS=H,RW=L, D0~D7=数据,E=高脉冲 {

rs=1; rd=0; lcden=0; P2=date; delay(5);

——仅供参考

lcden=1; delay(5); lcden=0; }

void init() {

uchar num; lcden=0;

write_com(0x38); //显示模式设置,默认为0x38,不用变。

write_com(0x0c); //显示功能设置0x0f为开显示,显示光标,光标闪烁;0x0c为开显示,不显光标,光标不闪

write_com(0x06); //设置光标状态默认0x06,为读一个字符光标加1. write_com(0x01); //显示清屏,将上次的内容清除,默认为0x01. write_com(0x80); //设置初始化数据指针,是在读指令的操作里进行的 delay(5); write_com(0x80); //初始化显示时间(table table1中的内容) for(num=0;num<15;num++) { write_date(table[num]); delay(5); } write_com(0x80+0x40); for(num=0;num<15;num++) { write_date(table1[num]); delay(5); } }

void Write1302(uchar dat) //写8bit数据 {

uchar i; SCLK=0; //拉低SCLK,为脉冲上升沿写入数据做好准备 delay1(2); //稍微等待,使硬件做好准备 for(i=0;i<8;i++) //连续写8个二进制位数据 {

DATA=dat&0x01; //取出dat的第0位数据写入1302 delay(2); //稍微等待,使硬件做好准备 SCLK=1; //上升沿写入数据

delay1(2); //稍微等待,使硬件做好准备 SCLK=0; //重新拉低SCLK,形成脉冲

dat>>=1; //将dat的各数据位右移1位,准备写入下一个数据位 } }

void WriteSet1302(uchar Cmd,uchar dat) //向cmd相应地址中写入一个字节的数据 {

RST=0; //禁止数据传递 ——仅供参考

SCLK=0; //确保写数居前SCLK被拉低 RST=1; //启动数据传输

delay1(2); //稍微等待,使硬件做好准备 Write1302(Cmd); //写入命令字 Write1302(dat); //写数据

SCLK=1; //将时钟电平置于已知状态 RST=0; //禁止数据传递 }

uchar Read1302(void) //读8bit数据 {

uchar i,dat;

delay(2); //稍微等待,使硬件做好准备 for(i=0;i<8;i++) //连续读8个二进制位数据 {

dat>>=1; //将dat的各数据位右移1位,因为先读出的是字节的最低位 if(DATA==1) //如果读出的数据是1 dat|=0x80; //将1取出,写在dat的最高位 SCLK=1; //将SCLK置于高电平,为下降沿读出 delay1(2); //稍微等待 SCLK=0; //拉低SCLK,形成脉冲下降沿 delay1(2); //稍微等待 }

return dat; //将读出的数据返回 }

uchar ReadSet1302(uchar Cmd) //从cmd相应地址中读一个字节的数据 {

uchar dat; RST=0; //拉低RST SCLK=0; //确保写数居前SCLK被拉低 RST=1; //启动数据传输 Write1302(Cmd); //写入命令字 dat=Read1302(); //读出数据 SCLK=1; //将时钟电平置于已知状态 RST=0; //禁止数据传递 return dat; //将读出的数据返回 }

void Init_DS1302(void) {

WriteSet1302(0x8E,0x00); //根据写状态寄存器命令字,写入不保护指令 WriteSet1302(0x80,((0/10)<<4|(0%10))); //根据写秒寄存器命令字,写入秒的初始值 WriteSet1302(0x82,((45/10)<<4|(45%10))); //根据写分寄存器命令字,写入分的初始值

WriteSet1302(0x84,((8/10)<<4|(8%10))); //根据写小时寄存器命令字,写入小时的初始值 WriteSet1302(0x86,((29/10)<<4|(29%10))); //根据写日寄存器命令字,写入日的初始值 WriteSet1302(0x88,((6/10)<<4|(6%10))); //根据写月寄存器命令字,写入月的初始值 WriteSet1302(0x8c,((12/10)<<4|(12%10))); // 根据写年寄存器命令字,写入年的初始值

WriteSet1302(0x8a,((1/10)<<4|(1%10))); // 根据写星期寄存器命令字,写入星期的初始值 ——仅供参考

}

void DisplaySecond(uchar x) //显示秒 {

uchar i,j; i=x/10; j=x%10;

write_com(0xc7); write_date(0x30+i); write_date(0x30+j); }

void DisplayMinute(uchar x) {

uchar i,j; i=x/10; j=x%10; write_com(0xc4); write_date(0x30+i); write_date(0x30+j); }

void DisplayHour(uchar x) {

uchar i,j; i=x/10; j=x%10; write_com(0xc1); write_date(0x30+i); write_date(0x30+j); }

void DisplayDay(uchar x) {

uchar i,j; i=x/10;

j=x%10; write_com(0x89); write_date(0x30+i); write_date(0x30+j); }

void DisplayMonth(uchar x) {

uchar i,j; i=x/10; j=x%10;

write_com(0x86); write_date(0x30+i); write_date(0x30+j); }

——仅供参考

//显示分 //显示小时 //显示日期、天 //显示日期、月

void DisplayYear(uchar x) //显示日期、年 {

uchar i,j; i=x/10; j=x%10;

write_com(0x83); write_date(0x30+i); write_date(0x30+j); }

void DisplayWeek(uchar x) //显示星期 { uchar i; x=x*3; write_com(0x8c); for(i=0;i<3;i++) {

write_date(table2[x]); x++; } }

void read_date(void) {

ReadValue = ReadSet1302(0x81); second=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //从DS1302读取时间秒 ReadValue = ReadSet1302(0x83); minute=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //从DS1302读取时间分 ReadValue = ReadSet1302(0x85); hour=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //从DS1302读取时间小时 ReadValue = ReadSet1302(0x87); day=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //从DS1302读取日期 日 ReadValue = ReadSet1302(0x89); month=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //从DS1302读取日期 月 ReadValue = ReadSet1302(0x8d); year=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //从DS1302读取日期 年 ReadValue=ReadSet1302(0x8b); week=ReadValue&0x07; //从DS1302读星期 DisplaySecond(second); // 显示秒、分、小时、天、月、年、星期

DisplayMinute(minute); DisplayHour(hour); DisplayDay(day); DisplayMonth(month); DisplayYear(year); DisplayWeek(week); }

void turn_val(char newval,uchar flag,uchar newaddr,uchar s1num) //刷新时间(将改变的时间显示到LCD上) ——仅供参考

{

newval=ReadSet1302(newaddr); //读取当前时间 newval=((newval&0x70)>>4)*10+(newval&0x0f); //将bcd码转换成十进制

if(flag) //判断是加一还是减一 {

newval++;

switch(s1num)

{ case 1: if(newval>99) newval=0; DisplayYear(newval); break;

case 2: if(newval>12) newval=1; DisplayMonth(newval); break; case 3: if(newval>31) newval=1; DisplayDay(newval); break; case 4: if(newval>6) newval=0; DisplayWeek(newval); break; case 5: if(newval>23) newval=0; DisplayHour(newval); break; case 6: if(newval>59) newval=0; DisplayMinute(newval); break; case 7: if(newval>59) newval=0; DisplaySecond(newval); break; default:break; } } else { newval--; switch(s1num) { case 1: if(newval==0) newval=99; DisplayYear(newval); break;

case 2: if(newval==0) newval=12; DisplayMonth(newval); break;

case 3: if(newval==0) newval=31; DisplayDay(newval); break;

case 4: if(newval<0) newval=6; ——仅供参考

DisplayWeek(newval); break;

case 5: if(newval<0) newval=23; DisplayHour(newval); break;

case 6: if(newval<0) newval=59; DisplayMinute(newval); break;

case 7: if(newval<0) newval=59; DisplaySecond(newval); break; default:break; } } WriteSet1302((newaddr-1),((newval/10)<<4)|(newval%10)); 寄存器 }

void key_scan(void) //键盘扫描程序 { uchar miao,s1num=0; if(menu==0) {

delay(5); if(menu==0) {

while(!menu); s1num++; while(1) { if(menu==0) { delay(5); if(menu==0) { while(!menu); s1num++; } } rd=0;

miao=ReadSet1302(0x81); second=miao;

WriteSet1302(0x80,miao|0x80); write_com(0x0f);//光标闪射 if(s1num==1)

{ year=ReadSet1302(0x8d); write_com(0x80+4); //年光标 if(add==0) ——仅供参考

//将新数据写入

{

delay(3); if(add==0)

{ while(!add);

turn_val(year,1,0x8d,1); } }

if(dec==0) {

delay(3); if(dec==0) { while(!dec); turn_val(year,0,0x8d,1); } } } if(s1num==2) { month=ReadSet1302(0x89); write_com(0x80+7); //月光标 if(add==0) { delay(3); if(add==0) { while(!add); turn_val(month,1,0x89,2); } } if(dec==0) { delay(3); if(dec==0) { while(!dec); turn_val(month,0,0x89,2); } } }

if(s1num==3)

{ day=ReadSet1302(0x87); write_com(0x80+10);//日光标 if(add==0) {

delay(3); if(add==0) { while(!add);

turn_val(day,1,0x87,3); ——仅供参考

} }

if(dec==0) {

delay(3); if(dec==0) { while(!dec);

turn_val(day,0,0x87,3); //写入日寄存器 } } }

if(s1num==4) { week=ReadSet1302(0x8b); write_com(0x80+14); //星期光标 if(add==0) { delay(3); if(add==0) { while(!add); turn_val(week,1,0x8b,4); } } if(dec==0) { delay(3); if(dec==0) { while(!dec); turn_val(week,0,0x8b,4); } } } if(s1num==5) { hour=ReadSet1302(0x85); write_com(0x80+0x40+2); //时光标 if(add==0) { delay(3); if(add==0) { while(!add);

turn_val(hour,1,0x85,5); } }

if(dec==0) {

delay(3); if(dec==0) ——仅供参考

{ while(!dec);

turn_val(hour,0,0x85,5); } } }

if(s1num==6) //调时间分

{ minute=ReadSet1302(0x83); write_com(0x80+0x40+5); if(add==0) {

delay(5); if(add==0) { while(!add); turn_val(minute,1,0x83,6); } } if(dec==0) { delay(3); if(dec==0) { while(!dec); turn_val(minute,0,0x83,6); } } } if(s1num==7)//调时间秒 { second=ReadSet1302(0x81); write_com(0x80+0x40+8);//秒光标 if(add==0) { delay(3); if(add==0) { while(!add); if(second==0x60) second=0x00; turn_val(second,1,0x81,7); } }

if(dec==0) {

delay(3); if(dec==0) { while(!dec);

turn_val(second,0,0x81,7); } }

——仅供参考

//写入分寄存器//写入分寄存器

}

if(s1num==8)

{ miao=ReadSet1302(0x81); second=miao;

WriteSet1302(0x80,second&0x7f); s1num=0;//s1num清零

write_com(0x0c);//光标不闪烁 break; } } } } }

void delay_18B20(unsigned int i)//延时1微秒 {

while(i--); }

void ds1820rst() //ds1820复位 { unsigned char x=0; DQ = 1; //DQ复位 delay_18B20(4); //延时 DQ = 0; //DQ拉低 delay_18B20(100); //精确延时大于480us DQ = 1; //拉高 delay_18B20(40); }

uchar ds1820rd()///读数据 { unsigned char i=0; unsigned char dat = 0; for (i=8;i>0;i--) { DQ = 0; //给脉冲信号 dat>>=1; DQ = 1; //给脉冲信号 if(DQ) dat|=0x80; delay_18B20(10); }

return(dat); }

void ds1820wr(uchar wdata)//写数据 {unsigned char i=0; for (i=8; i>0; i--) { DQ = 0;

DQ = wdata&0x01; delay_18B20(10); DQ = 1;

——仅供参考

wdata>>=1; } }

read_temp()//读取温度值并转换 {uchar a,b;

ds1820rst();

ds1820wr(0xcc);//跳过读序列号 ds1820wr(0x44);//启动温度转换 ds1820rst();

ds1820wr(0xcc);//跳过读序列号 ds1820wr(0xbe);//读取温度 a=ds1820rd(); b=ds1820rd(); tvalue=b; tvalue<<=8; tvalue=tvalue|a; if(tvalue<0x0fff) tflag=0; else {tvalue=~tvalue+1; tflag=1; }

tvalue=tvalue*(0.625);//温度值扩大10倍,精确到1位小数 return(tvalue); }

void ds1820disp()//温度值显示 { uchar flagdat; disdata[0]=tvalue/1000+0x30;//百位数 disdata[1]=tvalue%1000/100+0x30;//十位数 disdata[2]=tvalue%100/10+0x30;//个位数 disdata[3]=tvalue%10+0x30;//小数位 if(tflag==0) flagdat=0x20;//正温度不显示符号 else flagdat=0x2d;//负温度显示负号:- if(disdata[0]==0x30) {disdata[0]=0x20;//如果百位为0,不显示 if(disdata[1]==0x30)

{disdata[1]=0x20;//如果百位为0,十位为0也不显示 } }

write_com(0xc9);

write_date(flagdat);//显示符号位 write_com(0xca);

write_date(disdata[0]);//显示百位 write_com(0xcb); ——仅供参考

write_date(disdata[1]);//显示十位 write_com(0xcc);

write_date(disdata[2]);//显示个位 write_com(0xcd);

write_date(0x2e);//显示小数点 write_com(0xce);

write_date(disdata[3]);//显示小数位 write_com(0xcf); write_date('C'); }

void main() {

init(); //初始化显示 Init_DS1302(); //将1302初始化 read_temp(); //读取温度 ds1820disp();//显示 while(1) {

read_date(); key_scan(); read_temp();//读取温度 ds1820disp();//显示 } }

——仅供参考

因篇幅问题不能全部显示,请点此查看更多更全内容