基于51单片机设计的带有测温功能的电子时钟汇总

基于51单片机设计的带有测温功能的电子时钟汇总
基于51单片机设计的带有测温功能的电子时钟汇总

职业技能训练之

电子技术课程设计报告

学院电子与信息学院

设计题目基于51单片机设计的带有测温功能的电子时钟班级XXX

姓名XXX

学号XXX

指导教师XXX

时间2012年06月25日

目录

一、设计要求

二、课程设计的方案、目的及意义

三、硬件设计方案

四、软件设计方案

五、总结

六、参考资料

一、设计要求

用51单片机设计带温度显示的电子时钟,具体要求如下:

1、利用DS1302时钟芯片实现时钟功能模块。

2、时钟要求可以调节时间:年、月、日、时、分、秒。

3、利用LCD1602显示。

4、利用DS18B20芯片实现温度功能模块。

5、利用按键完成各项功能。

二、课程设计方案、目的及意义

1、总体方案:

用STC89C51单片机作为CPU主控制器,DS1302时钟芯片提供准确时钟信号,DS18B20温度传感器采集温度信息,三个按键进行加减调整、功能切换作用,通过LCD1602对外多功能显示。

2、具体方案:

CPU控制所有模块,通过循环反复从DS1302中读取时钟信息,传送至LCD1602显示,得到基本时钟功能。当分为59,秒为56时开始,每隔一秒LED 灯点亮240毫秒,0分0秒时LED灯点亮700毫秒。从而实现整点光报时。

定时循环从DS18B20中读取温度信息,传送至LCD1602显示,得到基本温度计功能。当温度高于30度(包括30度)时,点亮红色LED灯,提醒当天为高温天气。低于0度时,点亮蓝色LED灯,提醒当天为冰冻天气。

键盘使用扫面方式,MENU键控制功能切换,完成时钟和温度间的转换。OK键控制时间调整与确定,UP、DOWN键调节时间,R、L 键选择调整对象。进入调整时,暂停DS1302数据读取,并将改变的时间数据写入DS1302,并送LCD1602显示,同时,启动LCD1602光标闪烁,确定调整对象,完成人机对话。退出调整时,停止写入数据,重新读取DS1302时钟信息。从而完善时钟功能。

3、目的及意义

可作为产品生产,作为居家的时钟显示与温度计。

三、硬件设计方案

1、原理图

2、PCB图

3、各功能模块分析

(一)、主控制器:STC单片机89C51

功能:程序存储器16K、RAM数字存储器1280、可直接通过串口下载程序,单一+5V电源供电,五个中断源的中断控制系统,片内振荡器和时钟产生电路,石英晶体和微调电容需要外接。

CPU:由运算和控制逻辑组成,同时还包括中断系统和部分外部特殊功能寄存器。

RAM:用以存放可以读写的数据,如运算的中间结果、最终结果以及欲显示的数据。

ROM:用以存放程序、一些原始数据和表格。

I/O口:四个8位并行I/O口,既可用作输入,也可用作输出。P0口做I/O 口需接上拉电阻。

T/C:两个定时/记数器,既可以工作在定时模式,也可以工作在记数模式。

(二)、LCD1602显示模块

介绍:工业字符型液晶,能够同时显示16x02即32个字符(16列2行)。管脚信息:

显示模式设置:

显示开关及功能设置:

数据指针设置:

写时序:

注:R3调节显示屏对比度

(三)、DS1302时钟芯片

管脚信息:

VCC:Vcc1为后备电源,VCC2为主电源。在主电源关闭的情况下,也能保持时钟的连续运行。DS1302由Vcc1或Vcc2两者中的较大者供电。当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电。当Vcc2小于Vcc1时,DS1302由Vcc1供电。

X1、X2:X1和X2是振荡源,外接32.768kHz晶振。

RST:RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数

据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据的传送手段。当RST 为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。上电运行时,在Vcc>2.0V之前,RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。I/O为串行数据输入输出端(双向),后面有详细说明。

SCLK:SCLK为时钟输入端。

日历寄存器:

注:

(1):小时寄存器的位7用于定义DS1302是运行于12小时模式还是24小时模式。当为高时,选择12小时模式。在12小时模式下,位5为低时为AM,高时为PM。在24小时模式下,位5是第二个10小时位。

(2)、秒寄存器的位7定义为时钟暂停标志。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;该位置0是,时钟开始运行。

(3)、控制寄存器的位7是写保护位,其他7位均置为0。在任何的对时钟和RAM的写操作之前,WP位必须为0。当WP位1时,写保护位防止对任一寄存器的写操作。

