问题背景

DS18B20 是一款温度传感器,单片机可以通过 1-Wire 协议与 DS18B20 进行通信,最终将温度读出。1-Wire 总线的硬件接口很简单,只需要把 DS18B20 的数据引脚和单片机的一个 IO 口接上就可以了。硬件的简单,随之而来的,就是软件时序的复杂。

在每个 DS18B20 内部都有一个唯一的 64 位长的序列号,这个序列号值就存在 DS18B20内部的 ROM 中。开始的 8 位是产品类型编码(DS18B20 是 0x28),接着的 48 位是每个器件唯一的序号,最后的 8 位是 CRC 校验码。DS18B20 可以引出去很长的线,最长可以到几十米,测不同位置的温度。单片机可以通过和 DS18B20 之间的通信,获取每个传感器所采集到的温度信息,也可以同时给所有的 DS18B20 发送一些指令。

当一根很长的线上挂了很多个DS18B20测不同位置的温度时,由于是同一个总线上,需要根据每个器件的唯一序列号分别对每个器件进行温度值采样,DS18B20的通信协议有对此进行考虑,可以发序列号进行匹配,总线上所有器件在收到序列号后只有和自身序列号匹配的才能响应。

但怎么能事先知道每个器件的序列号?有些人说要先单个读取每个器件的序列号,把读到的序列号在程序的数组中表示,然后实际应用时就能对总线上每个DS18B20器件单个进行匹配读取。

这种方法在实际应用场景是完全不可能的,存在以下问题:
1.实际场景每个控制板对应不同的多个DS18B20,怎么单独读每个DS18B20的序列号是个大问题,严重降低生产效率
2.即使读到了每个控制板对应的多个DS18B20的序列号,那么需要把这些序列号发送给控制板进行保存,这个过程也很繁杂
3.即使上面2点都这样做了,那么如果产品使用中总线上某个DS18B20坏了,那维护过程也很困难,需要单独读取用于更换的DS18B20的序列号,然后发送给控制板使其改变内部的扫描列表

所以事先单个读取每个器件的序列号进行保存这样的方法肯定是行不通的,只能开机就能自动扫描总线上所有器件的序列号,然后再根据序列号去读取才是正常的逻辑。好在DS18B20的 1-Wire 通讯协议设计了扫描序列号的方法。有专用于扫描的指令,细节可继续往下看。

解决思路

首先了解一下单总线的硬件结构:

这个图里说明,DS18B20在输出时,是靠拉低DQ通信线实现低电平输出,靠释放DQ通信线(被上拉电阻拉高)来实现高电平输出。

也就是说它可以实现“线与”。多个DS18B20同时输出时,只要有一个低电平,则DQ通信线就会被拉低,只有低电平能被识别,只有都为高电平时,总线上才是高电平。单总线上识别不同的DS18B20就是靠“线与”来解决通信冲突的。(这个特点类似I2C总线)

DS18B20的datasheet中有这样一部分流程图:

F0是搜索ROM的指令,上面的流程图可以看出通过单总线通过F0指令获取DS18B20内部序列号ID的方法,访问每个bit时,分为三步:

先读取第一位;

再读取第一位的补码;

通过读取的数据和补码,和下表对应的结论,来判断总线上的器件状态:

然后写一位数据,告诉从器件,主机选择了哪些器件继续通信;如果从机当前ROM码全为0,则写0;如果从机当前ROM码全为1,则写1;如果从机当前ROM码有0也有1,则主机可以先写0,选择为0的器件继续通信;等本次后续的bit位都搜索完后,再回到第一个位冲突处,选择为1的器件,到另一个分支搜索。

主机通过不断循环、回溯,完成所有器件的搜索,获取所有器件的序列号。

下面举一个详细的例子来帮助理解:
(以下转载自https://blog.csdn.net/little_grapes/article/details/121917737)

假如总线上挂载了三个DS18B20,我们命名为a、b、c器件,为简化处理,假设器件ID只有两位,分别是:01、00、11;MCU从低位开始读取。

a)第一轮通信

主机MCU先读取第1位,a的最低位为1、b的最低位为0、c的最低位为1,由于最低位有0,所以总线数据会被识别为0;

再读取第1位的补码,a的最低位补码为0、b的最低位补码为1、c的最低位补码为0,则总线数据也会被识别为0(有0就为0,全1才为1);

