STC89C51/52单片机基于LCD1602的温度(DS18B20)与时间(DS1302)显示系统

一、项目目的:

1、掌握LCD1602、DS18B20、DS1302模块的工作原理及使用方法

2、学习单片机通过单总线(DS18B20)和三线接口(DS1302)与传感器通信

3、实现开发板上实时温度、时间的采集与LCD显示

4、理解中断机制在传感器数据采集中的应用(可选拓展)

二、项目内容:

  1、普中51单片机开发板结构及使用步骤

  2、keil软件的基本操作

  3、proteus绘制原理图基本步骤

  4、硬件功能:

①:LCD1602显示两行信息:

TIME 12:59:30 

TEMP 25.6℃ 

②:DS18B20实时采集环境温度(精度±0.5℃)

⑥:DS1302提供实时时钟(支持断电续走)

   5、软件功能

①初始化各模块并实现数据读取

②定时刷新显示

三、前期准备:

硬件材料:

        普中科技51开发板(板载DS1302与DS18B20),LCD1602.

软件准备:

        keil,普中科技烧录工具。

四、代码:

main.c

#include "LCD1602.h"
#include "DS18B20.h"
#include "ds1302.h"

float Temp0;
void main()
{
  	ds1302_init();//初始化DS1302
	LCD1602_Init();
	LCD_ShowString(1, 1, "TEMP:");
    LCD_ShowString(2, 1, "TIME:");

    
    while(1)
	{
        

            
            ds1302_read_time();
            Temp0 = DS18B20_ReadTemp();
            
            if (Temp0 < 0)
            {
                LCD_ShowChar(1, 6, '-');
                Temp0 = -Temp0;
            }
            else
            {
                LCD_ShowChar(1, 6, '+');
            }
            
        
        
        
        
		LCD_ShowNub(1, 7, Temp0, 3);
		LCD_ShowChar(1, 10, '.');
		LCD_ShowNub(1, 11, (unsigned long)(Temp0 * 10000) % 10000, 4);
        
        LCD_ShowNub(2, 6, gDS1302_TIME[2]/16, 1);
        LCD_ShowNub(2, 7, gDS1302_TIME[2]&0x0f, 1);
        LCD_ShowChar(2, 8, ':');
        LCD_ShowNub(2, 9, gDS1302_TIME[1]/16, 1);
        LCD_ShowNub(2, 10, gDS1302_TIME[1]&0x0f, 1);
        LCD_ShowChar(2, 11, ':');
        LCD_ShowNub(2, 12, gDS1302_TIME[0]/16, 1);
        LCD_ShowNub(2, 13, gDS1302_TIME[0]&0x0f, 1);
        
	}
}

One_wire.c

#include <REGX52.H>
#include <INTRINS.H>

sbit One_Wire_Data = P3^7;

void One_Wire_Delay10us(unsigned char xus)	//@11.0592MHz  单总线专用延时
{
	unsigned char data i;
	while(xus--)
	{
		i = 2;
		while (--i);	
	}

}

void One_Wire_Delay5us(void)	//@11.0592MHz
{
}


unsigned char One_Wire_Init(void)
{
	unsigned char One_Wire_ACK = 0;
	One_Wire_Data = 1;
	One_Wire_Data = 0;  // 拉低总线500us
	One_Wire_Delay10us(50);
	One_Wire_Data = 1;  // 释放总线
	One_Wire_Delay10us(6);  // 等待100us, 如果总线被拉低,说明从机有响应,否则未响应
	One_Wire_ACK = One_Wire_Data;
	One_Wire_Delay10us(50);  // 时序要求最小480us,延时400us让时序完整
	return One_Wire_ACK;
}

void One_Wire_WriteData(unsigned char Data)
{
	unsigned char i;
	for (i = 0; i < 8; i++)
	{
		if(Data & (0x01 << i))
		{
			One_Wire_Data = 0;
			One_Wire_Delay5us();
			One_Wire_Data = 1;
			One_Wire_Delay10us(6);
		}
		else
		{
			One_Wire_Data = 0;
			One_Wire_Delay10us(6);
		}
		One_Wire_Data = 1;
		One_Wire_Delay10us(1);
	}
}


unsigned char One_Wire_ReadData(void)
{
	unsigned char i, Data = 0x00;

	for (i = 0; i < 8; i++)
	{
		One_Wire_Data = 0;
		One_Wire_Delay5us();
		One_Wire_Data = 1;	
		One_Wire_Delay5us();
		if (One_Wire_Data)
		{
			Data |= (0x01 << i);
		}
		
		One_Wire_Data = 1;	
		One_Wire_Delay10us(6);	
	}
	return Data;	
}

