您好,欢迎来到九壹网。
搜索
您的当前位置:首页基于51单片机设计的带有测温功能的电子时钟汇总

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

来源:九壹网
 、

职业技能训练之 电子技术课程设计报告

学 院 设计题目 班 级 姓 名 学 号 指导教师 时 间

电子与信息学院 基于51单片机设计的带有测温功能的电子时钟 XXX XXX XXX XXX 2012年06月25日

电子课程设计报告

一、 设计要求

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

三、 硬件设计方案

四、 软件设计方案

五、 总结

六、 参考资料

目录

2

电子课程设计报告

一、 设计要求

用51单片机设计带温度显示的电子时钟,具体要求如下: 1、利用DS1302时钟芯片实现时钟功能模块。

2、时钟要求可以调节时间:年、月、日、时、分、秒。 3、利用LCD1602显示。

4、利用DS18B20芯片实现温度功能模块。 5、利用按键完成各项功能。

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

1、 总体方案:

用STCC51单片机作为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、 目的及意义

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

3

电子课程设计报告

三、硬件设计方案

1、原理图

2、PCB图

4

电子课程设计报告

3、各功能模块分析

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

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

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

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

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

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

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

5

电子课程设计报告

(二)、LCD1602显示模块

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

显示模式设置:

显示开关及功能设置:

6

电子课程设计报告

数据指针设置:

写时序:

7

电子课程设计报告

注: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输入驱动置高电平来启动所有的数

8

电子课程设计报告

据传送。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时,写保护位防止对任一寄存器的写操作。

控制字节:

注:

9

电子课程设计报告

控制字节的最高有效位(位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

10

电子课程设计报告

VDD:电源 存储器:

控制字节:

Skip ROM [CCh]:允许总线控制器不用提供 位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 内允许数据线拉到高电平。

11

电子课程设计报告

主机要生成一个写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{

12

电子课程设计报告

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 \\\\\ //设定行星期显示数

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;

13

,用以显示星期数

电子课程设计报告

days=(today.day+1+2*today.month+3*(today.month+1)/5+today.year+today.year/4-today.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); }

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

gotoxy(1,add); }

14

display_data(number);

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=0;

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

//当mode_num为7时,退出修改模式

15

电子课程设计报告

于判断是否为修改模式 }

if(mode_num!=0) {

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

delay(5); //延时

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

//为修改模式时,加减键的处理子程序

}

write_com(0x0c); TR0=1;

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

16

电子课程设计报告

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)

{

17

电子课程设计报告

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();

}

18

电子课程设计报告

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时,要重新赋

19

电子课程设计报告

值,置为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存放个位

20

电子课程设计报告

tmpchange(); //启动温度转换 wendu=tmp(); //读取温度值 A1=wendu/10; //求A1值 A2=wendu%10; //求A2值 gotoxy(2,10);

display_data(A1); //显示A1值

display_string(\".\"); //用小数点分开个位和小数

write_date(int_to_char[A2]); //向lcd写数据,并且注意要转换A2数据类型 }

void timer0(void) interrupt 1 {

TR0=0;

TL0=(TIMER0_COUNT & 0X00FF);//设置Timer0低八位数值 TH0=(TIMER0_COUNT >> 8);//设置Timer0高八位数值 TR0=1;

count++;

if(count==30) display_temp(); //延时,当count=30时,显示温度,并延时 if(count==200) //当count自加到200时,开始判断此时时、分、秒的值,并进行转换

{

count=0; now.second++;

if(now.second==60) //当秒达到60时,向分进位,且秒数置零 {

now.second=0; now.minute++;

if(now.minute==60) //当分达到60时,向时进位,且分数置零 {

now.minute=0;

21

电子课程设计报告

now.hour++; if(now.hour==24)

//当时达到24时,向天进位,且时数置

{

now.hour=0;

today.day++;

if(today.day>monthday(today.year,today.month))

//判断天数,当超过本月最大天数时,向月份进位,且天数置一

{

today.day=1; today.month++;

if(today.month==13) //判断月份,当月份超过12时,向年份进位,月份置一 {

today.month=1; today.year++; }

write_riqi(4,today.month); //在位置4处,向lcd写月份

}

write_riqi(7,today.day); //在位置7处,向lcd写天数

gotoxy(1,14); display_week();

}

}

write_time(4,now.minute);

//在位置4处,向lcd写分数

}

