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;
}

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

相关推荐
@小博的博客2 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
南宫生3 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
scan13 小时前
单片机串口接收状态机STM32
stm32·单片机·串口·51·串口接收
懒惰才能让科技进步3 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
love_and_hope4 小时前
Pytorch学习--神经网络--搭建小实战(手撕CIFAR 10 model structure)和 Sequential 的使用
人工智能·pytorch·python·深度学习·学习
Chef_Chen4 小时前
从0开始学习机器学习--Day14--如何优化神经网络的代价函数
神经网络·学习·机器学习
芊寻(嵌入式)4 小时前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
Qingniu014 小时前
【青牛科技】应用方案 | RTC实时时钟芯片D8563和D1302
科技·单片机·嵌入式硬件·实时音视频·安防·工控·储能
hong1616884 小时前
跨模态对齐与跨领域学习
学习
阿伟来咯~5 小时前
记录学习react的一些内容
javascript·学习·react.js