2回答

1收藏

[原创] 关于51驱动DS18B20的一些看法

51单片机 51单片机 3205 人阅读 | 2 人回复 | 2013-09-08

很多人用51第一次驱动DS18B20或多或少都会遇到一些问题。可能读出来是85.0,可能是e7.5(字符液晶显示),就是得不到正确值。而出现这些情况,基本上是时序错误导致(当然DS18B20损坏也会导致读出85)
一般人喜欢直接把网上的驱动直接复制过来用,这也是导致时序出错的重要原因,因为对于DS18B20这种单总线操作的器件,其时序间隙是以微秒(us)计算的,而大部分都是使用一个While(x--)的方式来得到延时时间,这样对于不同系列的单片机,不同晶振都会导致延时时间发生变化,从而导致时序出错。使用STC系列单片机的,即使是同一个晶振下,不同系列(如12系列和89系列)也会导致该延时函数执行的时间不同,通常情况下该演示程序的执行时间会有几倍等级的变化。
如何解决这个问题呢?以下是我的个人经验了。只提供大概思想,具体还是留给各位发挥吧
首先,假定我有一个延时程序,延时时间就为1us*倍数(void Delay_us(unsigned char i)),最大为255 us。为什么不直接定义一个iunsigned nt型变量呢?这样不就可以延时65535us吗?实际上,DS18B20的时序间隙最大也就650us左右,如果使用unsigned int型变量一方面浪费内存,一方面增加延时时间确定的难易程度。
按DS18B20的说明手册编写DS18B20的读写和复位时序。时序间隙时间都取要求时间间隙的中间值偏小一点。很多人认为自己写时序很困难,实际上,只有写过的人才清楚,DS18B20的时序真的很简单。
最后写1us产生时间。我一般是这样确定的:写一个while(x--)的延时函数,如
void Delay_Temp()  //?个机器周期*a  (标示执行一次的时钟周期方便以后不同频率时计算)
{
unsigned chat a=1;
while(a--);
}
将a赋值为1,在main函数中调用该函数,编译后进入调试,单步执行,查看该子程序的汇编语句,查看单片机手册各汇编指令执行周期,将执行一次循环体的总时间周期标志在该函数名后注释。根据晶振计算一下执行一次的时间,用1us除去即得到a值,把a重新赋值,那么这个函数就是近似1us的程序了(对于1T单片机并且时钟大于10MHz,若是12T或6T时钟频率又较低的最好考虑进Call和RET指令的时间)。
最后再编写一个
Void Delay_us(unsigned char Set_us)
{
  while(Set_us--)
   Delay_Temp();
}
以后换单片机(仅限于51)只要计算一下Delay_Temp的a值使该函数为1us即可。
我使用以上的办法,在STC不同系列不同晶振的硬件中都完美驱动DS18B20,而且改动程序不超过5秒(只是计算一下a值而已)
最后说一下为什么把DS18B20时间间隙设置为中间值偏小一点?很明显在之前延时函数时间的确定中,尤其是Delay_us中也是不断地循环调用Delay_Temp,会有额外的LCALL等指令时间。产生额外延时时间。若要计算得非常准确,可以在Delay_Temp的计算中加入进入该函数的LACLL等指令的值,以提高准确度。
另外再提一下,有些人得到温度是使用浮点数计算,这是非常浪费资源的做法,通常我们都是使用乘法加移位的办法获得最终温度数据(定点小数,即获取的温度为整型值,显示时认为加小数点),这样代码量起码减少1KB。此外,温度是有符号数,这点很多人经常忽略了。
还有,有人担心DS18B20自身发热会不会影响测量结果。经过实验对比,没什么影响
DS18B20上电后第一次可能读出来是85,这是正常的,可通过软件方法屏蔽。
使读出数据波动不大的办法,我采用的办法是连续读两次,若两次读取结果相同,则更新温度,而不是采用均值的办法。
以上仅是个人一点经验,欢迎大家指正


评分

参与人数 1声望 +3 与非币 +5 收起 理由
【小沫】 + 3 + 5

查看全部评分

我从不担心我努力了不优秀,只担心优秀的人都比我更努力。如果你无法忍受孤独,就不要追逐梦想。
分享到:
回复

使用道具 举报

回答|共 2 个

倒序浏览

沙发

hjf2002

发表于 2013-9-8 09:58:26 | 只看该作者

其实1-wire总线最主要的看懂其中的协议,Datasheet多看几遍就懂了。
板凳

Athos

发表于 2013-9-18 21:10:08 | 只看该作者

我一直没有写过时序,但是感觉这很重要,因为我在用STM32  的时候发现真的很需要时序的感觉
您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条