DS18B20这个温度传感器是非常常见的,起初使用时也没有太在意,拿原子哥的例程直接跑,发现温度也可以读取到。
但是到实际做项目了发现要用好也没有那么容易。
负温度的换算
负温度换算时要加上1,至于为什么,就是二进制有符号数转换的那个取反加1.
if(TH>7)
{
TH=~TH;
TL=~TL;
TL =TL+1;//这个加1别忘记 这个是错误的会导致一个BUG
temp=0;//温度为负
}else temp=1;//温度为正
注意!!!上述的计算方法有误
上述的计算会在某些数据上出错,如读取到TH为0XFF,TL为0x00,此时因为TH和TL是分别取反的,所以会导致结果为0x0000,而正确的结果是0x0100,因为TL取反后是0xFF,再加1会变成0x100,但是无法进位所以是错的。
修正后的代码如下
u8 TL,TH;
short tem,value;
tem = TH;
tem = tem<<8;
tem = tem | TL;
if(tem < 0)
{
tem = (short)((~tem + 1) *0.625);
tem = -tem;
}
else
tem = (short)(tem *0.625);
value = tem;
读取温度会跳变
由于DS18B20有可能不是直接焊接在板子上的,如果线过长就会影响到数据的读取,然后读取到不正常的数,导致温度跳变。这里除了可以用一些滤波算法外,更重要的是对数据的校验。DS18B20就自带了CRC8的校验。
代码如下
int DS18B20_Get_Temp(void)
{
volatile uint8_t temp;
volatile uint8_t TL,TH;
volatile short tem;
volatile int value,calibration;
uint8_t tab[9],Crc8;
DS18B20_Start (); //开始转换
DS18B20_Rst();
//DS18B20_Check();
if(DS18B20_Check()==1)
{
tem = -9999;//-9999,表示传感器故障
return tem;
}
DS18B20_Write_Byte(0xcc); // skip rom
DS18B20_Write_Byte(0xbe); // convert
tab[0]=TL=DS18B20_Read_Byte(); // LSB
tab[1]=TH=DS18B20_Read_Byte(); // MSB
tab[2]=DS18B20_Read_Byte(); //
tab[3]=DS18B20_Read_Byte(); //
tab[4]=DS18B20_Read_Byte(); //
tab[5]=DS18B20_Read_Byte(); //
tab[6]=DS18B20_Read_Byte(); //
tab[7]=DS18B20_Read_Byte(); //
tab[8]=DS18B20_Read_Byte(); // CRC
Crc8 = DS18B20_Crc(tab,8);
if(Crc8!=tab[8]) //CRC校验
{
return 0x7FFF;
}
if(TH>7)
{
TH=~TH;
TL=~TL;
TL =TL;
temp=0;//温度为负
}else temp=1;//温度为正
tem=TH; //获得高八位
tem<<=8;
tem+=TL;//获得底八位
tem=((float)tem)*0.625;//转换
if(temp)
{
value = tem;
}
else
{
value = -tem;
}
return value;
}
uint8_t DS18B20_Crc(uint8_t *addr, uint8_t len)
{
uint8_t crc = 0, inbyte, i, mix;
while (len--)
{
// inbyte 存储当前参与计算的新字节
inbyte = *addr++;
for (i = 8; i; i--)
{
// 将新字节与CRC从低位到高位, 依次做异或运算, 每次运算完CRC右移一位
// 如果运算结果值为1, 则将CRC与 1000 1100 作异或
// 第3,4位代表流程图中的异或运算, 第7位其实就是运算结果移入的1
mix = (crc ^ inbyte) & 0x01;
crc >>= 1;
if (mix)
{
crc ^= 0x8C;
}
inbyte >>= 1;
}
}
return crc;
}
数据一直读取到FF
这个问题是由于,在配置IO输入时,使用了库函数,尤其是HAL库,这样会导致某些情况下根本IO根本读取不到低电平,所以虽然DS18B20 正确的返回的数据,(用逻辑分析仪或者示波器看波形都是正确的),但是就是读取的都是FF,所以配置输入时最好直接修改寄存器。因为我在调试中一样的程序可能换一批板子就无法读取到了,但是改成寄存器配置就可以读取到,这个目前我也是比较疑惑,猜测是库函数的配置时间过长导致的,有想法的朋友可以一起讨论一下。
读取到85的问题
在上述原子哥中的程序,DS18B20_Start 启动转换后马上就接着去读取,这个时读取的其实是上一次转换后的值,实际手册中的介绍是开始转换后要等待一定的时间比如1秒去等待DS18B20完成转换后才可以去读取。按照原子哥的写法没有什么大错,但是某些情况下会导致读取到85。建议开始转换后要进行延时等待,这个延时可以用delay空等,也可以使用定时器,比如先开始转换
DS18B20_Start ();
等待定时器计时完成
开始读取数据
int DS18B20_Get_Temp(void)
{
volatile uint8_t temp;
volatile uint8_t TL,TH;
volatile short tem;
volatile int value,calibration;
uint8_t tab[9],Crc8;
DS18B20_Start (); //开始转换
//应该要延时等待数据转换完成
DS18B20_Rst();
省略。。。。
}