write_time(1,now.hour); //在位置1处,向lcd写时数

22

电子课程设计报告

}

}

write_time(7,now.second);

//在位置7处,向lcd写秒数

void main() {

SYSTEMTIME CurrentTime;

LcdInitiate(); //初始化lcd

timer0_initialize(); //初始化内部定时器

Initial_DS1302(); //初始化DS1302

DateToStr(&CurrentTime); //从DS1302读取日期 TimeToStr(&CurrentTime); //从DS1302读取时间 gotoxy(1,1);

display_string(CurrentTime.DateString); //显示日期

gotoxy(1,10);

display_week(); //显示周几 gotoxy(2,1);

display_string(CurrentTime.TimeString); //显示时间 gotoxy(2,10);

display_string(\"00.0C\"); while(1) keyscan(); }

(二)DS1302时钟模块 #include #include

23

//显示温度

电子课程设计报告

#ifndef _REAL_TIMER_DS1302_2003_7_21_ #define _REAL_TIMER_DS1302_2003_7_21_

sbit DS1302_CLK = P1^6; //实时时钟时钟线引脚 sbit DS1302_IO = P1^7; //实时时钟数据线引脚 sbit DS1302_RST = P1^5; //实时时钟复位线引脚 sbit ACC0 = ACC^0; sbit ACC7 = ACC^7;

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[9]; unsigned char TimeString[9];

//定义的时间类型

}SYSTEMTIME;

#define AM(X) X

#define PM(X) (X+12) #define DS1302_SECOND 0x80 0x80放的的秒数 #define DS1302_MINUTE #define DS1302_HOUR #define DS1302_WEEK #define DS1302_DAY

// 转成24小时制

//时钟芯片的寄存器位置,存放时间,如

0x82 0x84 0x8A 0x86

24

电子课程设计报告

#define DS1302_MONTH #define DS1302_YEAR #define DS1302_RAM(X)

0x88 0x8C

(0xC0+(X)*2)

//用于计算 DS1302_RAM 地

址的宏 RAM命令控制字地址:0xC0-0xFD

void DS1302InputByte(unsigned char d) //实时时钟写入一字节(内部函数) {

unsigned char i; ACC = d; for(i=8; i>0; i--) {

DS1302_IO = ACC0; //将IO线上的数据存放在ACC中

DS1302_CLK = 1;

//特别注意,DS1302写一数据时,

CLK要产生一负脉冲

DS1302_CLK = 0;

//每次移位一位

ACC = ACC >> 1;

} }

unsigned char DS1302OutputByte(void) //实时时钟读取一字节(内部函数) {

unsigned char i; for(i=8; i>0; i--) {

ACC = ACC >>1;

//将前一个下降沿读出的数据右

移一位从而读出该次读出的数放入ACC7 ACC7 = DS1302_IO;

DS1302_CLK = 1;

//SCLK下降沿读出DS1302的数

DS1302_CLK = 0; }

25

电子课程设计报告

return(ACC); }

void Write1302(unsigned char ucAddr, unsigned char ucDa) //其中Addr为 DS1302地址, ucData为要写的数据 {

DS1302_RST = 0; 沿,CLK为低电平 DS1302_CLK = 0; DS1302_RST = 1;

DS1302InputByte(ucAddr); // 地址,命令 DS1302InputByte(ucDa); DS1302_CLK = 1; DS1302_RST = 0; }

unsigned char Read1302(unsigned char ucAddr) {

unsigned char ucData; DS1302_RST = 0; DS1302_CLK = 0; DS1302_RST = 1;

DS1302InputByte(ucAddr|0x01); // 地址,命令

ucData = DS1302OutputByte(); // 读1字节数据 DS1302_CLK = 1;

DS1302_RST = 0; return(ucData); }

//读取DS1302某地址的数据