Oner_wrie.h

#ifndef __ONE_WIRE_H__
#define __ONE_WIRE_H__

unsigned char One_Wire_Init(void);
void One_Wire_WriteData(unsigned char Data);
unsigned char One_Wire_ReadData(void);

#endif

ds18b20.c

#include <REGX52.H>
#include "One_Wire.h"
#include "Delay1ms.h"

float DS18B20_ReadTemp()
{
	unsigned char Htemp, Ltemp;
	int Temp1;
	float Temp;
	One_Wire_Init();
	One_Wire_WriteData(0xCC);
	One_Wire_WriteData(0x44);
	
	One_Wire_Init();
	One_Wire_WriteData(0xCC);
	One_Wire_WriteData(0xBE);
	Ltemp = One_Wire_ReadData();
	Htemp = One_Wire_ReadData();
	Temp1 = Htemp << 8 | Ltemp;
	Temp = Temp1 / 16.0;
	return Temp;
}

ds18b20.h

#ifndef __DS18B20_H__
#define __DS18B20_H__

float DS18B20_ReadTemp();

#endif

ds1302.c

#include "ds1302.h"
#include "intrins.h"

//---DS1302写入和读取时分秒的地址命令---//
//---秒分时日月周年 最低位读写位;-------//
int gREAD_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; 
int gWRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};

//---DS1302时钟初始化2021年5月20日星期四13点51分47秒。---//
//---存储顺序是秒分时日月周年,存储格式是用BCD码---//
int gDS1302_TIME[7] = {0x47, 0x51, 0x13, 0x20, 0x04, 0x05, 0x21};


/*******************************************************************************
* 函 数 名       : ds1302_write_byte
* 函数功能		 : DS1302写单字节
* 输    入       : addr:地址/命令
				   dat:数据
* 输    出    	 : 无
*******************************************************************************/
void ds1302_write_byte(int addr,int dat)
{
	int i=0;
	
	DS1302_RST=0;
	_nop_();	
	DS1302_CLK=0;//CLK低电平
	_nop_();
	DS1302_RST=1;//RST由低到高变化
	_nop_();

	for(i=0;i<8;i++)//循环8次,每次写1位,先写低位再写高位
	{
		DS1302_IO=addr&0x01;
		addr>>=1;
		DS1302_CLK=1;
		_nop_();
		DS1302_CLK=0;//CLK由低到高产生一个上升沿,从而写入数据
		_nop_();		
	}
	for(i=0;i<8;i++)//循环8次,每次写1位,先写低位再写高位
	{
		DS1302_IO=dat&0x01;
		dat>>=1;
		DS1302_CLK=1;
		_nop_();
		DS1302_CLK=0;
		_nop_();		
	}
//	DS1302_RST=0;//RST拉低
	_nop_();	
}

/*******************************************************************************
* 函 数 名       : ds1302_read_byte
* 函数功能		 : DS1302读单字节
* 输    入       : addr:地址/命令
* 输    出    	 : 读取的数据
*******************************************************************************/
int ds1302_read_byte(int addr)
{
	int i=0;
	int temp=0;
	int value=0;

	DS1302_RST=0;
	_nop_();	
	DS1302_CLK=0;//CLK低电平
	_nop_();
	DS1302_RST=1;//RST由低到高变化
	_nop_();
	
	for(i=0;i<8;i++)//循环8次,每次写1位,先写低位再写高位
	{
		DS1302_IO=addr&0x01;
		addr>>=1;	
		DS1302_CLK=1;
		_nop_();
		DS1302_CLK=0;//CLK由低到高产生一个上升沿,从而写入数据
		_nop_();		
	}
	for(i=0;i<8;i++)//循环8次,每次读1位,先读低位再读高位
	{
		temp=DS1302_IO;
		value=(temp<<7)|(value>>1);//先将value右移1位,然后temp左移7位,最后或运算
		DS1302_CLK=1;
		_nop_();
		DS1302_CLK=0;
		_nop_();		
	}
//	DS1302_RST=0;//RST拉低
	_nop_();	
	DS1302_CLK=1;//对于实物中,P3.4口没有外接上拉电阻的,此处代码需要添加,使数据口有一个上升沿脉冲。
	_nop_();
	DS1302_IO = 0;
	_nop_();
	DS1302_IO = 1;
	_nop_();	
	return value;		
}

