DS18B20 是美信公司的一款温度传感器,单片机可以通过 1-Wire 协议与 DS18B20 进行通信,最终将温度读出。1-Wire 总线的硬件接口很简单,只需要把 DS18B20 的数据引脚和单片机的一个 IO 口接上就可以了。硬件的简单,随之而来的,就是软件时序的复杂。1-Wire总线的时序比较复杂。
在每个 DS18B20 内部都有一个唯一的 64 位长的序列号,这个序列号值就存在 DS18B20内部的 ROM 中。开始的 8 位是产品类型编码(DS18B20 是 0x10),接着的 48 位是每个器件唯一的序号,最后的 8 位是 CRC 校验码。DS18B20 可以引出去很长的线,最长可以到几十米,测不同位置的温度。单片机可以通过和 DS18B20 之间的通信,获取每个传感器所采集到的温度信息,也可以同时给所有的 DS18B20 发送一些指令。
实现方法:(以总线上挂2个器件为例)
1.先单个读取每个器件的64位序列号
2.把序列号用数组存放起来
3.读取温度时发不同的序列号匹配对应器件
下面以总线上挂2个器件为例贴出代码
ds18b20.c文件的代码
#include "ds18b20.h"
unsigned char wendubuf1[2];//wendubuf[0]存扩大10倍的温度高8位 wendubuf[0]存扩大10倍的温度低8位 比如326 为32.6
unsigned char wendubuf2[2];//wendubuf[0]存扩大10倍的温度高8位 wendubuf[0]存扩大10倍的温度低8位 比如326 为32.6
unsigned char rom1[8]={0x28,0xCB,0x0C,0x96,0xF0,0x01,0x3C,0xE4};//28 CB 0C 96 F0 01 3C E4
unsigned char rom2[8]={0x28,0x63,0xC6,0x48,0xF6,0xB7,0x3C,0xEB};//28 63 C6 48 F6 B7 3C EB
void DS18B20Init()
{
SetGpioMode(GPIOP3,PIN_3,GPIO_Mode_Out_IN);//设置GPIO模式 GPIO_Mode_Out_IN准双向 GPIO_Mode_Out_PP推挽 GPIO_Mode_AIN高阻GPIO_Mode_Out_OD开漏
DS_OUT=1; //输出1
}
/* 复位总线,获取存在脉冲,以启动一次读写操作 */
unsigned char Get18B20Ack()
{
u8 retry=0;
DS_OUT = 0; //产生500us复位脉冲
delay_us(750);
DS_OUT = 1;
delay_us(15); //延时15us
while (DS_OUT&&retry<200)//等待被拉低
{
retry++;
delay_us(1);
};
if(retry>=200)return 1;
else retry=0;
while (!DS_OUT&&retry<240)
{
retry++;
delay_us(1);
};
if(retry>=240)return 1;
return 0;
}
//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void) // read one bit
{
u8 Data;
DS_OUT=0;
delay_us(2);
DS_OUT=1;
delay_us(12);
if(DS_OUT)Data=1;
else Data=0;
delay_us(50);
return Data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
//写一个字节到DS18B20
//dat:要写入的字节
/* 向DS18B20写入一个字节,dat-待写入字节 */
void Write18B20(unsigned char dat)
{
u8 j;
u8 testb;
for (j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if (testb)
{
DS_OUT=0;// Write 1
delay_us(2);
DS_OUT=1;
delay_us(60);
}
else
{
DS_OUT=0;// Write 0
delay_us(60);
DS_OUT=1;
delay_us(2);
}
}
}
/* 从DS18B20读取一个字节,返回值-读到的字节 */
unsigned char Read18B20()
{
u8 i,j,dat;
dat=0;
for (i=1;i<=8;i++)
{
j=DS18B20_Read_Bit();
dat=(j<<7)|(dat>>1);
}
return dat;
}
/* 启动一次18B20温度转换,返回值-表示是否启动成功 */
void Start18B20()
{
unsigned char ack=1;
ack = Get18B20Ack(); //执行总线复位,并获取18B20应答
if (ack == 0) //如18B20正确应答,则启动一次转换
{
Write18B20(0xCC); //跳过ROM操作
Write18B20(0x44); //启动一次温度转换
}
}
void ReadRom(void)//读ROM
{
unsigned char index;
bit ack;
ack = Get18B20Ack(); //执行总线复位,并获取18B20应答
if (ack == 0) //如18B20正确应答,则启动一次转换
{
Write18B20(0x33); //读ROM
}
for (index = 0;index < 8;index++)
{
rom1[index] = Read18B20();
}
}
void CheckRom(unsigned char a) //匹配ROM命令
{
unsigned char j; //用于循环
Get18B20Ack(); //执行总线复位,并获取18B20应答
Write18B20(0x55); //发送匹配ROM命令
if(a==1)
{
for(j=0;j<8;j++)
{
Write18B20(rom1[j]);//发送18B20的序列号,先发送低字节
}
}else if(a==2)
{
for(j=0;j<8;j++)
{
Write18B20(rom2[j]);//发送18B20的序列号,先发送低字节
}
}
}
/* 读取DS18B20转换的温度值,返回值-表示是否读取成功 */
unsigned char Get18B20Temp1(int *temp)
{
unsigned char ack=1;
unsigned char LSB, MSB; //16bit温度值的低字节和高字节
ack = Get18B20Ack(); //执行总线复位,并获取18B20应答
if (ack == 0) //如18B20正确应答,则读取温度值
{
//Write18B20(0xCC); //跳过ROM操作
CheckRom(1);
Write18B20(0xBE); //发送读命令
LSB = Read18B20(); //读温度值的低字节
MSB = Read18B20(); //读温度值的高字节
*temp = ((int)MSB << 8) + LSB; //合成为16bit整型数
}
return ack; //ack==0表示操作应答,所以返回值为其取反值
}
void Get_Temp1()//获取温度
{
unsigned char res=1;
int temp; //读取到的当前温度值
int intT, decT; //温度值的整数和小数部分
//unsigned char wenduzs,wenduxs;//整数 小数
res = Get18B20Temp1(&temp); //读取当前温度
if (res==0) //读取成功时,刷新当前温度显示
{
if(temp<0)//把负数变正数
{
temp=~(temp-1);//把负数变为正数
intT = temp >> 4; //右移后16进制转换为十进制值刚好等于温度整数值,分离出温度值整数部分0.0625
}
else
{
intT = temp >> 4; //右移后16进制转换为十进制值刚好等于温度整数值,分离出温度值整数部分0.0625
}
decT = temp & 0x000F; //分离出温度值小数部分
decT = (decT*10) / 16; //1/16=0.0625,除以16等于乘以0.0625,乘以10就保留了1位小数了。二进制的小数部分转换为1位十进制位
//wenduzs=(unsigned char)intT;//整除
//wenduxs=(unsigned char)decT;//小数
//wendubuf[0]=wenduzs;//把整数存入暂存数组0
//wendubuf[1]=wenduxs;//把小数存入暂存数组1 注意 小数是*10后的值
wendubuf1[0]=(intT*10+decT)>>8;//高8位
wendubuf1[1]=(unsigned char)(intT*10+decT);//低8位
}
Start18B20(); //重新启动下一次转换
}
/* 读取DS18B20转换的温度值,返回值-表示是否读取成功 */
unsigned char Get18B20Temp2(int *temp)
{
unsigned char ack=1;
unsigned char LSB, MSB; //16bit温度值的低字节和高字节
ack = Get18B20Ack(); //执行总线复位,并获取18B20应答
if (ack == 0) //如18B20正确应答,则读取温度值
{
//Write18B20(0xCC); //跳过ROM操作
CheckRom(2);
Write18B20(0xBE); //发送读命令
LSB = Read18B20(); //读温度值的低字节
MSB = Read18B20(); //读温度值的高字节
*temp = ((int)MSB << 8) + LSB; //合成为16bit整型数
}
return ack; //ack==0表示操作应答,所以返回值为其取反值
}
void Get_Temp2()//获取温度
{
unsigned char res=1;
int temp; //读取到的当前温度值
int intT, decT; //温度值的整数和小数部分
//unsigned char wenduzs,wenduxs;//整数 小数
res = Get18B20Temp2(&temp); //读取当前温度
if (res==0) //读取成功时,刷新当前温度显示
{
if(temp<0)//把负数变正数
{
temp=~(temp-1);//把负数变为正数
intT = temp >> 4; //右移后16进制转换为十进制值刚好等于温度整数值,分离出温度值整数部分0.0625
}
else
{
intT = temp >> 4; //右移后16进制转换为十进制值刚好等于温度整数值,分离出温度值整数部分0.0625
}
decT = temp & 0x000F; //分离出温度值小数部分
decT = (decT*10) / 16; //1/16=0.0625,除以16等于乘以0.0625,乘以10就保留了1位小数了。二进制的小数部分转换为1位十进制位
//wenduzs=(unsigned char)intT;//整除
//wenduxs=(unsigned char)decT;//小数
//wendubuf[0]=wenduzs;//把整数存入暂存数组0
//wendubuf[1]=wenduxs;//把小数存入暂存数组1 注意 小数是*10后的值
wendubuf2[0]=(intT*10+decT)>>8;//高8位
wendubuf2[1]=(unsigned char)(intT*10+decT);//低8位
}
Start18B20(); //重新启动下一次转换
}
ds18b20.h文件的代码
```c
#ifndef _DS18B20_H
#define _DS18B20_H
#include "config.h"
sbit DS_OUT=P3^3;
extern unsigned char wendubuf1[2];//wendubuf[0]存扩大10倍的温度高8位 wendubuf[1]存扩大10倍的温度低8位 比如326 为32.6
extern unsigned char wendubuf2[2];//wendubuf[0]存扩大10倍的温度高8位 wendubuf[1]存扩大10倍的温度低8位 比如326 为32.6
extern unsigned char rom1[8];
extern unsigned char rom2[8];
void Start18B20(void);// 启动一次18B20温度转换,返回值-表示是否启动成功 */ 初始化值调用1次
void Get_Temp1(void);//温度处理函数,包括把温度值存入数组 获取温度值调用
void Get_Temp2(void);//温度处理函数,包括把温度值存入数组 获取温度值调用
void Ds18B20_Gpio_Init(void);// IO口初始化
void DS18B20Init();//温度传感器
void ReadRom(void);//读ROM
#endif
main.c文件代码
#include "ds18b20.h"
void main()
{
DS18B20Init();//温度传感器
while(1)
{
if(bitVar.flag1S==1) //1s标志
{
bitVar.flag1S=0;
if(bitVar.change==0)
{
Get_Temp1();
bitVar.change=1;
}else
{
Get_Temp2();
bitVar.change=0;
}
wenDu.wenDu1=( (wendubuf1[0]<<8)+wendubuf1[1] );
wenDu.wenDu2=( (wendubuf2[0]<<8)+wendubuf2[1] );
//ReadRom();//读ROM时用串口把序列号输出来
// UartWrite(rom1, 8); //串口输出
}
}
}