// 写1字节数据

//写数据时,要注意RST产生一上升

26

电子课程设计报告

void DS1302_SetProtect(bit flag) //是否写保护 {

if(flag)

Write1302(0x8E,0x80); //0x8E 控制字节地址,bit7=WP WP=1 禁止数据

写入DS1302 }

void DS1302_SetTime(unsigned char Address, unsigned char Value) // 设置时间函数 {

DS1302_SetProtect(0);

Write1302(Address, ((Value/10)<<4 | (Value%10))); //读写数据时要特别注意数else

Write1302(0x8E,0x00); //WP=0 允许写入DS1302

据的处理,要写的数据要进行拼凑,将Value数据的十位取出,放入高四位,Value的个位放在低四位上 }

void DS1302_GetTime(SYSTEMTIME *Time) {

unsigned char ReadValue; //用来传递读取1302的数据

ReadValue = Read1302(DS1302_SECOND);//读取秒数,存放在ReadValue变量中

Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //

ReadValue数据的第4、5、6位取出,实际上就是秒数的十位,但是以二进制的形式表示,所以要进行移位,向右移四位,在乘以10,在加上ReadValue变量中秒数的,将十六进制的数化成十进制数表示,并放在Time->Second中

ReadValue = Read1302(DS1302_MINUTE);

Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);//同上 ReadValue = Read1302(DS1302_HOUR);

27

电子课程设计报告

}

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) 序 {

//年份转换成可供lcd显示的字段子程

Time->DateString[0] = Time->Year/10 + '0'; //Time->DateString[0]中存放的就

是要显示年的十位

Time->DateString[1] = Time->Year%10 + '0'; //Time->DateString[1]中存放的就

是要显示年的个位

Time->DateString[2] = '-';

//年和月份之间用-分开

Time->DateString[3] = Time->Month/10 + '0'; //Time->DateString[3]中存放的

就是要显示月份的十位

Time->DateString[4] = Time->Month%10 + '0'; //Time->DateString[4]中存放的

就是要显示月份的个位

Time->DateString[5] = '-';

Time->DateString[6] = Time->Day/10 + '0'; //Time->DateString[6]中存放的就

是要显示日的十位

Time->DateString[7] = Time->Day%10 + '0'; //Time->DateString[7]中存放的

就是要显示日的个位

Time->DateString[8] = '\\0';

//记住,结束时要添加'\\0',以作为

28

电子课程设计报告

结束标志 }

void TimeToStr(SYSTEMTIME *Time) { }

void Initial_DS1302(void) {

unsigned char Second=Read1302(DS1302_SECOND);

if(Second&0x80) //BIT7=CH CH=0振荡器允许工作,CH=1,

//DS1302的初始化程序

Time->TimeString[0] = Time->Hour/10 + '0'; Time->TimeString[1] = Time->Hour%10 + '0'; Time->TimeString[2] = ':';

Time->TimeString[3] = Time->Minute/10 + '0'; Time->TimeString[4] = Time->Minute%10 + '0'; Time->TimeString[5] = ':';

Time->TimeString[6] = Time->Second/10 + '0'; Time->TimeString[7] = Time->Second%10 + '0'; Time->TimeString[8] = '\\0';

//时间转换成可供lcd显示的字段子程序

振荡器停止 } #endif

(三)DS18B20温度模块 #include

29

DS1302_SetTime(DS1302_SECOND,0);

电子课程设计报告

#include #define uchar unsigned char #define uint unsigned int

uint temp; // 温度变量

sbit DS=P1^0; //定义数据传输接口 void delay1(uchar count) //延时 {

while(count>0) count--; }

void reset(void) //送初值和初始命令 { DS=0; delay1(100); DS=1; delay1(4); delay1(200); }

bit read_bit(void) //读一比特,特别注意对于18b20,要读数据时,一定要产生DS正脉冲,然后才传送数据 {

bit temp; DS=0; _nop_(); DS=1; _nop_(); temp=DS; delay1(200); return temp;

30

电子课程设计报告

}

uchar read_byte(void) //读一字节的数据 {

uchar i,byte=0; bit j;

