(51单片机)LCD显示温度(DS18B20教程)(LCD1602教程)(延时函数教程)(单总线教程)

 演示视频:

LCD显示温度

 源代码

如上图将9个文放在Keli5 中即可,然后烧录在单片机中就行了

烧录软件用的是STC-ISP,不知道怎么安装的可以去看江科大的视频:

【51单片机入门教程-2020版 程序全程纯手打 从零开始入门】https://www.bilibili.com/video/BV1Mb411e7re?p=2&vd_source=ada7b122ae16cc583b4add52ad89fd5e

源代码:

头文件要记得宏定义和重定义,避免重复调用:

#ifndef _Timer0_h_//名字根据文件名定义即可
#define _Timer0_h_

//声明函数……

#endif

 main.c

#include <STC89C5xRC.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"

float T;//浮点数温度

//主函数
void main(){
	DS18B20_ConvertT();//上电先转换一次温度,防止第一次读数据错误
	Delay(1000);//延时1秒启动
	LCD_Init();//LCD1602初始化
	LCD_ShowString(1,1,"Temperature:");//LCD初始化显示
	while(1){//无限循环
		DS18B20_ConvertT();//转换一次温度
		T=DS18B20_ReadT();//读取温度
		if(T<0){//如果温度小于0
		LCD_ShowChar(2,1,'-');//显示负号
		T=-T;//将温度变为正数
		}
		else{//如果温度大于等于0
			LCD_ShowChar(2,1,'+');//显示正号
		}
		LCD_ShowNum(2,2,T,3);//显示温度整数部分
		LCD_ShowChar(2,5,'.');//显示小数点
		LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);//显示温度小数部分
	}
}

LCD1602.c

#include <STC89C5xRC.H>


//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()		//@11.0592MHz
{
	unsigned char i, j;

	i = 11;
	j = 190;
	do
	{
		while (--j);
	} while (--i);
}


/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}

LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();//初始化
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);//显示单个字符
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);//显示字符串
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//显示数字
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);//显示带符号数字
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//显示十进制数字
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//显示二进制数字

#endif

DS18B20.c

//DS18B20.c

#include <STC89C5xRC.H>
#include "OneWire.h"

//DS18B20指令
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE

/**
  * @brief  DS18B20开始温度变换
  * @param  无
  * @retval 无
  */
void DS18B20_ConvertT(){
	OneWire_Init();//单总线初始化
	OneWire_SendByte(DS18B20_SKIP_ROM);//单总线发送1个字节(跳过ROM指令)
	OneWire_SendByte(DS18B20_CONVERT_T);//单总线发送1个字节(转换温度指令)
}

/**
  * @brief  DS18B20读取温度
  * @param  无
  * @retval 温度数值
  */
float DS18B20_ReadT(){
	unsigned char TLSB,TMSB;//温度格式
	int Temp;//临时变量
	float T;//浮点数温度
	OneWire_Init();//单总线初始化
	OneWire_SendByte(DS18B20_SKIP_ROM);//单总线发送1个字节(跳过ROM指令)
	OneWire_SendByte(DS18B20_READ_SCRATCHPAD);//单总线发送1个字节(转换温度指令)
	TLSB=OneWire_ReceiveByte();//低8位
	TMSB=OneWire_ReceiveByte();//高8位
	Temp=(TMSB<<8)|TLSB;//合并温度16位
	T=Temp/16.0;//正确转换温度(浮点数)
	return T;//返回温度值
}

DS18B20.h

//DS18B20.h


#ifndef __DS18B20_H__
#define __DS18B20_H__

float DS18B20_ReadT();
void DS18B20_ConvertT();

#endif

OneWire.c

//OneWire.c

#include <STC89C5xRC.H>

//引脚定义
sbit OneWire_DQ=P3^7;

/**
  * @brief  单总线初始化
  * @param  无
  * @retval 从机响应位,0为响应,1为未响应
  */
