51单片机学习--DS18B20温度读取&温度报警器


需要先编写OneWire模块,再在DS18B20模块中调用OneWire模块的函数

先根据原理图做好端口的声明:

c 复制代码
sbit OneWire_DQ = P3^7;

接下来像之前一样把时序结构用代码模拟出来:

c 复制代码
unsigned char OneWire_Init(void)
{
	unsigned char i;
	unsigned char AckBit;
	OneWire_DQ = 1;
	
	OneWire_DQ = 0; //总线拉低
	//延时500us
	i = 227; while (--i);
	
	OneWire_DQ = 1; //释放总线
	//延时70us
	i = 29; while (--i);
	
	AckBit = OneWire_DQ;
	//延时500us
	i = 227; while (--i);
	
	return AckBit;
}

c 复制代码
void OneWire_SendBit(unsigned char Bit)
{
	unsigned char i;
	OneWire_DQ = 0; //总线拉低
	
	//延时10us后直接读电平
	i = 4; while(--i);
	OneWire_DQ = Bit;
	
	//延时50us凑满时间片
	i = 22; while(--i);
	
	OneWire_DQ = 1;
}

c 复制代码
unsigned char OneWire_ReceiveBit(void)
{
	unsigned char i;
	unsigned char Bit;
	OneWire_DQ = 0;
	
	//延时5us
	i = 2; while(--i);
	OneWire_DQ = 1;
	//延时5us
	i = 2; while(--i);
	
	Bit = OneWire_DQ;
	
	//延时50us
	i = 22; while(--i);
	
	return Bit;
}

c 复制代码
void OneWire_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i = 0; i < 8; i ++) {
		OneWire_SendBit(Byte & (0x01<<i));
	}
}

unsigned char OneWire_ReceiveByte(void)
{
	unsigned char i;
	unsigned char Byte = 0x00;
	for(i = 0; i < 8; i ++) {
		if(OneWire_ReceiveBit()) {Byte |= (0x01<<i);}
	}
	
	return Byte;
}

至此,OneWire模块就写好了,接下来在DS18B20模块中模拟数据帧:

c 复制代码
#include <REGX52.H>
#include "OneWire.h"

#define DS18B20_SKIP_ROM   0xCC
#define DS18B20_CONVERT_T  0x44
#define DS18B20_READ_SCRATCHPAD  0xBE

void DS18B20_ConvertT(void)
{
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIP_ROM);
	OneWire_SendByte(DS18B20_CONVERT_T);
}


float DS18B20_ReadT(void)
{
	unsigned char TLSB, TMSB;
	int temp;
	float T;
	
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIP_ROM);
	OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
	TLSB = OneWire_ReceiveByte();
	TMSB = OneWire_ReceiveByte();
	
	temp = (TMSB<<8) | TLSB;
	T = temp / 16.0;
	
	return T;
}

之后只需要在main中调用即可

c 复制代码
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"

float T;


void main()
{
	LCD_Init();
	LCD_ShowString(1, 1, "Temperature!");
	
	while(1)
	{
		DS18B20_ConvertT();
		T = DS18B20_ReadT();
		
		if(T < 0)
		{
			LCD_ShowChar(2, 1, '-');
			T = -T;
		}
		else
		{
			LCD_ShowChar(2, 1, '+');
		}
		LCD_ShowNum(2, 2, T, 3);//整数部分
		LCD_ShowChar(2, 5, '.');
		LCD_ShowNum(2, 6, (unsigned long)(T *10000) % 10000, 4); //小数部分
	}
}

接下来进行一些综合的应用:温度报警器,按键可调整报警温度的上下限,且能用AT24C02记录上下限,掉电不丢失(这里默认所有温度不会超过DS18B20的芯片范围)

c 复制代码
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"
#include "AT24C02.h"
#include "Key.h"
#include "Timer0.h"


float T, TShow;
char THigh, TLow;
unsigned char KeyNum;