/*******************************************************************************
* 函 数 名       : ds1302_init
* 函数功能		 : DS1302初始化时间
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void ds1302_init(void)
{
	int i=0;
	ds1302_write_byte(0x8E,0X00);
	for(i=0;i<7;i++)
	{
		ds1302_write_byte(gWRITE_RTC_ADDR[i],gDS1302_TIME[i]);	
	}
	ds1302_write_byte(0x8E,0X80);	
}

/*******************************************************************************
* 函 数 名       : ds1302_read_time
* 函数功能		 : DS1302读取时间
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void ds1302_read_time(void)
{
	int i=0;
	for(i=0;i<7;i++)
	{
		gDS1302_TIME[i]=ds1302_read_byte(gREAD_RTC_ADDR[i]);	
	}	
}

ds1302.h

#ifndef _ds1302_H
#define _ds1302_H

#include "REG52.h"

//管脚定义
sbit DS1302_RST=P3^5;//复位管脚
sbit DS1302_CLK=P3^6;//时钟管脚
sbit DS1302_IO=P3^4;//数据管脚

//变量声明
extern int gDS1302_TIME[7];//存储时间;

//函数声明
void ds1302_init(void);
void ds1302_read_time(void);


#endif

LCD1602.c

#include <REGX52.H>
#include <INTRINS.H>
#define  LCD_DATA   P0
sbit LCD1602_RS = P2^6;
sbit LCD1602_EN = P2^7;
sbit LCD1602_WR = P2^5;


void LCD1602_Delay1ms(void)	//@11.0592MHz
{
	unsigned char data i, j;

	_nop_();
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}


void LCD1602_WriteCmd(unsigned char Cmd)
{
	LCD1602_RS = 0;
	LCD1602_WR = 0;
	LCD1602_EN = 0;
	LCD_DATA = Cmd;
	LCD1602_EN = 1;
	LCD1602_Delay1ms();
	LCD1602_EN = 0;
	LCD1602_Delay1ms();
}

void LCD1602_WriteData(unsigned char Data)
{
	LCD1602_RS = 1;
	LCD1602_WR = 0;
	LCD1602_EN = 0;
	LCD_DATA = Data;
	LCD1602_EN = 1;
	LCD1602_Delay1ms();
	LCD1602_EN = 0;	
	LCD1602_Delay1ms();
}

void LCD1602_Init()
{
	LCD1602_WriteCmd(0x38);
	LCD1602_WriteCmd(0x0C);
	LCD1602_WriteCmd(0x06);
	LCD1602_WriteCmd(0x01);
}

// 用于设置位置,在哪里显示。
void Set_Position(unsigned char Row, unsigned char Col)
{
	if (Row == 1)
	{
		LCD1602_WriteCmd(0x80 | (Col - 1));  // 映射的位置的数据最高位固定为1,所以真实的地址应该要再或上0x80
	}
	else
	{
		LCD1602_WriteCmd((0x80 | (Col - 1)) + 0x40);
	}
}

// 在特定位置,显示 单个字节
void LCD_ShowChar(unsigned char Rows, unsigned char Cols, unsigned char Char)
{
	Set_Position(Rows, Cols);
	LCD1602_WriteData(Char);
	
}

// 在特定位置,显示 一个字符串
void LCD_ShowString(unsigned char Rows, unsigned char Cols, unsigned char String[])
{
	unsigned char i;
	Set_Position(Rows, Cols);
	for (i = 0; String[i] != '\0'; i++)
	{
		LCD1602_WriteData(String[i]);
	}
}



//123 / 100 % 10  = 1
//123 / 10  % 10  = 2
//123 / 1   % 10  = 3
// 用于计算某个数字的次方结果 
unsigned int SetNub(unsigned char Nub, Len)
{
	unsigned int NubValue = 1;
	unsigned char i;
	for (i = 0; i < (Len - 1); i++)
	{
		NubValue *= Nub;
	}
	return NubValue;
}

// 在特定位置,显示一个有符号数字(0 ~ 65535)
void LCD_ShowNub(unsigned char Rows, unsigned char Cols, unsigned int Nub, unsigned char Len)
{
	unsigned char i;
	Set_Position(Rows, Cols);
	for (i = Len; i > 0; i--)
	{
		LCD1602_WriteData(((Nub / SetNub(10, i) % 10) + 0x30)); // 加上0x30是将数字转换成字符。例如数字0的十六进制ASCLL码为0x00,但是字符0的十六进制ASCLL码为0x30,所以直接加0x30就可以完成转换了。
	}
}

// 在特定位置,显示一个有符号数字(-32768 ~ 32767)
void LCD_ShowSignedNub(unsigned char Rows, unsigned char Cols, int Nub, unsigned char Len)
{
	unsigned char i;
	Set_Position(Rows, Cols);
	if (Nub >= 0)
	{
		LCD1602_WriteData('+');
		for (i = Len; i > 0; i--)
		{
			LCD1602_WriteData(((Nub / SetNub(10, i) % 10) + 0x30)); // 加上0x30是将数字转换成字符。例如数字0的十六进制ASCLL码为0x00,但是字符0的十六进制ASCLL码为0x30,所以直接加0x30就可以完成转换了。
		}
	}
	else
	{
		LCD1602_WriteData('-');
		for (i = Len; i > 0; i--)
		{
			LCD1602_WriteData(((-(Nub) / SetNub(10, i) % 10) + 0x30)); // 加上0x30是将数字转换成字符。例如数字0的十六进制ASCLL码为0x00,但是字符0的十六进制ASCLL码为0x30,所以直接加0x30就可以完成转换了。
		}
	}
}

// 在特定位置,显示一个十六进制数 (0 ~ 0xFFFF)
void LCD_ShowHex(unsigned char Rows, unsigned char Cols, unsigned int Hex, unsigned char Len)
{
	unsigned char i, HexNub;
	Set_Position(Rows, Cols);
	
	for (i = Len; i > 0; i--)
	{
		HexNub = (Hex / SetNub(16, i)) % 16;
		if (HexNub < 10)  LCD1602_WriteData(HexNub + 0x30);  
		else LCD1602_WriteData(HexNub + 55);
	}
		
}

// 在特定位置,显示一个二进制数(0 ~ 1111 1111 1111 1111)
void LCD_ShowBin(unsigned char Rows, unsigned char Cols, unsigned int Bin, unsigned char Len)
{
	unsigned char i, BinNub;
	Set_Position(Rows, Cols);
	
	for (i = Len; i > 0; i--)
	{
		BinNub = (Bin / SetNub(2, i)) % 2;
		LCD1602_WriteData(BinNub + 0x30);
	}
		
}

LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__

void LCD1602_Init();
void LCD1602_WriteCmd(unsigned char Cmd);
void LCD1602_WriteData(unsigned char Data);
void LCD_ShowChar(unsigned char Rows, unsigned char Cols, unsigned char Char);
void LCD_ShowString(unsigned char Rows, unsigned char Cols, unsigned char String[]);
void LCD_ShowNub(unsigned char Rows, unsigned char Cols, unsigned int Nub, unsigned char Len);
void LCD_ShowSignedNub(unsigned char Rows, unsigned char Cols, int Nub, unsigned char Len);
void LCD_ShowHex(unsigned char Rows, unsigned char Cols, unsigned int Hex, unsigned char Len);
void LCD_ShowBin(unsigned char Rows, unsigned char Cols, unsigned int Bin, unsigned char Len);

#endif

delay1ms.c

#include <REGX52.H>
#include <INTRINS.H>
void Delay1ms(unsigned int xms)	//@11.0592MHz
{
	unsigned char data i, j;
	while(xms--)
	{
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);		
	}

}

delay1ms.h

#include <REGX52.H>
#include <INTRINS.H>
void Delay1ms(unsigned int xms)	//@11.0592MHz
{
	unsigned char data i, j;
	while(xms--)
	{
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);		
	}

}

运行结果:

        

注意事项:
 

        对于普中开发板中同时使用ds1302与ds18b20会发生引脚冲突。

        在上述代码中ds1302的通信协议中如果将DS1302_RST拉低,也就是P3^5引脚拉低,会导致ds18b20无法正常通信。如果要同时使用这两个模块请注意。此结果只是针对普中开发板。

        所以在代码中将P3^5引脚拉低的代码注释掉代码可以正常运行。

由图而知   当CS=0  DOUT=0     CS=1   DOUT为高阻状态

由此而知   当p35=0  p37=0

所以  当ds1302时钟芯片  和   温度ds18b20  工作时

一旦将  p35=0  那么p37=0    则使然   ds1302  和   ds18b20冲突  

附件连接:

通过网盘分享的文件:STC51-------LCD1602+DS1302+DS18B20
链接: https://pan.baidu.com/s/178pfkL4p2ge4yR5upJqBJg 提取码: yp4s

参考文献:

ds1302和ds18b20发生冲突 - 24小时必答区

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值