主机MCU识别到两次都为0,则知道总线上有器件为0、也有器件为1;则主机MCU需要选择一条路径继续访问;如果我们的算法选择优先访问0的路径;则向总线上写入一个0位;三个从器件收到发送来的0后,只有b器件的对应位符合,则下一次通信时,a、c器件都不会再响应;

b)第二轮通信

主机MCU开始读取第2位,由于只有b器件响应,则读到0;

再读取第2位的补码,由于只有b器件响应,可以读到1;

主机MCU两次读取到0、1,则可以判定总线上的器件该位为0;

又因为,上一轮选择的是0路径,加上本次识别到的0,则可以断定,总线上有一个器件的ID是00;识别完ID的总长度(2位)后,本次搜索结束。

c)回溯后再次通信

与第一轮通信类似,最后写入的位改为1;则只有a、c器件的对应位符合,后续只有a、c器件会继续通信,b器件不再响应;

主机MCU开始读取第二位,读取到0;

读取补码,读到0;

则主机可以断定,第二位有0、也有1;再加上上一轮通信中选择的1路径,则可以断定,总线上有两个器件:10和11。

至此,所有ID搜索完毕,搜索的示意见下图:

实际的DS18B20的ID有64bit,上面的例子只有2bit,搜索的原理是一样的,以此可以获取总线上所有器件的ID,之后就可以通过ID来访问特定器件了。

按上述方法实现搜索ID的代码如下所示:

#define MaxSensorNum 8
unsigned char DS18B20_ID[MaxSensorNum][8];  // 存检测到的传感器DS18B20_ID的数组,前面的维数代表单根线传感器数量上限
unsigned char DS18B20_SensorNum;            // 检测到的传感器数量(从1开始,例如显示1代表1个,8代表8个)// 自动搜索ROM
void DS18B20_Search_Rom(void)
{uint8_t k, l, chongtuwei, m, n, num;uint8_t zhan[64+1];//按顺序保存冲突位的位号uint8_t ss[64];//保存64个bitl = 0;num = 0;do{DS18B20_Rst(); //注意:复位的延时不够delay_us(750); //480、720DS18B20_Write_Byte(0xf0);for (m = 0; m < 8; m++)//8 * 8 = 64bit ID{uint8_t s = 0;//用于缓存当前读到的字节位for (n = 0; n < 8; n++)//8bit{k = DS18B20_Read_2Bit();//读两位数据k = k & 0x03;s >>= 1;//低位开始if (k == 0x01)//01读到的数据为0 写0 此位为0的器件响应{DS18B20_Write_Bit(0);ss[(m * 8 + n)] = 0;}else if (k == 0x02)//10读到的数据为1 写1 此位为1的器件响应{s = s | 0x80;DS18B20_Write_Bit(1);ss[(m * 8 + n)] = 1;}else if (k == 0x00)//读到的数据为00 有冲突位 判断冲突位{//如果冲突位大于栈顶写0 小于栈顶写以前数据 等于栈顶写1chongtuwei = m * 8 + n + 1;if (chongtuwei > zhan[l]){DS18B20_Write_Bit(0);ss[(m * 8 + n)] = 0;zhan[++l] = chongtuwei;//记录该新的冲突位}else if (chongtuwei < zhan[l]){s = s | ((ss[(m * 8 + n)] & 0x01) << 7);DS18B20_Write_Bit(ss[(m * 8 + n)]);}else if (chongtuwei == zhan[l]){s = s | 0x80;DS18B20_Write_Bit(1);ss[(m * 8 + n)] = 1;l = l - 1;}}else{//没有搜索到break;}}DS18B20_ID[num][m] = s; // 保存搜索到的ID字节}num = num + 1;// 保存搜索到的个数} while (zhan[l] != 0 && (num < MaxSensorNum));//测试完所有冲突位,或搜索到的个数大于最大数则退出DS18B20_SensorNum = num;
}

CRC校验

DS18B20 在读取8字节ROM和9字节暂存器时,最后一个字节都是前面所有字节的CRC校验值。我们在读ROM时,最好要校验一下CRC是否正确,否则要认为读取的数据有误。

计算CRC的等效多项式为(这是datasheet中的式子, 并非幂运算, 要结合后面的流程图理解)

CRC=X8+X5+X4+1