unsigned char OneWire_Init(){
	unsigned char i;
	unsigned char AckBit;
	OneWire_DQ=1;
	OneWire_DQ=0;
	i = 247;while (--i);//Delay 500us
	OneWire_DQ=1;
	i = 32;while (--i);//Delay 70us
	AckBit=OneWire_DQ;
	i = 247;while (--i);//Delay 500us
	return AckBit;
}

/**
  * @brief  单总线发送一位
  * @param  Bit 要发送的位
  * @retval 无
  */
void OneWire_SendBit(unsigned char Bit){
	unsigned char i;
	OneWire_DQ=0;
	i = 4;while (--i);//Delay 10us
	OneWire_DQ=Bit;
	i = 24;while (--i);//Delay 50us
	OneWire_DQ=1;
}

/**
  * @brief  单总线接收一位
  * @param  无
  * @retval 读取的位
  */
unsigned char OneWire_ReceiveBit(){
	unsigned char i;
	unsigned char Bit;
	OneWire_DQ=0;
	i = 2;while (--i);//Delay 5us
	OneWire_DQ=1;
	i = 2;while (--i);//Delay 5us
	Bit=OneWire_DQ;
	i = 24;while (--i);//Delay 50us
	return Bit;
}

/**
  * @brief  单总线发送一个字节
  * @param  Byte 要发送的字节
  * @retval 无
  */
void OneWire_SendByte(unsigned char Byte){
	unsigned char i;
	for(i=0;i<8;i++){
		OneWire_SendBit(Byte&(0x01<<i));
	}
}

/**
  * @brief  单总线接收一个字节
  * @param  无
  * @retval 接收的一个字节
  */
unsigned char OneWire_ReceiveByte(){
	unsigned char i;
	unsigned char Byte=0x00;
	for(i=0;i<8;i++){
		if(OneWire_ReceiveBit()){(Byte|=0x01<<i);}
	}
	return Byte;
}

OneWire.h

//OneWire.h

#ifndef __OneWire_H__
#define __OneWire_H__

unsigned char OneWire_Init();
unsigned char OneWire_ReceiveByte();
void OneWire_SendByte(unsigned char Byte);
unsigned char OneWire_ReceiveBit();
void OneWire_SendBit(unsigned char Bit);

#endif

Delay.c 

//Delay.c

#include <STC89C5xRC.H>
#include <INTRINS.H>

