一、制作目的
- 熟练掌握键盘、显示、DS18B20温度传感器等各个模块;
- 训练系统综合的能力;
- 编写键盘、显示、DS18B20温度传感器综合程序。
二、开发环境
- Keil开发环境
- Proteus仿真环境。
三、制作内容用LCD、DS18B20、按键配合完成设计
- LCD显示,当前的室内温度及时间和日期(可使用DS1302时钟芯片);
- 用按键可调节日期和时间
四、实验步骤
-
进行方案分析该项实践要使用到LCD1604,DS18B20,DS1302这三种元器件,并且要使用按键与DS1302进行连接,从而实现通过按键调整DS1302的日期和时间。首先应该对各个部分功能能清晰显示,显示后再对各个部分进行衔接.
-
搭建仿真电路图
运用了AT89C51单片机,LCD1604,DS1302时钟芯片,三个独立按键和DS18B20芯片。 -
构思整体设计本组选择的是用LCD显示,当前的室内温度及时间和日期(其中使用了DS1302时钟芯片),并且用按键可调节日期和时间。首先搭建LCD1604的仿真电路,测试无误后搭建DS1302时钟芯片电路,并且添加三个独立按键,经调试无误,最后搭建DS18B20温度传感器电路,在LCD1604的后两行进行显示,进而完成整体设计。
-
绘制流程图
-
分模块调试
(1)首先检测LCD1604是否能正常运行
(2)LCD1604能正常显示,接下来对DS18B20温度传感器模块进行测试,是否能准确显示改变的温度值。(这里使用-10度测试)
(3)对按键进行检测,是否能对日期产生改变,功能键功能是按一下即可改变所要调的位置。例如一开始调秒,按一下功能键即可调分,可由秒分时日月周年循环切换,其他两个独立按键一个是对当前值+1一个是对当前值-1,我们这里测试能不能调到2000年与23小时时候。
-
综合调试能够正确显示所设定初值的DS1302时钟上的日期与时间,并且可以通过按键对日期和时间进行改变,DS18B20温度传感器也能在LCD1604上显示出当前温度值并且能够随着温度值的改变而在LCD1604上的显示改变。
五、实验程序与分析
- 程序
#include <reg51.h>#include <intrins.h>
//包含_nop_( )空函数指令的头文件
#define uchar unsigned char
#define uint unsigned int
sbit RS=P2^0; //LCD1604位变量
sbit RW=P2^1; //LCD1604位变量
sbit E=P2^2; //LCD1604位变量
sbit RST=P0^0;
//DS1302芯片复位,1—芯片的读/写使能,0—芯片复位并被禁止读/写
sbit SCLK=P0^1; //DS1302芯片同步串行时钟输入
sbit DS1302IO=P0^2; //DS1302芯片数据输入/输出
sbit DQ= P2^3; //定义DS18B20端口DQ
void lcd_initial(void); //LCD初始化函数
void check_busy(void); //检查忙标志函数
void write_command(uchar com); //写命令函数
void write_data(uchar dat); //写数据函数
void delay(uint); //延时函数
void DS1302_initial(); //DS1302初始化函数
void DS1302Write(uchar addr,uchar dat);
//对DS1302时钟写入命令,写入数据
void DS1302Read_Time();
//读取七个字节的时钟信号:秒分时日月周年
uchar DS1302Read(uchar addr);
//对DS1302时钟写入命令,读出数据
void xiangshi(); //将DS1302上的时钟显示在LCD1604上
void button(); //根据按键值进行操作
void DS18B20_init(void);//DS18B20的初始化
uchar read_bit(void); //读一位
uchar read_byte(void); //读一个字节
void write_bit(uchar temp);//写一位
void write_byte(uchar val);//写一个字节
void read_T(void); //读温度
void delay5(uchar n); //5us延时
void convert_T();
void display_T(void);
uchar Read_DS1302_data[]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
//DS1302秒分时日月周年读命令
uchar Write_DS1302_ADDR[]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
//DS1302秒分时日月周年写命令
uchar TIME[7]={0x00,0x00,0x12,0x05,0x02,0x02,0x20};
//DS1302初值
uchar digit=0;//功能键
uchar temp_data_l,temp_data_h;
uint temp_data;
uchar code LCDData[10]=
{0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};
uchar code ditab[16] =
{0x30,0x31,0x31,0x32,0x33,0x33,0x34,0x34,0x35,0x36,
0x36,0x37,0x38,0x38,0x39,0x39};
uchar code table2[16] ="Show Tempreture:";
uchar display[7] = {0x00,0x00,0x00,0x2e,0x00,0xdf,0x43};
void main(void) //主函数
{
lcd_initial( ); //调用对LCD初始化函数
DS1302_initial(); //调用对DS1302初始化函数
while(1){
DS1302Read_Time();//读取七个字节的时钟信号:秒分时日月周年
xiangshi();//将DS1302上的时钟显示在LCD1604上
button(); //根据按键值进行操作
read_T();
convert_T();
display_T(); }
delay(100); //延时
write_command(0x01);//写入清屏命令
delay(100); //延时 }
void button()//根据按键值进行操作
{
uchar n,i;
P1=0x07;//只看重前三位,后五位忽略
n=P1;
n=n&0x07;
if(n!=0x07){ //检测按键按bai下
delay(500); //去抖
if(n!=0x07); } //检测按键确实按下,进行按键处理
if(n==0x06){
digit++;} //由转换周改为转换年(例子)
if(digit==7){
digit=0;} //轮了一圈又回来
if(n==0x05){
for(i=0;i<7;i++){
if(digit==i){
if((TIME[i]&0x0f)<9){
TIME[i]=TIME[i]+0x01;}
else{TIME[i]=TIME[i]+0x10-0x09;}
DS1302_initial(); }}} //按下第二个建加1
if(n==0x03){
for(i=0;i<7;i++){
if(digit==i){
if((TIME[i]&0x0f)<10&&(TIME[i]&0x0f)!=0){
TIME[i]=TIME[i]-0x01;}
else{TIME[i]=TIME[i]+0x09-0x10;}
DS1302_initial(); }}} //按下第三个建减1
void delay(uint j)//1ms延时子程序
{
uchar i=250;
for(;j>0;j--)
{
while(--i);
i=249;
while(--i);
i=250; }}
void delay5(uchar n){
do
{
_nop_();
_nop_();
_nop_();
n--;
}
while(n);}
void xiangshi() //将DS1302上的时钟显示在
LCD1604上{
button();
write_command(0x80);
write_data(0x30+2);
write_data(0x30+0);
write_data(0x30+TIME[6]/16); //年
write_data(0x30+(TIME[6]&0x0f));
write_data('-');
write_data(0x30+TIME[4]/16); //月
write_data(0x30+(TIME[4]&0x0f));
write_data('-');
write_data(0x30+TIME[3]/16); //日
write_data(0x30+(TIME[3]&0x0f)); write_command(0x8d); //星期
write_data(0x30+(TIME[5]&0x07));
write_command(0xc0);
write_data(0x30+TIME[2]/16); //小时
write_data(0x30+(TIME[2]&0x0f));
write_data(':');
write_data(0x30+TIME[1]/16); //分
write_data(0x30+(TIME[1]&0x0f));
write_data(':');
write_data(0x30+TIME[0]/16); //秒
write_data(0x30+(TIME[0]&0x0f));}``
void convert_T()
{
uchar temp;
if((temp_data_h&0xf0)==0xf0)//判断是不是负数
{
temp_data_l=~temp_data_l;//如果是负数,取反
if(temp_data_l==0xff) //如果低八位全为1 {
temp_data_l=temp_data_l+0x01;
temp_data_h=~temp_data_h;
temp_data_h=temp_data_h+0x01; //高八位加1
}
else
{
temp_data_l=temp_data_l+0x01; //取反后需要加1
temp_data_h=~temp_data_h; }
display[4]=ditab[temp_data_l&0x0f];//查表得小数位的值
temp=((temp_data_l&0xf0)>>4)|((temp_data_h&0x0f)<<4);
display[0]=0x2d;
display[1]=LCDData[(temp%100)/10];
display[2]=LCDData[(temp%100)%10]; }
else //如果不是负数
{
display[4]=ditab[temp_data_l&0x0f];//查表得小数位的值
temp=((temp_data_l&0xf0)>>4)|((temp_data_h&0x0f)<<4);
//个位数值
display[0]=LCDData[temp/100]; //百位
display[1]=LCDData[(temp%100)/10]; //十位
display[2]=LCDData[(temp%100)%10]; //个位
}}
void display_T(void){
uchar i;
write_command(0x90);
for(i=0;i<16;i++)
{
write_data(table2[i]);
}
write_command(0xd0);
for(i=0;i<7;i++) {
write_data(display[i]); } }
void DS1302_initial() //DS1302初始化函数
{
uchar n;
DS1302Write(0x8e,0x00);
//禁止写保护(正常工作时不能改变DS1302里的数据,就要进行写保护,就像家里要锁上一把锁)
for(n=0;n<7;n++){//写入七个字节时钟信号:秒分时日月周年
DS1302Write(Write_DS1302_ADDR[n],TIME[n]);
}
DS1302Write(0x8e,0x80); //打开写保护功能
}
void DS1302Read_Time()
//读取七个字节的时钟信号:秒分时日月周年
{
uchar n;
for(n=0;n<7;n++){
TIME[n]=DS1302Read(Read_DS1302_data[n]);}}
``````c
void DS1302Write(uchar addr,uchar dat)
//对DS1302时钟写入命令,写入数据
{
uchar n;
RST=0;//禁止读写
_nop_();
SCLK=0; //输入时钟置为低电平
_nop_();
RST=1; //允许读写
_nop_();
for(n=0;n<8;n++){//开始传送8位地址命令
DS1302IO=addr&0x01; //数据从最低位开始传送
addr>>=1;
SCLK=1; //时钟拉高,写入一位数据(上升沿写入)
_nop_();
SCLK=0; //拉低,写入完毕
_nop_(); }
for(n=0;n<8;n++){//写入八位数据
DS1302IO=dat&0x01; //数据从最低位开始传送
dat>>=1;
SCLK=1;
//时钟拉高,写入一位数据(上升沿写入)
_nop_();
SCLK=0; //拉低,写入完毕
_nop_();
}
RST=0; //写入数据完毕
_nop_();}
``````c
uchar DS1302Read(uchar addr) //对DS1302时钟写入命令,读出数据
{
uchar n,dat,dat1;
RST=0; //禁止读写
_nop_();
SCLK=0; //输入时钟置为低电平
_nop_();
RST=1; //允许读写
_nop_();
for(n=0;n<8;n++){//开始传送8位地址命令
DS1302IO=addr&0x01; //数据从最低位开始传送
addr>>=1;
SCLK=1; //时钟拉高,写入一位数据(上升沿写入)
_nop_();
SCLK=0; //拉低,写入完毕
_nop_(); }
_nop_();
for(n=0;n<8;n++){ //读取八位数据
dat1=DS1302IO;
dat=(dat>>1)|(dat1<<7);
SCLK=1;
_nop_();
SCLK=0; //DS1302下降沿,放置数据
_nop_(); }
RST=0; //写入数据完毕
_nop_();
SCLK=1; //DS1302复位的稳定时间,具体为什么不大清楚
_nop_();
DS1302IO=0;
_nop_();
DS1302IO=1;
_nop_();
return dat;}
void DS18B20_init(void) //DS18B20的初始化
{
DQ =0;
delay5(120);
//拉低1-Wire总线超过480us来发送复位脉冲,释放总线进入接收模式
DQ =1;
//总线释放后,5KΩ左右的上拉电阻将1-Wire总线拉至高电平
delay5(16); //等待15-60us
delay5(80);
//DS18B20内部总线拉低60-240us来实现发送一个存在脉冲
}
uchar read_bit(void)
//DS18B20读一个位(每个读时段最小必须有60us的持续时间和并且独立的写时段至少有1us的恢复时间)
{
DQ=0;//主设备拉低总线
_nop_();//空操作用于恢复时间
_nop_();
DQ=1; //主设备释放总线
delay5(2);//采样有效时间为15us
return(DQ); }
uchar read_byte(void)//DS18B20读一个字节
{
uchar i,temp; temp=0;
for(i=0;i<8;i++) {
temp>>=1;
if(read_bit()) {
temp|=0x80;
delay5(11);//每个读时段最小必须有60us的持续时间
}
delay5(6); }
return(temp);}
void write_bit(uchar temp)//写一位
{
DQ=0;
if(temp==1)//写1的时候,不需要等待,直接拉高主设备总线
DQ=1;
delay5(12);//写0的时候,至少拉低60us
DQ=1; }
void write_byte(uchar val)//写一个字节
{
uchar i,temp;
for(i=0;i<8;i++) {
temp=val>>i;//从最低位开始一位一位写进去
temp=temp&0x01;
write_bit(temp);
delay5(5); }}
void read_T(void)//读温度
{
DS18B20_init(); //DS18B20的初始化
write_byte(0xCC); //跳过读序号列号的操作(跳过ROM)
write_byte(0x44); //启动温度转换
delay5(500);
DS18B20_init(); //DS18B20的初始化
write_byte(0xCC); //跳过读序号列号的操作
write_byte(0xBE); //读取温度寄存器
temp_data_l= read_byte(); //温度低8位
temp_data_h = read_byte(); //温度高8位
}
void check_busy(void)
{ //检查忙标志函数
uchar dt;
do
{
dt=0xff;
E=0;
RS=0;
RW=1;
E=1;
dt=P3; }while(dt&0x80);
E=0;}
void write_command(uchar com) { //写命令函数
check_busy();
E=0;
RS=0;
RW=0;
P3=com;
E=1;
_nop_( );
E=0;
delay(1);}
void write_data(uchar dat)//写数据函数
{
check_busy();
E=0;
RS=1;
RW=0;
P3=dat; E=1;
_nop_();
E=0;
delay(1);
}
void LCD_initial(void) //液晶显示器初始化函数
{
write_command(0x38);
//写入命令0x38:8位两行显示,5×7点阵字符
write_command(0x0c);
//写入命令0x0C:开整体显示,光标关,无黑块
write_command(0x06);
//写入命令0x06:光标右移
write_command(0x01);
//写入命令0x01:清屏
delay(1);}
- 实验结果的分析与总结,有无改进方案?
实验结果与分析:基本功能能够实现,但是在对DS1302时钟芯片进行调值时,比如24小时后应该归零,但是却变成了25,而且在减法的时候,一旦从0再减,就会变成乱码,这些是有缺漏的。改进方案:改进按键检测与按键进入的子程序中的程序,从而能够实现24小时后归零并且在时钟为00时能够从前借一位。