1-Wire总线的CRC计算由移位寄存器和异或门组成的多项式发生器来执行: 移位寄存器位初始化为0, 然后从第一个字节的最低位开始, 一次移入一位, 根据计算结果决定是否与第4, 第5位作异或, 然后CRC也往右移, 最后移位寄存器的值就是CRC.

从实现方法上可以分为两个方向,一个是查表法,还有一个是计算法。
通过计算:运行速度相对慢,占用内存相对小。以时间换空间
通过查表:运行速度相对快,占用内存相对大。以空间换时间
最终方法的使用,酌情考虑。

通过计算得到8位CRC:

unsigned char DS18B20_Crc8(unsigned char* dat, unsigned char len)
{unsigned char i, x; unsigned char crcbuff;unsigned char crc = 0;for (x = 0; x < len; x++){crcbuff = dat[x];for (i = 0; i < 8; i++){if (((crc ^ crcbuff) & 0x01) == 0)crc >>= 1;else {crc ^= 0x18; //CRC=X8+X5+X4+1crc >>= 1;crc |= 0x80;}crcbuff >>= 1;}}return crc;
}

通过查表得到8位CRC:

unsigned char dscrc_table[] = {0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83,0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41,0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e,0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc,0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0,0xe1, 0xbf, 0x5d, 0x03, 0x80, 0xde, 0x3c, 0x62,0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d,0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff,0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5,0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x07,0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58,0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a,0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6,0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24,0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b,0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9,0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0x0f,0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd,0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92,0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50,0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c,0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee,0x32, 0x6c, 0x8e, 0xd0, 0x53, 0x0d, 0xef, 0xb1,0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73,0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49,0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b,0x57, 0x09, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4,0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16,0xe9, 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a,0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8,0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7,0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35
};unsigned char DS18B20_Crc8(unsigned char* dat, unsigned char len)
{unsigned char crc = 0;for (x = 0; x < len; x++){crc = dscrc_table[crc ^ dat[x]];}return crc;
}

获取温度

经过上面的步骤,扫描了所有的ROM,然后CRC校验也没问题,那么接着就用0x55指令匹配ROM对每个ROM分别进行读取温度的操作即可。
代码示例:

float DS18B20_Get_Temp_ID(uint8_t* ID)
{//u8 flag;uint8_t j;//匹配的字节uint8_t TL, TH;short Temperature;float Temperature1;DS18B20_Rst();DS18B20_Check();DS18B20_Write_Byte(0xcc);// skip romDS18B20_Write_Byte(0x44);// convertDS18B20_Rst();DS18B20_Check();// DS18B20_Write_Byte(0xcc);// skip rom//匹配ID,i为形参DS18B20_Write_Byte(0x55);for (j = 0; j < 8; j++){DS18B20_Write_Byte(ID[j]);}DS18B20_Write_Byte(0xbe);// convertTL = DS18B20_Read_Byte(); // LSBTH = DS18B20_Read_Byte(); // MSBif (TH & 0xfc){//flag=1;Temperature = (TH << 8) | TL;Temperature1 = (~Temperature) + 1;Temperature1 *= 0.0625;}else{//flag=0;Temperature1 = ((TH << 8) | TL)*0.0625;}return Temperature1;
}
     for(int num=0;num<DS18B20_SensorNum;num++){log_debug("ID:");for(int i=0;i<8;i++){log_debug("%02x",DS18B20_ID[num][i]);}log_debug("  TM:%.2f\r\n",DS18B20_Get_Temp_ID((uint8_t *)DS18B20_ID[num]));}

推荐:

https://blog.csdn.net/little_grapes/article/details/121917737

https://blog.csdn.net/weixin_44457994/article/details/121758826

https://blog.csdn.net/weixin_39827856/article/details/125134565

1-Wire总线上挂载多个DS18B20温度传感器驱动程序相关推荐

  1. IIC总线上挂载多个从机的程序实现

    文章目录 IIC总线上挂载多个从机的程序实现 IIC简介: 1.项目的硬件参考电路: 2.程序实现: 2.1.程序框架设计: 2.2 IIC 总线接口程序实现: 2.3 加速度传感器的配置: 2.4 ...

  2. 总线上挂多个DS18B20器件读取方法

    DS18B20 是美信公司的一款温度传感器,单片机可以通过 1-Wire 协议与 DS18B20 进行通信,最终将温度读出.1-Wire 总线的硬件接口很简单,只需要把 DS18B20 的数据引脚和单 ...

  3. DS18B20温度传感器 ------ 自学笔记

    目录 一.简介 二.DS18B20的特点 三.DS18B20实物图 四.DS18B20的内部结构 4.1.64位(激)光刻只读存储器 4.2.DS18B20温度转换规则 4.3.DS18B20温度传感 ...

  4. 使用树莓派GPIO17和GPIO18连接DS18B20温度传感器(关于DS18B20的时序设置)

    一.树莓派自带W1-GPIO读取 在树莓派中有一种自带的查看DS18B20温度信息,已经写好DS18B20时序,对外设设备进行读取信息然后保存的值在/sys/bus/w1/devices/28-xxx ...

  5. 超声波测距系统(stc89c52单片机+lcd1602+srf04超声波传感器+DS18B20温度传感器+无源蜂鸣器)

    求赞,它是我写博客的动力,请鼓励鼓励我,谢谢 基于超声波测距的倒车报警系统. 计划通过超声波传感器测距,温度传感器校正误差,lcd1602显示结果,距离小于某一值时将蜂鸣报警. 总体设计思路 HC-S ...

  6. VHDL语言编写DS18B20温度传感器程序详解

    网上关于DS18B20的资料很多,但是光有程序,没有讲解,导致身边很多同学即使拿到源码也无从下手,故写此篇文章,一方面接收DB18B20,一方结合原理详细讲解源码的意义. 器件原理: 原理图:     ...

  7. STM32外设有哪些?外设在总线上是怎么挂载的?

    一:STM32外设有哪些? 外设在STM32库文件的下面路径下:(STM32库:stm32f10x_stdperiph_lib) stm32f10x_stdperiph_lib\STM32F10x_S ...

  8. 将GPIO外设挂到Cortex_M3 AHB总线上详细流程扩展外设步骤总结

    使用一个外设之前,你要弄明白这个外设是干什么的,它是怎样工作的,它的输入输出接口都是啥,你还要知道怎样给外设分配地址. GPIO的具体硬件结构在这里不多说了.(如果你要自己写代码,那硬件结构必须弄得明 ...

  9. 【51单片机】AT24C02存储器(I²C总线)/DS18B20温度传感器(单总线)

    目录 一.AT24C02存储器 1.AT24C02存储器介绍 2.存储器简化模型 3.AT24C02存储器原理图 二.I²C总线 1.I²C总线的介绍 2.I²C电路 3.I²C时序图 3.1I²C开 ...

最新文章

  1. 利用https访问站点(基于linux系统)
  2. CCS编译出错:缺少头文件的解决办法
  3. 我的第一个REST客户端程序!
  4. 南华大学计算机学院吴取劲,一种基于图深度优先搜索的基本路径集自动生成优化算法-南华大学学报.PDF...
  5. Kafka的rebalance机制
  6. python excel详解_Python - excel 详解
  7. 中国人去日本买电饭煲令人痛心!董明珠:难道我泱泱大国造不出吗?
  8. 2018.10.27 bzoj3209: 花神的数论题(数位dp)
  9. 设置SVN忽略文件和文件夹(文件夹)
  10. 标准3层神经网络搭建Demo
  11. Android图片剪裁库:uCrop
  12. java校园圈子论坛跳蚤市场小程序源码
  13. pmf-automl源码分析
  14. 2017-百度-安全岗笔试
  15. 关于esxi主机上vm无法ping通edge网关设备ip的处理
  16. 编码员,程序员,黑客,开发人员和计算机科学家走进维恩图
  17. px em pt单位区别
  18. Sybase数据库常用时间操作
  19. Go调用webservice
  20. “共享”日本旅游车票 销售电商已消失

热门文章

  1. 接上篇,CNN在短文本分类中的应用遇到的一些问题/GPU/cuda/tensorflow
  2. 测试策略模板——Test Strategy(中英文)
  3. 1120.泽泽在英国
  4. 互联网本来很简单,但为啥人们看的那么复杂
  5. java开发杂项总结
  6. OO Design Principles(OO设计原则): SOLID
  7. 冒泡,选择,快速,二叉树排序速度比较
  8. java输入名字和语句_java键盘输入语句怎么写
  9. SNAP实现DInSAR
  10. 高中教师资格证笔试计算机学科知识与能力,2019下半年浙江教师资格证高中《信息技术学科知识与教学能力》真题及答案解析...