• 51单片机外设篇:DS18B20


    温度传感器

    测温度的方式:物理(汞柱、气压)、电子(金属电性能随温度变化)
    早期:热敏电阻(模拟接口)
    现代:专用sensor(数字接口,如I2C、DS18B20的单总线接口等)

    DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点。

    测温范围:-55°C 到 +125°C

    通信接口:1-Wire(单总线)

    其它特征:可形成总线结构、内置温度报警功能、可寄生供电。

    引脚及应用电路:

    单总线(1-Wire BUS)是由Dallas公司开发的一种通用数据总线:

    一根通信线:DQ,异步、半双工

    单总线只需要一根通信线即可实现数据的双向传输,当采用寄生供电时,还可以省去设备的VDD线路,此时,供电加通信只需要DQ和GND两根线。

    DS18B20的基本特征

    1. 内置集成ADC,外部数字接口
    2. 单总线数字接口,布线成本低
    3. 温度范围宽、精确率高(相对)
    4. 数字值温度分辨率位数可软件设置
    5. 温度阈值报警功能,且阈值可内置存储掉电不丢失
    6. 温度采集速度快(750ms)
    7. 内置唯一64位序列码,CPU可以单线串联无限多个DS18B20
    8. 支持VDD供电,或通过数据总线及内部电容实现寄生电源供电

    综合评价:
    DS18B20是很多年前的东西了
    现在趋向于温度+湿度的综合传感器
    现实应用一般低端用热敏电阻、热电偶,高端用精密传感器
    学习重点和难点是单总线协议的时序编程实现

    注意18B20直接读出绝对温度值不需要参考点。

    单总线协议

    • 首先搞清楚你的系统是单点还是多点
    • DS18B20规定总线上的数据是LSB的(一个字节是从最低位开始传输的)
    • 单总线硬件连接要求:漏极开路式+5K欧姆的上拉电阻(见上方的图)
    • 总线低电平超过480us,从设备将被复位

    1-Wire总线系统即一个总线主设备控制一个或多个从设备。DS18B20始终是一个从设备。当总线上只有一个从设备时,此系统被称为“单节点”系统;当总线上有多个从设备连接时,此系统被称之为“多节点”系统。

    1-Wire总线上所有的命令或者数据的发送接收都是遵循低位先发送的原则。

    单总线协议标准执行步骤

    主机必须按照单总线协议设定好的完整序列和DS18B20通信,每个回合包含3个步骤:初始化+ROM操作指令+功能操作指令。顺序不能错也不能省略任何一个。


    DS18B20自己本身不会主动去进行温度测量,而是需要主控CPU主动发起一个温度转换的过程,这么设计是因为温度转换本身是要耗电的,所以设计为平时待机等待温度转换命令后才去进行温度AD转换。
    主控CPU和DS18B20之间的通信是分周期的,譬如我们要让DS18B20进行温度转换就是一个周期。这个周期包含一个初始化+N个命令。(每个周期的开始都要有一个初始化,然后跟着N个命令)

    初始化过程主要是探测目标DS18B20是否存在,若存在将芯片初始化。


    命令很重要。所以DS18B20是一个典型的“命令-响应”型外设。学习这种外设的关键是命令集。


    ROM操作指令
    DS18B20支持多个芯片串联在一根总线上,也就是所谓的单总线协议,所以必须要主控CPU要能够区分总线上多个18B20,因此有个ROM操作指令来完成这个任务。
    ROM操作指令和温度采集一点关系都没有,所以当我们总线上只有一个18B20的时候ROM操作指令我们不需要去管。
    一旦系统中单总线上有多个18B20,那么我们必须借助ROM操作指令来区分多个18B20,而且这个区分过程可能需要多条ROM指令来完成。
    如果系统中只有一个18B20,那么就用一条skip rom命令(0xCC)就可以跳过这个阶段。

    功能指令
    ROM操作指令目的是为了在单总线上多个18B20中挑选到那个当前我们要操作的18B20,而功能指令是为了和选定的18B20通信从而获取温度。

    初始化:

    初始化就是:主设备先拉低数据总线超过480us以发出一个复位脉冲,然后从设备DS18B20收到复位脉冲后内部进行硬件复位,复位完成后回复主设备一个存在脉冲,主设备收到了存在脉冲后就认为从设备已经准备好,初始化完成。(确认设备存在)

    ROM命令

    当总线上的主设备检测到了存在脉冲后,就可以执行ROM命令。这些命令是对每个设备独一无二的64位ROM编码进行操作的,当总线上连接有多个设备时,可以通过这些命令识别各个设备。这些命令同时也可以使主设备确定该总线上有多少个什么类型的设备或者有温度报警信号的设备。总共包含有5种ROM命令,每个命令的长度都是8 Bit。主设备在执行DS18B20功能命令之前必须先执行一个适当的ROM命令。(找到具体的设备)

    DS18B20功能命令

    当总线上的主设备通过ROM命令确定了哪个DS18B20能够进行通信时,主设备可以向其中一个DS18B20发送功能命令。这些命令使得主设备可以向DS18B20的暂存寄存器写入或者读出数据,初始化温度转换及定义供电模式。(具体的操作)

    DS18B20 

    详见:DS18B20数据手册-中文版 - 知乎

    DS18B20采用严谨的1-Wire总线通信协议来保证数据的完整性。该协议定义多个信号形式:复位脉冲,存在脉冲,写0,写1,读0,读1。主设备执行除了存在脉冲外的所有其他信号。

    初始化程序—复位和存在脉冲

    与DS18B20所有的通信都是由初始化序列开始的,该序列包括从主设备发出的复位脉冲及从DS18B20响应的存在脉冲组成。当DS18B20响应复位信号的存在脉冲后,则其向主设备表明其在该总线上,并且已经做好操作命令。

    在初始化序列期间,总线上的主设备通过拉低1-Wire总线超过480us来发送(TX)复位脉冲。之后主设备释放总线而进入接收模式(RX)。当总线释放后,5kΩ左右的上拉电阻将1-Wire总线拉至高电平。当DS18B20检测到该上升边沿信号后,其等待15us至60us后通过将1-Wire总线拉低60us至240us来实现发送一个存在脉冲。

    读/写时段

    主设备通过写时段向DS18B20中写入数据,通过读时段从DS18B20中读取数据。1-Wire总线上每一个读写时段只能传送一个位的数据。

    写时段

    写时段有两种情况:“写1”时段和“写0”时段。主设备通过写1时段来向DS18B20中写入逻辑1以及通过写0时段来向DS18B20中写入逻辑0。每个写时段最小必须有60us的持续时间且独立的写时段间至少有1us的恢复时间。两个写时段都是由主设备通过将1-Wire总线拉低来进行初始化。

    为了形成写1时段,在将1-Wire总线拉低后,主设备必须在15us之内释放总线。当总线释放后,5kΩ的上拉电阻将总线拉至高。为了形成写0时段,在将1-Wire总线拉低后,在整个时段期间主设备必须一直拉低总线(至少60us)。

    在主设备初始化写时段后,DS18B20将会在15us至60us的时间窗口内对总线进行采样。如果总线在采样窗口期间是高电平,则逻辑1被写入DS18B20;若总线是低电平,则逻辑0被写入DS18B20。

    读时段

    仅在读时段期间DS18B20才能向主设备传送数据。因此,主设备在执行完读暂存寄存器[BEh]或读取供电模式[B4h]后,必须及时地生成读时段,这样DS18B20才能提供所需的数据。此外,主设备可以在执行完转换温度[44h]或拷贝EEPROM[B8h]命令后生成读时段,以便获得DS18B20功能命令的操作信息。

    每个读时段最小必须有60us的持续时间且独立的写时段间至少有1us的恢复时间。读时段通过主设备将总线拉低超过1us再释放总线来实现初始化。当主设备初始化完读时段后,DS18B20将会向总线发送0或者1。DS18B20通过将总线拉至高来发送逻辑1,将总线拉至低来发送逻辑0。当发送完0后,DS18B20将会释放总线,则通过上拉电阻该总线将会恢复到高电平的闲置状态。从DS18B20中输出的数据在初始化读时序后仅有15us的有效时间。因此,主设备在开始改读时段后的15us之内必须释放总线,并且对总线进行采样。

    代码实现

    1. #include "ds18b20.h"
    2. #include "uart.h"
    3. #include
    4. /******* 延时函数 *******************/
    5. void delay750us(void) //误差 -0.217013888889us
    6. {
    7. unsigned char a,b;
    8. for(b=198;b>0;b--)
    9. for(a=2;a>0;a--);
    10. }
    11. void delay15us(void) //误差 -0.894097222222us
    12. {
    13. unsigned char a;
    14. for(a=4;a>0;a--);
    15. }
    16. void delay70us(void) //误差 -0.555555555556us
    17. {
    18. unsigned char a,b;
    19. for(b=1;b>0;b--)
    20. for(a=28;a>0;a--);
    21. }
    22. void delay45us(void) //误差 -0.512152777778us
    23. {
    24. unsigned char a;
    25. for(a=18;a>0;a--);
    26. }
    27. void delay1ms(void) //误差 -0.651041666667us
    28. {
    29. unsigned char a,b;
    30. for(b=4;b>0;b--)
    31. for(a=113;a>0;a--);
    32. }
    33. void delay750ms(void) //误差 -0.000000000183us
    34. {
    35. unsigned char a,b,c;
    36. for(c=147;c>0;c--)
    37. for(b=127;b>0;b--)
    38. for(a=17;a>0;a--);
    39. _nop_(); //if Keil,require use intrins.h
    40. }
    41. /****** 低层时序 *********************************************/
    42. // 返回0则表示初始化成功,返回1则表示初始化失败
    43. unsigned char Ds18b20Init(void)
    44. {
    45. unsigned char i = 0;
    46. DATA = 0; //将总线拉低480us~960us
    47. delay750us(); // 实际延时750us,符合480-960之间的条件
    48. DATA = 1; //然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
    49. i = 0;
    50. while (DATA) //等待DS18B20拉低总线
    51. {
    52. i++;
    53. if(i>5)//等待>5MS
    54. {
    55. return 1;//初始化失败
    56. }
    57. delay15us();
    58. }
    59. return 0;//初始化成功
    60. }
    61. void Ds18b20WriteByte(unsigned char dat)
    62. {
    63. unsigned int i = 0, j = 0;
    64. for (j=0; j<8; j++)
    65. {
    66. DATA = 0; //每写入一位数据之前先把总线拉低1us
    67. i++;
    68. DATA = dat & 0x01; //然后写入一个数据,从最低位开始
    69. delay70us(); // 时序要求最少60us
    70. DATA = 1; //然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
    71. dat >>= 1;
    72. }
    73. }
    74. unsigned char Ds18b20ReadByte()
    75. {
    76. unsigned char byte = 0, bi = 0;
    77. unsigned int i = 0, j = 0;
    78. for (j=8; j>0; j--)
    79. {
    80. DATA = 0; //先将总线拉低1us
    81. i++;
    82. DATA = 1; //然后释放总线
    83. i++;
    84. i++; //延时6us等待数据稳定
    85. bi = DATA; //读取数据,从最低位开始读取
    86. /*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/
    87. byte = (byte >> 1) | (bi << 7);
    88. //byte |= (bi << (8-j));
    89. delay45us();
    90. }
    91. return byte;
    92. }
    93. /*************** 高层时序 *************************************/
    94. void Ds18b20ChangTemp(void)
    95. {
    96. Ds18b20Init();
    97. delay1ms();
    98. Ds18b20WriteByte(0xcc); //跳过ROM操作命令
    99. Ds18b20WriteByte(0x44); //温度转换命令
    100. delay750ms(); //等待转换成功,而如果你是一直刷着的话,就不用这个延时了
    101. }
    102. void Ds18b20ReadTempCom(void)
    103. {
    104. Ds18b20Init();
    105. delay1ms();
    106. Ds18b20WriteByte(0xcc); //跳过ROM操作命令
    107. Ds18b20WriteByte(0xbe); //发送读取温度命令
    108. }
    109. void Ds18b20ReadTemp2(void)
    110. {
    111. unsigned int temp = 0;
    112. unsigned char tmh, tml;
    113. double t = 0;
    114. Ds18b20ChangTemp(); //先写入转换命令
    115. Ds18b20ReadTempCom(); //然后等待转换完后发送读取温度命令
    116. tml = Ds18b20ReadByte(); //读取温度值共16位,先读低字节
    117. tmh = Ds18b20ReadByte(); //再读高字节
    118. // temp = tmh;
    119. // temp <<= 8;
    120. // temp |= tml;
    121. temp = tml | (tmh << 8);
    122. t = temp * 0.0625;
    123. uart_send_byte(0);
    124. uart_send_byte((unsigned char)t);
    125. return;
    126. }
    127. void Ds18b20ReadTemp(void)
    128. {
    129. unsigned int temp = 0;
    130. unsigned char tmh, tml;
    131. Ds18b20ChangTemp(); //先写入转换命令
    132. Ds18b20ReadTempCom(); //然后等待转换完后发送读取温度命令
    133. tml = Ds18b20ReadByte(); //读取温度值共16位,先读低字节
    134. tmh = Ds18b20ReadByte(); //再读高字节
    135. uart_send_byte(0);
    136. uart_send_byte(tml);
    137. uart_send_byte(tmh);
    138. return;
    139. }
  • 相关阅读:
    小程序直播 + 电商,想做新零售电商就用它吧!
    企业管理软件使用与择选时要注意五点
    李宏毅hw-6利用GAN生成动漫图像
    Java线程与锁-1
    opencv-phase 函数
    又一款机器学习模型解释神器:LIME
    php使用正则表达式和翻译字典json文件做翻译
    住建部发话,9月15日后,全面禁止使用这9项施工工艺、设备和材料
    Solana流支付协议Zebec完成850万美元融资,CircleVentures等参投
    Node.js学习-15跨域
  • 原文地址:https://blog.csdn.net/qq_28576837/article/details/126102809