for(i=0;i<8;i++) {

byte=_cror_(byte ,1); //将byte数据向右循环一位,每次都取出最高位 j=read_bit(); //设定j为读取进来的一比特数

if(j==0) byte=byte|0x00; //当j=0时,即读取一字节数完毕,取出次字节数

else byte=byte|0x80; }

return byte; }

void write_byte(uchar command) //写一字节到18b20 { uchar i;

for(i=0;i<8;i++) //用i来设定一字节的数据 {

if((command & 0x01)==0) 脉冲 { DS=0; delay1(8); DS=1; _nop_();

31

//每次取出最高位

//返回字节数

//取出最低位为零时,DS要产生一个负脉冲

电子课程设计报告

} else { DS=0; _nop_(); DS=1; delay1(8); }

command=_cror_(command,1); } }

void tmpchange(void) //启动18b20 { reset();

write_byte(0xcc); //直接向18b20发送温度变换命令 write_byte(0x44); //启动18b20进行温度转换 }

uint tmp() //获取温度 { float tt; uchar a,b; reset();

write_byte(0xcc); //直接向18b20发送温度变换命令 write_byte(0xbe); //读取温度寄存器的温度值 a=read_byte();//读低八位 b=read_byte();//读高八位 temp=b;

//否则产生一上升沿

32

电子课程设计报告

temp<<=8; //因为18b20处理接受到的数据要进行处理,先将高八位左移4位

temp=temp|a; //在和低八位相或,整合取得的数据 tt=temp*0.0625; temp=tt*10+0.5; return temp; }

(四)LCD1602显示模块

#include //包含单片机寄存器的头文件 #include //包含_nop_()函数定义的头文件 #define uchar unsigned char #define uint unsigned int sbit rs=P2^0;//寄存器选择信号 sbit rw=P2^1;

//读写控制信号线

sbit lcden=P2^2; //使能信号线

sbit BF=P0^7; //忙碌标志位,将BF位定义为P0.7引脚 #define lcd_mode 0x38 #define display_cursor 0x0f #define undisplay_cursor 0x0c

char code int_to_char[]={'0','1','2','3','4','5','6','7','8','9'};

//******************************************************** void delay(uint x) { }

//******************************************************** unsigned char BusyTest(void)

33

//延时程序

uint a,b; for(a=x;a>0;a--)

for(b=10;b>0;b--);

电子课程设计报告

{

bit result;

rs=0; //根据规定,RS为低电平,RW为高电平时,可以读状态

rw=1;

lcden=1; //E=1,才允许读写 _nop_(); //空操作 _nop_(); _nop_();

_nop_(); //空操作四个机器周期,给硬件反应时间 result=BF; //将忙碌标志电平赋给result

lcden=0;

return result; }

/***************************************************** 函数功能:将模式设置指令或显示地址写入液晶模块 入口参数:dictate

***************************************************/ void WriteInstruction (unsigned char dictate) {

while(BusyTest()==1); //如果忙就等待

rs=0; //根据规定,RS和R/W同时为低电平时,可以

写入指令

rw=0;

lcden=0; //E置低电平(根据表8-6,写指令时,E为高

脉冲,

// 就是让E从0到1发生正跳变,所以应先

置\"0\"

_nop_();

_nop_(); //空操作两个机器周期,给硬件反应时间

34

电子课程设计报告

P0=dictate; //将数据送入P0口,即写入指令或地址 _nop_(); _nop_(); _nop_();

_nop_(); //空操作四个机器周期,给硬件反应时间 lcden=1; //E置高电平 _nop_(); _nop_(); _nop_();

_nop_(); //空操作四个机器周期,给硬件反应时间 lcden=0; //当E由高电平跳变成低电平时,液晶模块开

始执行命令 }

void write_com(uchar com) //写命令 { }