//延时函数
void Delay(unsigned int xms)		//@11.0592MHz
{
	unsigned char i, j;
	while(xms){
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}

 Delay.h

//Delay.h

#ifndef __Delay_H__
#define __Delay_H__

//延时函数头文件
void Delay(unsigned int xms);
#endif

 代码解析与教程:

 Dealy模块
  • 包含源代码与头文件,不需要知道怎么实现的会用即可,后续使用,直接将头文件和源代码拿过来用即可;

xms是定义的毫秒,1000毫秒就是1秒;模版生成的是1毫秒的,因此xms等于1000

 LCD1602模块
  • 包含源代码与头文件,不需要知道怎么实现的会用即可,后续使用,直接将头文件和源代码拿过来用即可;使用格式:(参考江科大的视频素材)

  • LCD1602相关重要知识:
    • LCD1602有两上下两行显示屏,每行各有16个小显示屏,如上图中的LCD_ShowString(1,3,"Hello"),第一个参数是第一行还是第二行,第2个参数是对应第几行的第几个小显示屏,最后一个是输出的东西,同理,到LCD_ShowNum(1,9,123,3)里,前三个和前面一样,最后一个参数是显示的位数,不够就在前面补0,例如输入1,参数为4,显示就是0001,输入23,参数为3,显示就是023

  • 上图是LCD1602的开发原理模块图,由图可知,P10-P17是控制显示屏的重点;像矩阵那样,S1,由P17和P13控制,同理,其他按键一样,由于开发板限制,我们一般使用列遍历来控制按键,例如:S1,S5,S9,S13他们四个共用P13,所以将其为1列,将P13设定为1,P17设定为0,S1就会亮,其他按键同理。
单总线模块
  • 包含源代码与头文件,需要知道怎么实现的,会用,后续使用,直接将头文件和源代码拿过来用即可;因为这部分比较难,轻度认识理解就行,越深越难
  • 在此之前,我们要认识一下单总线:

  • 单总线和上节课将的I2C总线有相似之处:

(51单片机)LCD显示数据存储(DS1302时钟模块教学)(LCD1602教程)(独立按键教程)(延时函数教程)(I2C总线认识)(AT24C02认识)-CSDN博客

  • 了解之后,di总线可以理解成一个传输协议,或者传输方式,规则;作用是将一个机器(主机)的数据传输到另一个机器(从机);来看DS18B20原理图

由于可以看到I/O口是P37,因此单总线引脚就是P37

//引脚定义
sbit OneWire_DQ=P3^7;
  • 通过这个东西,将主机(单片机)数据传递到从机(DS18B20);下面来看传输是怎么实现的
  • 单总线传输时有时序结构,时序结构通过一条DQ线来控制,因此叫单总线
  • 因为单总线只有一条线,需要通过不同的延时来实现数据传输,因此需要用STC-ISP的软件延时功能,生成延时:

  • 时序结构有5个小模块,看图:

这是初始化部分中间那条线就是DQ通讯线,高电平是1(高一点的部分),低电平是0(低一点的部分);图中可以看到DQ从1变0,然后480us后,释放总线变1,然后15-60us后再变0,60-240us后变1;因此有

/**
  * @brief  单总线初始化
  * @param  无
  * @retval 从机响应位,0为响应,1为未响应
  */
unsigned char OneWire_Init(){
	unsigned char i;
	unsigned char AckBit;
	OneWire_DQ=1;
	OneWire_DQ=0;
	i = 247;while (--i);//Delay 500us
	OneWire_DQ=1;
	i = 32;while (--i);//Delay 70us
	AckBit=OneWire_DQ;
	i = 247;while (--i);//Delay 500us
	return AckBit;
}

接下来是发送数据,发送一个bit,DQ先1后0,在1-15us内,让DS18B20数据感应,然后总时间大于60us释放总线变1,即可发送一个bit:

/**
  * @brief  单总线发送一位
  * @param  Bit 要发送的位
  * @retval 无
  */
void OneWire_SendBit(unsigned char Bit){
	unsigned char i;
	OneWire_DQ=0;
	i = 4;while (--i);//Delay 10us
	OneWire_DQ=Bit;
	i = 24;while (--i);//Delay 50us
	OneWire_DQ=1;
}

发送bit是参数Bit,i是延时用的,重点来了:注意看图中的时间先后;

接下来是接收bit,接收一个bit,DQ先1后0,在1-15us内变1,要在接近15us的时候让DS18B20数据感应,然后总时间大于60us,即可接收一个bit:

/**
  * @brief  单总线接收一位
  * @param  无
  * @retval 读取的位
  */
unsigned char OneWire_ReceiveBit(){
	unsigned char i;
	unsigned char Bit;
	OneWire_DQ=0;
	i = 2;while (--i);//Delay 5us
	OneWire_DQ=1;
	i = 2;while (--i);//Delay 5us
	Bit=OneWire_DQ;
	i = 24;while (--i);//Delay 50us
	return Bit;
}

接收bit是无参数,返回值是bit,i是延时用的,重点来了:注意看图中的先后顺序,而且要在15us附近数据感应;


接下来是发送一个字节和接收一个字节。
 
/**
  * @brief  单总线发送一个字节
  * @param  Byte 要发送的字节
  * @retval 无
  */
void OneWire_SendByte(unsigned char Byte){
	unsigned char i;
	for(i=0;i<8;i++){
		OneWire_SendBit(Byte&(0x01<<i));
	}
}
/**
  * @brief  单总线接收一个字节
  * @param  无
  * @retval 接收的一个字节
  */
unsigned char OneWire_ReceiveByte(){
	unsigned char i;
	unsigned char Byte=0x00;
	for(i=0;i<8;i++){
		if(OneWire_ReceiveBit()){(Byte|=0x01<<i);}
	}
	return Byte;
}

低位在前,因此左移,&(有0即0)的操作是获取当前位,|(有1即1)的操作当OneWire_ReceiveBit不为0时,将当前位赋值给Byte

DS18B20模块
  • 包含源代码与头文件,需要知道怎么实现的,会用,后续使用,直接将头文件和源代码拿过来用即可;因为这部分比较难,轻度认识理解就行,越深越难
  • DS18B20是温度传感器,可以理解上述中所提到的从机主机就是单片机

DS18B20有字节地址操作,作用是进行一定的操作,ROM和功能操作:

  • 了解之后,来看数据帧,也就是数据是怎么在单总线中传输的:

首先可以看到温度变换的操作,先初始化,然后发送跳过ROM的字节地址进行跳过ROM,然后发送温度变换的字节地址,进行温度转换

/**
  * @brief  DS18B20开始温度变换
  * @param  无
  * @retval 无
  */
void DS18B20_ConvertT(){
	OneWire_Init();//单总线初始化
	OneWire_SendByte(DS18B20_SKIP_ROM);//单总线发送1个字节(跳过ROM指令)
	OneWire_SendByte(DS18B20_CONVERT_T);//单总线发送1个字节(转换温度指令)
}

首先可以看到温度读取的操作,先初始化,然后发送跳过ROM的字节地址进行跳过ROM,然后发送温度读取的字节地址,进行读暂存器和连续的读操作

/**
  * @brief  DS18B20读取温度
  * @param  无
  * @retval 温度数值
  */
float DS18B20_ReadT(){
	unsigned char TLSB,TMSB;//温度格式
	int Temp;//临时变量
	float T;//浮点数温度
	OneWire_Init();//单总线初始化
	OneWire_SendByte(DS18B20_SKIP_ROM);//单总线发送1个字节(跳过ROM指令)
	OneWire_SendByte(DS18B20_READ_SCRATCHPAD);//单总线发送1个字节(转换温度指令)
	TLSB=OneWire_ReceiveByte();//低8位
	TMSB=OneWire_ReceiveByte();//高8位
	Temp=(TMSB<<8)|TLSB;//合并温度16位
	T=Temp/16.0;//正确转换温度(浮点数)
	return T;//返回温度值
}


从上图可以看出,TLSB和TMSB温度格式,是8位的,MS的前5为都是符号位。将它俩合到一块就是温度。

 main模块
  • 注释写的很清楚,这里不做解释了
#include <STC89C5xRC.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"

float T;//浮点数温度

//主函数
void main(){
	DS18B20_ConvertT();//上电先转换一次温度,防止第一次读数据错误
	Delay(1000);//延时1秒启动
	LCD_Init();//LCD1602初始化
	LCD_ShowString(1,1,"Temperature:");//LCD初始化显示
	while(1){//无限循环
		DS18B20_ConvertT();//转换一次温度
		T=DS18B20_ReadT();//读取温度
		if(T<0){//如果温度小于0
		LCD_ShowChar(2,1,'-');//显示负号
		T=-T;//将温度变为正数
		}
		else{//如果温度大于等于0
			LCD_ShowChar(2,1,'+');//显示正号
		}
		LCD_ShowNum(2,2,T,3);//显示温度整数部分
		LCD_ShowChar(2,5,'.');//显示小数点
		LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);//显示温度小数部分
	}
}

 注:该代码是本人自己所写,可能不够好,不够简便,欢迎大家指出我的不足之处。如果遇见看不懂的地方,可以在评论区打出来,进行讨论,或者联系我。上述内容全是我自己理解的,如果你有别的想法,或者认为我的理解不对,欢迎指出!!!如果可以,可以点一个免费的赞支持一下吗?谢谢各位彦祖亦菲!!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值