控制字节:

注:

控制字节的最高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入DS1302中,位6如果为0,则表示存取日历时钟数据,为1表示存取RAM数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1表示进行读操作,控制字节总是从最低位开始输出。

数据读写及时序:

在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。

(四)、DS18B20温度传感器

管脚信息:

GND:接地

DQ:数据I/O

VDD:电源

存储器:

控制字节:

Skip ROM [CCh]:允许总线控制器不用提供64 位ROM 编码就使用存储器操作命令,在单点总线情况下右以节省时间。

Convert T[44h]:开始温度转换。

Read Scratchpad[BEh]:读取暂存器和CRC 字节,知道9字节

时序:

读时间隙:

当从DS12B20读取数据时,主机生成读时间隙。当主机把数据线从逻辑高电平拉到逻辑低电平的时候,写时间隙开始。数据线必须保持至少1μs,从DS18B20 输出的数据在读时间隙的下降沿出现后15μs 内有效。因此,主机在读时间隙开始后必须停止把I/O 脚驱动为低电平15μs,以读取I/O 脚状态。在读时间隙的结尾,I/O 引脚将被外部上拉电阻拉到高电平。所有读时间隙必须最少60μs,包括两个读周期间至少1μs的恢复时间。

写时间隙:

当主机把数据线从逻辑高电平拉到逻辑低电平的时候,写时间隙开始。所有写时间隙必须最少持续60μs,包括两个写周期间至少1μs 的恢复时间。I/O 线电平变低后,DS18B20 在一个15μs 到60μs 的窗口内对I/O 线采样。如果线上是高电平,就是写1,如果线上是低电平,就是写0。

主机要生成一个写时间隙,必须把数据线拉到低电平然后释放,在写时间隙开始后的15μs 内允许数据线拉到高电平。

主机要生成一个写0 时间隙,必须把数据线拉到低电平并保持60μs。(六)、按键部分

原理:

键盘扫描,先将P3.6口置零,此时扫描S5~S8,如果P3.0~P3.3中有为0,则说明有对应键按下。然后将P3.7口置零,此时扫描S1~S4,如果P3.0~P3.3中有为0,则说明有对应键按下。

四、软件设计方案

部分程序代码

(一)主程序:

#include

#include"lcd1602.h" //包含头文件,直接引用显示文件

#include"ds18b20.h"

#include "DS1302.h"

#define uchar unsigned char

#define uint unsigned int

#define TIMER0_COUNT 0xEE11

sbit mode=P3^0; //设定修改位数

sbit plus=P3^1; //加键

sbit dec=P3^2; //减键

uchar count,s1num,timer0_tick,count=0;

typedef struct{

char hour;

char minute;

char second;

}time;

typedef struct{

uint year;

char month;

char day;

}date;

time now={11,20,7}; //显示时间初始值

date today={12,6,16};

char code dayofmonth[]={31,28,31,30,31,30,31,31,30,31,30,31}; //设定月份数组,用以判定12个月的最大值

char code weekday[7][10]={"Week1 ","Week2 ","Week3 ","Week4 ","Week5 ","Week6 ","Week7 "}; //设定行星期显示数

uchar monthday(uchar year,uchar month)

{

if(month==2 && year%4==0) //用以判定是否为润年,其2月有29天

return(29);

else

return(28); //非闰年时的该月份天数28

}

void display_week() //由年月日计算星期,用以显示星期数

{

char days;

days=(today.day+1+2*today.month+3*(today.month+1)/5+today.year+today.year/4-to day.year/100+today.year/400)%7;

display_string(&weekday[days][0]);

}

static void timer0_initialize(void) //timer0 initialize

{

EA=0; //设置不接受所有中断

timer0_tick=0;

TR0=0; //关闭Timer0

TMOD=0X01; //设置Timer0为模式2,16位工作模式

TL0=(TIMER0_COUNT & 0X00FF); //设置Timer0低八位数值

TH0=(TIMER0_COUNT >> 8); //设置Timer0高八位数值

PT1=1; //设置Timer0的优先级为最高

ET0=1; //设置接受Timer0的中断

TR0=1; //启动Timer0

EA=1; //设置系统接受中断

}

void write_time(uchar add,uchar number) //写时间

{

gotoxy(2,add);

display_data(number);

}

void write_riqi(uchar add,uchar number) //写日期

{

gotoxy(1,add);

display_data(number);

}

void keyscan() //按键扫描程序