void main()
{
	THigh = AT24C02_ReadByte(0);
	TLow = AT24C02_ReadByte(1);
	//第一次读可能是非法值,所以要特判一下
	if(THigh > 125 || TLow < - 55 || THigh <= TLow)
	{
		THigh = 20;
		TLow = 15;
	}
	
	Timer0_Init();
	LCD_Init();
	LCD_ShowString(1, 1, "T:");
	LCD_ShowString(2, 1, "TH:");
	LCD_ShowString(2, 9, "TL:");
	LCD_ShowSignedNum(2, 4, THigh, 3);
	LCD_ShowSignedNum(2, 12, TLow, 3);
	
	while(1)
	{
		KeyNum = Key();
		
		
		/*温度读取及显示*/
		DS18B20_ConvertT();
		T = DS18B20_ReadT();
		
		if(T < 0)
		{
			LCD_ShowChar(1, 3, '-');
			TShow = -T;
		}
		else
		{
			LCD_ShowChar(1, 3, '+');
			TShow = T;
		}
		LCD_ShowNum(1, 4, TShow, 3);
		LCD_ShowChar(1, 7, '.');
		LCD_ShowNum(1, 8, (unsigned long)(TShow * 100)%100, 2);
		
		
		/*阈值判断及显示*/
		if(KeyNum)
		{
			if(KeyNum == 1) THigh ++;
			if(KeyNum == 2) THigh --;
			if(KeyNum == 3) TLow ++;
			if(KeyNum == 4) TLow --;
			
			LCD_ShowSignedNum(2, 4, THigh, 3);
			LCD_ShowSignedNum(2, 12, TLow, 3);
			AT24C02_WriteByte(0, THigh);
			Delay(5);
			AT24C02_WriteByte(1, TLow);
			Delay(5);
		}
		
		if(T > THigh) LCD_ShowString(1, 13, "OV:H");
		else if(T < TLow) LCD_ShowString(1, 13, "OV:L");
		else LCD_ShowString(1, 13, "    ");
			
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count ++;
	if(T0Count >= 20) //20ms执行一次
	{
		T0Count = 0;
		
    Key_Loop();
	}
}

这还没完,在与定时器的结合中,定时器的中断会影响OneWire的延时,从而影响其中的时序结构进而影响温度实时获取,所以在时序结构编写的代码中需要加上屏蔽定时器中断的代码:开始时加上EA = 0,最后加上EA = 1,改版后的OneWire模块如下:

c 复制代码
#include <REGX52.H>

sbit OneWire_DQ = P3^7;

unsigned char OneWire_Init(void)
{
	unsigned char i;
	unsigned char AckBit;
	EA = 0;  //屏蔽中断
	OneWire_DQ = 1;
	
	OneWire_DQ = 0; //总线拉低
	//延时500us
	i = 227; while (--i);
	
	OneWire_DQ = 1; //释放总线
	//延时70us
	i = 29; while (--i);
	
	AckBit = OneWire_DQ;
	//延时500us
	i = 227; while (--i);
	
	EA = 1;
	return AckBit;
}

void OneWire_SendBit(unsigned char Bit)
{
	unsigned char i;
	EA = 0;  //屏蔽中断
	OneWire_DQ = 0; //总线拉低
	
	//延时10us后直接读电平
	i = 4; while(--i);
	OneWire_DQ = Bit;
	
	//延时50us凑满时间片
	i = 22; while(--i);
	
	OneWire_DQ = 1;
	EA = 1;
}

unsigned char OneWire_ReceiveBit(void)
{
	unsigned char i;
	unsigned char Bit;
	EA = 0;  //屏蔽中断
	OneWire_DQ = 0;
	
	//延时5us
	i = 2; while(--i);
	OneWire_DQ = 1;
	//延时5us
	i = 2; while(--i);
	
	Bit = OneWire_DQ;
	
	//延时50us
	i = 22; while(--i);
	
	EA = 1;
	return Bit;
}


void OneWire_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i = 0; i < 8; i ++) {
		OneWire_SendBit(Byte & (0x01<<i));
	}
}

unsigned char OneWire_ReceiveByte(void)
{
	unsigned char i;
	unsigned char Byte = 0x00;
	for(i = 0; i < 8; i ++) {
		if(OneWire_ReceiveBit()) {Byte |= (0x01<<i);}
	}
	
	return Byte;
}

但是这样虽然能维护好单总线的通信,但这样直接屏蔽中断却会影响定时器的计时准确度,从而对定时器控制的其他模块造成影响,这也就是单总线的一大缺点,不过在这个实例中,按键的定时器准度要求并不高,所以这样控制影响不大

相关推荐
EkihzniY44 分钟前
OCR 证件识别:驱动澳门酒店自助入住智能化
嵌入式硬件·ocr
好家伙VCC1 小时前
数学建模模型 全网最全 数学建模常见算法汇总 含代码分析讲解
大数据·嵌入式硬件·算法·数学建模
伴杯猫1 小时前
【ESP32-IDF】基础外设开发2:系统中断矩阵
c语言·单片机·嵌入式硬件·mcu·物联网·github
搬砖的小码农_Sky1 小时前
常见的显示器接口技术
嵌入式硬件·计算机外设·显示器
茯苓gao1 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾2 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT2 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa2 小时前
HTML和CSS学习
前端·css·学习·html
点灯小铭3 小时前
基于STM32单片机的智能粮仓温湿度检测蓝牙手机APP设计
stm32·单片机·智能手机·毕业设计·课程设计
沐欣工作室_lvyiyi3 小时前
基于单片机的智能路灯(论文+源码)
单片机·嵌入式硬件·毕业设计