目录
今天我们要做的是一个温度计,因为江西这几天的温度太高了,所以就产生了去测一下室外温度的想法,所以我就在下午5点左右带着单片机去了室外,开始了测温。不过呆了一分钟不到就溜了,哈哈哈!
引脚及应用电路
这是 DS18B20 的原理图,本身难度不大,主要就是关于单总线的知识,我们后面会详细介绍。
左边是上拉电阻,下方是有电源时走的线路,上方是DQ单总线走的线路。先通过一个ROM寻址,相当于大门的功能,然后是内存控制,相当于管家,再到RAM关联的设备,这大概就是内部的结构,在图中都有说明。
初始化:主机将总线拉低至少480us,然后释放总线,等待15~60us后,存在的从机会拉低总线60~240us以响应主机,之后从机将释放总线
发送一位:主机将总线拉低60~120us,然后释放总线,表示发送0;主机将总线拉低1~15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60us
接收一位:主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1 ,整个时间片应大于60us
发送一个字节:连续调用8次发送一位的时序,依次发送一个字节的8位(低位在前)
注:有些同学可能觉得读和写如此相似,单片机怎么去区分是读还是写呢?其实单片机默认为写入,相当于课堂上默认老师先说话一样,然后给出相应的指令,才能读取,相当于老师让我们回答问题之后,学生才能说话一样。
接收一个字节:连续调用8次接收一位的时序,依次接收一个字节的8位(低位在前)
以补码的形式存储,便于计算机进行计算,有不了解的同学可以去看我的数电笔记。计算转换的过程如下所示:
介绍完了硬件部分,我们接下来就来编写代码实现其功能吧!
首先是单总线的代码部分,如下所示:
// OneWire.c
#include <REGX52.H>
//DQ引脚定义
sbit OneWire_DQ=P3^7;
/**
* @brief 单总线初始化
* @param 无
* @retval 从机响应位,0为响应,1为未响应
*/
unsigned char OneWire_Init(void)
{
unsigned char i;
unsigned char AckBit;
OneWire_DQ=1;
OneWire_DQ=0;
i = 227;while (--i); //@11.0592MHz Delay 500us
OneWire_DQ=1; //释放
i = 34;while (--i); //Delay 80us
AckBit=OneWire_DQ; // 应答
i = 227;while (--i); //Delay 500us
return AckBit;
}
/**
* @brief 单总线发送一位
* @param Bit 要发送的位
* @retval 无
*/
void OneWire_SendBit(unsigned char Bit)
{
unsigned char i;
OneWire_DQ=0;
i = 3;while (--i); //Delay 10us
OneWire_DQ=Bit;
i = 22;while (--i); //Delay 50us
OneWire_DQ=1;
}
/**
* @brief 单总线接收一位
* @param 无
* @retval 读取的位
*/
unsigned char OneWire_ReceiveBit(void)
{
unsigned char i;
unsigned char Bit;
OneWire_DQ=0;
i = 2;while (--i); //Delay 6us
OneWire_DQ=1;
i = 2;while (--i); //Delay 6us
Bit=OneWire_DQ;
i = 22;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(void)
{
unsigned char i;
unsigned char Byte=0x00;
for(i=0;i<8;i++)
{
if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}
}
return Byte;
}
然后是DS18B20的代码,如下所示:
// DS18B20.c
#include <REGX52.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(void)
{
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_CONVERT_T);
}
/**
* @brief DS18B20读取温度
* @param 无
* @retval 温度数值
*/
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; // 右移4位,取小数
return T;
}
最后就是我们代码的主函数了,代码如下所示:
// main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"
float T;
void main()
{
DS18B20_ConvertT(); //上电先转换一次温度,防止第一次读数据错误
Delay(1000); //等待转换完成
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);//显示温度小数部分
}
}
感觉温度应该还可以升,但是等不了了,30秒左右温度就到了44度,(刚出去的时候就有40多度)然后就先回去了,这还是下午5点,中午两点都不敢去尝试。(其实是还没睡醒,哈哈哈!)
https://live.csdn.net/v/embed/227757
测温