{

uchar mode_num; //设定mode_num,来判断是哪一位要修改,当mode_num为零时,为非修改模式

if(mode==0) //"修改位"的选择

{

delay(5); //延时

if(mode==0) //当外部按键mode没按下一次时,都使mode_num自加,即mode_num表示为按键mode按下的次数

{ mode_num++;

while(!mode);

TR0=0;

write_com(0x0f);

}

if(mode_num==1) gotoxy(1,1); //当mode_num为1时,为年份改变位,光标移到(1,2)

if(mode_num==2) gotoxy(1,4); //当mode_num为2时,为月份改变位,光标移到(1,5)

if(mode_num==3) gotoxy(1,7); //当mode_num为3时,为日期改变位,光标移到(1,8)

if(mode_num==4) gotoxy(2,1); //当mode_num为4时,为小时改变位,光标移到(2,2)

if(mode_num==5) gotoxy(2,4); //当mode_num为5时,为分钟改变位,光标移到(2,5)

if(mode_num==6) gotoxy(2,7); //当mode_num为6时,为秒数改变位,光标移到(2,8)

if(mode_num==7) //当mode_num为7时,退出修改模式

{

mode_num=0; //非修改模式时,将mode_num置零,有助

于判断是否为修改模式

write_com(0x0c);

TR0=1;

}

}

if(mode_num!=0) //为修改模式时,加减键的处理子程序

{

if(plus==0) //当加键为零,即外部触发一次时,进行以下处理

{

delay(5); //延时

if(plus==0) //延时再次判断加键,防止抖动

{

if(mode_num==1) //当为年份改变时,年份自加一,并且显示修改后的日期

{

today.year++;

write_riqi(1,today.year); //因为年份主要是最后两位在改变,所以除以100来计算年份值,除以100求得的商值为十位

gotoxy(1,14);

display_week(); //因为改变日期都会改变周数,所以要重新显示

}

if(mode_num==2) //以下处理同上

{

today.month++;

if(today.month==13) today.month=1; //特别注意,月份不能超过12,当为13时,要将月份置1

write_riqi(4,today.month); //除以10来计算月份值,除以10求得的商值为十位

gotoxy(1,14);

display_week();

}

if(mode_num==3) //以下处理同上

{

today.day++;

if(today.day>monthday(today.year,today.month)) //特别要判断每个月份的最大值,不能超过此数,超过后要将天数置一

today.day=1;

write_riqi(7,today.day); //除以10来计算月份值,除以10求得的商值为十位

gotoxy(1,14);

display_week();

}

if(mode_num==4) //以下处理同上

{

now.hour++;

if(now.hour==24) now.hour=0; //小时不能超过24

write_time(1,now.hour);

}

if(mode_num==5) //以下处理同上

{

now.minute++;

if(now.minute==60) now.minute=0;

write_time(4,now.minute);

}

if(mode_num==6) //以下处理同上

{

now.second++;

if(now.second==60) now.second=0;

write_time(7,now.second);

}

}

}

if(dec==0) //减键的处理和加键处理处理相反{

delay(5);

if(dec==0)

{

if(mode_num==1)

{

today.year--;

write_riqi(1,today.year);

gotoxy(1,14);

display_week();

}

if(mode_num==2)

{

today.month--;

if(today.month==0) today.month=12; //要注意月份的最小值为1,当减为零时,要讲月份置为12

write_riqi(4,today.month); //除以10来计算月份值,除以10求得的商值为十位

gotoxy(1,14);

display_week();

}

if(mode_num==3)

{

today.day--;

if(today.day==0) //当天数减为0时,要重新赋值,为上一个月的最大值

today.day=monthday(today.year,today.month);

write_riqi(7,today.day); //除以10来计算月份值,除以10求得的商值为十位

gotoxy(1,14);

display_week();

}

if(mode_num==4)

{

now.hour--;

if(now.hour<0) now.hour=23; //当小时数小于0时,要重新赋

值,置为23

write_time(1,now.hour);

}

if(mode_num==5)

{

now.minute--;

if(now.minute<0) now.minute=59; //注意点同上

write_time(4,now.minute);

}

if(mode_num==6)

{

now.second--;

if(now.second<0) now.second=59; //注意点同上

write_time(7,now.second);

}

}

}

}

}

void display_temp() //显示温度子程序

{

uint wendu; //设置wendu变量来存放从18b20读取的温度uchar A1,A2; //A1用来存放温度值的十位,A2存放个位

相关主题
相关文档
最新文档