void WriteAddress(unsigned char x) {

WriteInstruction(x|0x80); //显示位置的确定方法规定为\"80H+地址码x\" }

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

35

P0=com; rs=0;

//写命令,记住时序问题

//rs要为低电平

//使lcd使能端产生一个正脉冲

lcden=0; delay(10); lcden=1; delay(10); lcden=0;

电子课程设计报告

void write_date(unsigned char y) //写数据 {

{

while(BusyTest()==1);

rs=1; //RS为高电平,RW为低电平时,可以写入数据 rw=0;

lcden=0; //E置低电平(根据表8-6,写指令时,E为高脉冲,

P0=y; //将数据送入P0口,即将数据写入液晶模块

_nop_(); _nop_();

_nop_();

_nop_(); //空操作四个机器周期,给硬件反应时间

lcden=1; //E置高电平 _nop_(); _nop_(); _nop_();

_nop_(); //空操作四个机器周期,给硬件反应时间

lcden=0; //当E由高电平跳变成低电平时,液晶模块开始执行

命令 } }

//******************************************************** void LcdInitiate(void) {

delay(15); //延时15ms,首次写指令时应给LCD一段较长的反应时间

WriteInstruction(0x38); //显示模式设置:16×2显示,5×7点阵,8位数据接口

delay(5); //延时5ms WriteInstruction(0x38);

36

电子课程设计报告

}

delay(5);

WriteInstruction(0x38); delay(5);

WriteInstruction(0x0d); //显示模式设置:显示开,有光标,光标闪烁 delay(5);

WriteInstruction(0x0c); //显示模式设置:光标右移,字符不移 delay(5);

WriteInstruction(0x01); //清屏幕指令,将以前的显示内容清除 delay(5);

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

void display_string(char *p) //写字符串 {

while(*p) {

write_date(*p); delay(20); p++;

} }

//******************************************************** void gotoxy(uchar x,uchar y) //设置移动坐标 {

if(x==1)

write_com(0x80+y); else

write_com(0xc0+y);

37

电子课程设计报告

}

//******************************************************** void display_data(char number) //显示数据 {

uchar x,y;

x=number/10; //将取得的数据转换成十进制 y=number%10;

write_date(int_to_char[x]); write_date(int_to_char[y]); }

(五)键盘扫描部分

char fun,flag,flag2,adjust,wei,adt; sbit rr=P3^7; sbit ll=P3^6;

sbit anjian=P1^6;

sbit right=P3^3; sbit left=P3^2; sbit up=P3^1; sbit down=P3^0; sbit menu=P3^3; sbit ok=P3^2;

void keyscan() { uchar i;

ll=0;

//按键功能标志位 //按键灯光提醒 //调整时间按键程序 38

电子课程设计报告

if(fun==1) //功能1,时钟情况下 {

if(ok==0) //判断OK键摁下 { delay_MS(5); //去抖动 if(ok==0) {

}

} ll=1; rr=0;

while(!ok); //松手监测 anjian=0; //提示灯点亮 delay_MS(240); anjian=1;

flag++; //按键计数

ds1302_write_byte(write_wp,0x00); //DS1302暂停ds1302_write_byte(write_sec,0x80); ds1302_write_byte(write_wp,0x80);

if(flag==2) //调整时间完毕,退出调整 { wei=0; adt=0;

ds1302_write_time(); flag=0;

} adjust=1;

//允许调整时间标志位

lcd1602_write_command(0x0d); lcd1602_write_command(0x80+0x03);

39

电子课程设计报告

if(adjust==1) {

if(right==0) //判断左移按键 { } ……

delay_MS(5); if(right==0) { }

while(!right); anjian=0; delay_MS(240); anjian=1;

wei++; //移动位置标志 adt=1; if(wei==1) { } ……

if(wei==8) //循环移动 { }

wei=1;

lcd1602_write_command(0x0d); lcd1602_write_command(0x80+0x03); lcd1602_write_command(0x0d); //显示光标 lcd1602_write_command(0x80+0x03);

if(adt==1)

40

电子课程设计报告

{ if(up==0) //时间加1

……

{

ll=1;

}

delay_MS(5); if(up==0) { while(!up); anjian=0; delay_MS(240); anjian=1; if(wei==1) { time1[1]=time1[1]+1;

if(time1[1]==80) { time1[1]=0;

}

ds1302_write_time(); //写入调整后的时间 ds1302_read_time(); //显示调整后的时间 for(i=1;i<8;i++) TempData[i]=time1[i]; change(); display();

lcd1602_write_command(0x0d); lcd1602_write_command(0x80+0x03);

}

41

电子课程设计报告

}

}

void keyscan1() //主功能键的扫描 { ll=0; if(menu==0) { delay_MS(5); if(menu==0) { while(!menu); anjian=0; delay_MS(240); anjian=1; fun++; if(fun==1) { flag=1; flag2=1; EA=0; shizhong(); delay_MS(1000); flag=0;

} if(fun==2) {

flag=1;

42

电子课程设计报告

}

}

}

}

