DS18B20是常用的一款数字型温度传感器,具有结构简单、抗干扰能力强、精度高、价格便宜等的优点。本文以它为例总结一些单总线数字传感器在使用过程中需要注意的一些事项,以供大家参考。
文章目录
硬件综述
在介绍这种传感器之前,先来梳理一下什么叫单总线数字传感器,个人理解为:所谓单总线,就是只有一根引脚用作主机和从机之间数据传递,如果是数字型的传感器,一般这根线一般还要承载时钟同步的任务。
DS18B20就是上面所说的那样一种传感器,如下图(图片来自百度):
它有3根引脚,但只有中间的那一根才用做数据交互、时钟同步等。对于这样的单总线数字传感器来说,它对主机产生的时序信号要求十分严格,主机产生的时序稍“不合它意”,就会出现时序紊乱的后果,最终会造成传感器不响应,这样也就不会有输出了。所以在使用这类传感器的时候,切记时序要严格控制。
基于51单片机的代码
以常用的51单片机为例,这里我们给出该传感器的驱动程序,首先是传感器的初始化(也称为复位),其时序应为:主机先将总线电平拉低一段时间,数据手册上说500us左右,但实际上可能存在传感器响应较快,而且单片机执行延时代码的时候,会额外占用一小部分机器周期,所以,延时的时候一般 50 − 100 u s 50-100us 50−100us就差不多了;然后释放总线,等待传感器的响应,等待时间约为 15 − 60 u s 15-60us 15−60us,但实际时间 5 − 10 u s 5-10us 5−10us就差不多了;之后,如果总线电平为0,说明响应成功,否则失败。具体可以参考我下面贴出来的代码:
void delay(unsigned int t) //延时,重要!单总线传感器对时序要求比较严苛,请不要轻易修改此函数!
{
for(;t>0;t--) ;
}
bit DS18B20_Init(void) //初始化;更多参考资料,尽在微信公众号“24K纯学渣”
{
bit pre;
DQ = 0;
delay(50);
DQ = 1;
delay(3);
pre = DQ;
delay(25);
return pre;
}
之后就是往传感器里面写数据和读数据了,写1bit的时序为:写0的话,主机需要将总线拉低至少60us;写1的时候,先拉低15us,再拉高45us左右。读1bit数据的时序为:主机先将总线拉低15us,然后释放总线,接着的45us的时间内就是读取到的数据,也就是说,读和写的基本时间单位都为60us左右。下面以字节为单位,给出示例代码:
unsigned char TempReadByte(void) //读取字节数据;更多参考资料,尽在微信公众号“24K纯学渣”
{
unsigned char i;
unsigned char v;
for(i=8;i>0;i--)
{
v >>= 1;
DQ = 0;
DQ = 1;
delay(1);
if(DQ) v |= 0x80;
delay(6);
}
return v;
}
void TempWriteByte(unsigned char dat)
{
unsigned char i;
for(i=8;i>0;i--)
{
DQ = 0;
DQ = dat&0x01;
delay(5);
DQ = 1;
dat /= 2;
}
delay(5);
}
然后就是读取温度值了。既然是数字型传感器,那么就一定存在配置寄存器、读数据寄存器等流程,DS18B20的寄存器内部存储结构主要分为两部分,一部分是ROM,存放的是64位的序列号,另一部分才是我们感兴趣的数据或控制寄存器,以字节为单位总有8B,分别为:温度转换后的低字节、温度转换后的高字节、高温触发器(TH)、低温触发器(TL)、配置寄存器、3B的保留和最后的CRC校验字节。这些传感器中,我们只感兴趣的是前两个字节,它有16位,但是16bit中高5位为符号位(1为负、0为正)、紧接着7bit为整数部分,最后4bit为小数部分,所以读取出这16bit的数据之后还要做进一步的计算。下面给出获取一次温度数据的例子:
//更多参考资料,尽在微信公众号“24K纯学渣”
float getTemp(void) //获取一次温度数据,返回的数据可精确到0.0625
{
unsigned char tl,th;
unsigned int tt;
float temp = 0.0;
bit fg; //标记正负;更多参考资料,尽在微信公众号“24K纯学渣”
DS18B20_Init();
TempWriteByte(0xcc); //直接往控制寄存器写入命令
TempWriteByte(0xbe); //准备从数据寄存器读取数据;更多参考资料,尽在微信公众号“24K纯学渣”
tl = TempReadByte();
th = TempReadByte();
DS18B20_Init();
TempWriteByte(0xcc);
TempWriteByte(0x44); //转换,为下次读取做准备
tt = th*256+tl;
fg = 0;
if(tt&0xf800) //如果高5位为11111,则说明温度值为负
{
tt = ~tt + 1;
fg = 1;
}
temp = tt/16.0; //转化为浮点数
if(fg) temp *= (-1);
return temp;
}
最后,借助51的串口,把数据以保留一位小数的格式打印到串口助手上,代码如下:
void main(void)
{
float temp;
int Itemp;
char buf[8] = "ERROR! "; //如果DS18B20初始化失败或其他原因,将会发送错误提示字符串
uartInit();
while(1)
{
temp = getTemp(); //计算得到一次浮点型温度数值
Itemp = (int)(temp * 10); //可保留一位小数
if(temp < 0) //标注正负符号
{
buf[0] = '-';
Itemp = 0 - Itemp;
}
else
{
buf[0] = '+';
}
buf[1] = Itemp / 1000 + 0x30; //把浮点型数据转化为字符串
buf[2] = (Itemp % 1000) / 100 + '0';
buf[3] = (Itemp %100) / 10 + '0';
buf[4] = '.';
buf[5] = Itemp % 10 + '0';
buf[6] = ' ';
sendString(buf,7); //发送数据
delay_ms(1000);
}
return;
}
总结
对于单总线数字传感器,这里还要再提一下,因为时序问题真的非常重要,如果时序不正确,初始化的时候就会失败,也就没有后面所谓的读写数据了,所以在调试的过程中,如遇到读不到数据的情况,很有可能就是时序问题导致的。最后的最后,完整的Keil Project共享在微信公众号“24K纯学渣”上面,回复“温度传感器”即可获取。