flag2=1; wendu(); delay_MS(1000); EA=1; flag2=0; fun=0;

ll=1;

五、总结

通过课程设计,锻炼了进行课题制作的能力,从画原理图,元器件的选用,电路的设计,阅读芯片资料,程序的编写,调试中,进一步掌握单片机技术,提高编程能力。并且激发学习的兴趣,理论与实际相结合,为进一步自主研究打下基础。

在整个过程中,PCB图的绘画以及程序的编写这两方面需很大的耐心。同时,要求仔细研究,严格按照时序图编写。另外,在程序编写上,如何将温度和时钟通过一键进行功能的切换也是一个难点。在做到此项功能之前,必须要求各个功能模块直接划分清晰,程序流程及逻辑合理。要求各个模块之间不会产生干扰。完成后,可以提高程序结构度,熟练标志位的正确使用,进一步提高逻辑能力。

对于综合性的程序,使用多个头文件的方式,能有效提高编程时的清晰度,逻辑性。也便于迅速准确查找问题所在。因此,又可提高头文件的使用能力,熟悉头文件的功能及意义。

我在编写此课题的程序时,先将各个功能模块划分清楚,确定各个头文件。然后更具芯片说明书,先将各个芯片基本功能的子程序写完。其中包括初始化,

43

电子课程设计报告

读写字节,写命令等程序。然后将各个模块功能进行整合。

先将DS1302读出的时间数据,转换为字符,送LCD1602显示,从而完成基本时钟功能。程序完成后,烧入芯片,测试程序,如果能按照预设格式显示出时间,并且时间走动,说明时钟和显示模块正常。如果未能达到预设效果,检查程序,反复调试,直至达到效果,方可继续。建议检查DS1302读写时序部分的程序,LCD1602初始化部分程序。

然后开始编写按键功能部分,按要求完成各个功能。注意各个标志位的使用,功能的切换,按键调时后,LCD1602的及时显示,按键之间的互相屏蔽。可通过标志位允许后续按键的功能,调时时,写入后,立即读取显示,即可完成人机对话。

接着,写DS18B20部分的程序,用定时器中断,定时控制温度读取。读取温度转换成字符,送LCD1602显示。实现温度功能。此处应注意中断的开启和关闭。不然会影响温度读取。我在读取子程序中先关闭中断,退出子程序前重新开启中断。

再来完成时钟和温度功能的拓展部分,整点报时和高温报警。原理很简单,关键在于,报警监测程序加在哪里。一开始我加在了读取时间和温度的程序部分,然后发现如果这样,那么在LCD1602显示该数据之前就产生了报警,从而产生了时差,所以,我将报警程序加在了显示时进行判断。

紧接着就改完成两个功能间切换的程序,通过一个fun标志位,切换功能模块。设置两个标志允许位。当切换至时钟时,关闭温度允许,切换后,则反之。 最后添加开机画面,直接LCD1602显示。按下功能键后进入程序。完成所有部分。

整个程序部分并不困难,有一定的单片机基础的话,很容易的能写完所有的程序。难点在于完成代码后的调试,查错,需要很强的耐心。

七、 参考资料

1、DS1302芯片说明书(中文版) 2、DS18B20芯片说明书(中文版) 3、LCD1602芯片说明书(中文版)

44

电子课程设计报告

4、郭天祥《十天学会单片机》

45

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

Copyright © 2019- 91gzw.com 版权所有 湘ICP备2023023988号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务