写点什么

STM32 入门开发 编写 DS18B20 温度传感器驱动 (读取环境温度、支持级联)

作者:DS小龙哥
  • 2022 年 8 月 14 日
    重庆
  • 本文字数:6065 字

    阅读完需:约 20 分钟

一、环境介绍

编程软件: keil5


操作系统: win10


MCU 型号: STM32F103C8T6


STM32 编程方式: 寄存器开发 (方便程序移植到其他单片机)


温度传感器: DS1820


DS18B20 是一个数字温度传感器,采用的是单总线时序与主机通信,只需要一根线就可以完成温度数据读取;


DS18B20 内置了 64 位产品序列号,方便识别身份,在一根线上可以挂接多个 DS18B20 传感器,通过 64 位身份验证,可以分别读取来至不同传感器采集的温度信息。



二、DS18B20 介绍

2.1 DS18B20 的主要特征

\1. 全数字温度转换及输出。


\2. 先进的单总线数据通信。


\3. 最高 12 位分辨率,精度可达土 0.5 摄氏度。


\4. 12 位分辨率时的最大工作周期为 750 毫秒。


\5. 可选择寄生工作方式。


\6. 检测温度范围为–55° C ~+125° C (–67° F ~+257° F)


\7. 内置 EEPROM,限温报警功能。


\8. 64 位光刻 ROM,内置产品序列号,方便多机挂接。


\9. 多样封装形式,适应不同硬件系统。


2.2 DS18B20 引脚功能

GND 电压地


DQ 单数据总线


VDD 电源电压


NC 空引脚

2.3 DS18B20 工作原理及应用

DS18B20 的温度检测与数字数据输出全集成于一个芯片之上,从而抗干扰力更强。其一个工作周期可分为两个部分,即温度检测和数据处理。


18B20 共有三种形态的存储器资源,它们分别是:ROM 只读存储器,用于存放 DS18B20ID 编码,其前 8 位是单线系列编码(DS18B20 的编码是 19H),后面 48 位是芯片唯一的序列号,最后 8 位是以上 56 的位的 CRC 码(冗余校验)。数据在出产时设置不由用户更改,DS18B20 一共有 64 位 ROM。


RAM 数据暂存器,用于内部计算和数据存取,数据在掉电后丢失, DS18B20 共 9 个字节 RAM,每个字节为 8 位。第 1、 2 个字节是温度转换后的数据值信息,第 3、 4 个字节是用户 EEPROM(常用于温度报警值储存)的镜像。在上电复位时其值将被刷新。第 5 个字节则是用户第 3 个 EEPROM 的镜像。第 6、 7、 8 个字节为计数寄存器,是为了让用户得到更高的温度分辨率而设计的,同样也是内部温度转换、计算的暂存单元。第 9 个字节为前 8 个字节的 CRC 码。 EEPROM 非易失性记忆体,用于存放长期需要保存的数据,上下限温度报警值和校验数据,DS18B20 共 3 位 EEPROM,并在 RAM 都存在镜像,以方便用户操作。



DS18B20 默认工作在 12 位分辨率模式,转换后得到的 12 位数据,存储在 DS18B20 的两个 8 比特的 RAM 中(最前面的两个字节),二进制中的前面 5 位是符号位,如果测得的温度大于 0,这 5 位为 0,只要将测到的数值乘于 0.0625 即可得到实际温度;如果温度小于 0,这 5 位为 1,测到的数值需要取反加 1 再乘于 0.0625 即可得到实际温度。或者使用位运算方式提取温度: 小数位是占用的是低 4 位,高位是整数位(不考虑负数情况)。

2.4 DS18B20 芯片 ROM 指令表

1. Read ROM(读 ROM) [33H] (方括号中的为 16 进制的命令字) 这个命令允许总线控制器读到 DS18B20 的 64 位 ROM。只有当总线上只存在一个 DS18B20 的时候才可以使用此指令,如果挂接不只一个,当通信时将会发生数据冲突


2. atch ROM(指定匹配芯片) [55H] 这个指令后面紧跟着由控制器发出了 64 位序列号,当总线上有多只 DS18B20 时,只有与控制发出的序列号相同的芯片才可以做出反应,其它芯片将等待下一次复位。这条指令适应单芯片和多芯片挂接。


3. Skip ROM(跳跃 ROM 指令) [CCH] 这条指令使芯片不对 ROM 编码做出反应,在单总线的情况之下,为了节省时间则可以选用此指令。如果在多芯片挂接时使用此指令将会出现数据冲突,导致错误出现。


4. Search ROM(搜索芯片) [F0H] 在芯片初始化后,搜索指令允许总线上挂接多芯片时用排除法识别所有器件的 64 位 ROM。


5. Alarm Search(报警芯片搜索) [ECH] 在多芯片挂接的情况下,报警芯片搜索指令只对附合温度高于 TH 或小于 TL 报警条件的芯片做出反应。只要芯片不掉电,报警状态将被保持,直到再一次测得温度什达不到报警条件为止。


6. Write Scratchpad (向 RAM 中写数据) [4EH] 这是向 RAM 中写入数据的指令,随后写入的两个字节的数据将会被存到地址 2(报警 RAM 之 TH)和地址 3(报警 RAM 之 TL)。写入过程中可以用复位信号中止写入。


7. Read Scratchpad (从 RAM 中读数据) [BEH] 此指令将从 RAM 中读数据,读地址从地址 0 开始,一直可以读到地址 9,完成整个 RAM 数据的读出。芯片允许在读过程中用复位信号中止读取,即可以不读后面不需要的字节以减少读取时间。


8. Copy Scratchpad (将 RAM 数据复制到 EEPROM 中) [48H] 此指令将 RAM 中的数据存入 EEPROM 中,以使数据掉电不丢失。此后由于芯片忙于 EEPROM 储存处理,当控制器发一个读时间隙时,总线上输出“0”,当储存工作完成时,总线将输出“1”。 在寄生工作方式时必须在发出此指令后立刻超用强上拉并至少保持 10MS,来维持芯片工作。


9. Convert T(温度转换) [44H] 收到此指令后芯片将进行一次温度转换,将转换的温度值放入 RAM 的第 1、 2 地址。此后由于芯片忙于温度转换处理,当控制器发一个读时间隙时,总线上输出“0”,当储存工作完成时,总线将输出“1”。在寄生工作方式时必须在发出此指令后立刻超用强上拉并至少保持 500MS,来维持芯片工作。


10. Recall EEPROM(将 EEPROM 中的报警值复制到 RAM) [B8H] 此指令将 EEPROM 中的报警值复制到 RAM 中的第 3、 4 个字节里。由于芯片忙于复制处理,当控制器发一个读时间隙时,总线上输出“0”,当储存工作完成时,总线将输出“1”。另外,此指令将在芯片上电复位时将被自动执行。这样 RAM 中的两个报警字节位将始终为 EEPROM 中数据的镜像。


11. Read Power Supply(工作方式切换) [B4H] 此指令发出后发出读时间隙,芯片会返回它的电源状态字,“0”为寄生电源状态,“1”为外部电源状态。

2.5 DS18B20 时序图

2.5.1 DS18B20 复位及应答关系示意图



每一次通信之前必须进行复位,复位的时间、等待时间、回应时间应严格按时序编程。


DS18B20 读写时间隙:DS18B20 的数据读写是通过时间隙处理位和命令字来确认信息交换的。

2.5.2 向 DS18B20 写数据 0 和数据 1


在写数据时间隙的前 15uS 总线需要是被控制器拉置低电平,而后则将是芯片对总线数据的采样时间,采样时间在 15~60uS,采样时间内如果控制器将总线拉高则表示写“1”,如果控制器将总线拉低则表示写“0”。


每一位的发送都应该有一个至少 15uS 的低电平起始位,随后的数据“0”或“1”应该在 45uS 内完成。


整个位的发送时间应该保持在 60~120uS,否则不能保证通信的正常。


注意: DS18B20 读写数据都是从低位开始传输。

2.5.3 从 DS18B20 读数据 0 和数据 1


读时间隙时控制时的采样时间应该更加的精确才行,读时间隙时也是必须先由主机产生至少 1uS 的低电平,表示读时间的起始。随后在总线被释放后的 15uS 中 DS18B20 会发送内部数据位,这时控制如果发现总线为高电平表示读出“1”,如果总线为低电平则表示读出数据“0”。每一位的读取之前都由控制器加一个起始信号。


注意:必须在读间隙开始的 15uS 内读取数据位才可以保证通信的正确。


在通信时是以 8 位“0”或“1”为一个字节,字节的读或写是从低位开始的。

2.5.4 读取一次温度的顺序(总线上只有单个 DS18B20 情况)

\1. 发送复位信号


\2. 检测回应信号


\3. 发送 0xCC


\4. 发送 0x44


\5. 发送复位信号


\6. 检测回应信号


\7. 写 0xcc


\8. 写 0xbe


\9. 循环 8 次读取温度低字节


\10. 循环 8 次读取温度高字节


\11. 合成 16 位温度数据,处理

三、驱动代码

3.1 DS18B20.c

 #include "ds18b20.h" /* 函数功能: DS18B20初始化 硬件连接: PB15 */ void DS18B20_Init(void) {         RCC->APB2ENR|=1<<3; //PB         GPIOB->CRH&=0x0FFFFFFF;         GPIOB->CRH|=0x30000000;         GPIOB->ODR|=1<<15; //上拉 } /* 函数功能: 检测DS18B20设备是否存在 返回值  : 1表示设备不存在 0表示设备正常 */ u8 DS18B20_CheckDevice(void)  //包含了复位脉冲、检测存在脉冲 {         DS18B20_OUTPUT_MODE();//初始化为输出模式         DS18B20_OUT=0;        //产生复位脉冲         DelayUs(750);         //产生750us的低电平         DS18B20_OUT=1;        //释放总线         DelayUs(15);          //等待DS18B20回应         if(DS18B20_CleckAck())//检测存在脉冲         {                 return 1;         }         return 0; } /* 函数功能: 检测DS18B20设备的存在脉冲 返回值  : 1表示错误 0表示正常 */ u8 DS18B20_CleckAck(void) {         u8 cnt=0;         DS18B20_INPUT_MODE();//初始化为输入模式         while(DS18B20_IN&&cnt<200) //等待DS18B20响应存在脉冲         {                 DelayUs(1);                 cnt++;         }         if(cnt>=200)return 1; //错误                  cnt=0;         while((!DS18B20_IN)&&cnt<240) //等待DS18B20释放总线         {                 DelayUs(1);                 cnt++;         }         if(cnt>=240)return 1; //错误         return 0; } /* 函数功能: 写一个字节 首先学会如何写一个位。 */ void DS18B20_WriteByte(u8 cmd) {         u8 i;         DS18B20_OUTPUT_MODE(); //初始化为输出模式         for(i=0;i<8;i++)       {                 DS18B20_OUT=0;  //产生写时间间隙(写开始)                 DelayUs(2);                 DS18B20_OUT=cmd&0x01; //发送实际的数据位                 DelayUs(60);    //等待写完成                 DS18B20_OUT=1;  //释放总线,准备下一次发送                 cmd>>=1;        //继续发送下一位数据         } } /* 函数功能: 读一个字节 首先学会如何读一个位。 */ u8 DS18B20_ReadByte(void) {      u8 i,data=0;         for(i=0;i<8;i++)      {             DS18B20_OUTPUT_MODE(); //初始化为输出模式             DS18B20_OUT=0;  //产生读时间间隙(读开始)             DelayUs(2);             DS18B20_OUT=1;  //释放总线             DS18B20_INPUT_MODE(); //初始化为输入模式             DelayUs(8);    //等待DS18B20的数据输出             data>>=1;      //高位补0,默认以0为准           if(DS18B20_IN) data|=0x80;             DelayUs(60);             DS18B20_OUT=1;  //释放总线,等待读取下一位数据      }      return data; } /* 函数功能: 读取一次DS18B20的温度数据 返 回 值: 读取的温度数据 考虑的情况:  总线上只是接了一个DS18B20的情况 */ u16 DS18B20_ReadTemp(void) {       u16 temp=0;         u8 temp_H,temp_L;       DS18B20_CheckDevice();   //发送复位脉冲、检测存在脉冲         DS18B20_WriteByte(0xCC); //跳过ROM序列检测         DS18B20_WriteByte(0x44); //启动一次温度转换                  //等待温度转换完成         while(DS18B20_ReadByte()!=0xFF){}                  DS18B20_CheckDevice();   //发送复位脉冲、检测存在脉冲         DS18B20_WriteByte(0xCC); //跳过ROM序列检测         DS18B20_WriteByte(0xBE); //读取温度                  temp_L=DS18B20_ReadByte(); //读取的温度低位数据         temp_H=DS18B20_ReadByte(); //读取的温度高位数据         temp=temp_L|(temp_H<<8);   //合成温度         return temp; }
复制代码

3.2 DS18B20.h

 #ifndef DS18B20_H #define DS18B20_H #include "stm32f10x.h" #include "sys.h" #include "delay.h" #include "ds18b20.h" #include "usart.h" /*封装接口*/ //初始化DS18B20为输入模式 #define DS18B20_INPUT_MODE() {GPIOB->CRH&=0x0FFFFFFF;GPIOB->CRH|=0x80000000;} //初始化DS18B20为输出模式 #define DS18B20_OUTPUT_MODE(){GPIOB->CRH&=0x0FFFFFFF;GPIOB->CRH|=0x30000000;} //DS18B20 IO口输出 #define DS18B20_OUT PBout(15) //DS18B20 IO口输入 #define DS18B20_IN PBin(15)  //函数声明 u8 DS18B20_CleckAck(void); u8 DS18B20_CheckDevice(void); void DS18B20_Init(void); u16 DS18B20_ReadTemp(void); u8 DS18B20_ReadByte(void); void DS18B20_WriteByte(u8 cmd); #endif
复制代码

3.3 延时函数

 /* 函数功能: 延时us单位 */ void DelayUs(int us) { #ifdef  _SYSTICK_IRQ_     int i,j;     for(i=0;i<us;i++)         for(j=0;j<72;j++); #else     u32 tmp;     SysTick->VAL=0;         //CNT计数器值        SysTick->LOAD=9*us;     //9表示1us     SysTick->CTRL|=1<<0;    //开启定时器      do     {             tmp=SysTick->CTRL; //读取状态     }while((!(tmp&1<<16))&&(tmp&1<<0));     SysTick->VAL=0;         //CNT计数器值        SysTick->CTRL&=~(1<<0); //关闭定时器  #endif   }
复制代码

3.4 main.c 调用 DS18B20 读取温度打印到串口

 #include "stm32f10x.h" #include "ds18b20.h" u8 DS18B20_ROM[8]; //存放DS18B20的64为ROM编码 int main(void) {     u16 temp;     USARTx_Init(USART1,72,115200);//串口1的初始化     DS18B20_Init();   //DS18B20初始化          /*1. 读取DS18B20的64位ROM编码*/     //发送复位脉冲、检测存在脉冲     while(DS18B20_CheckDevice())      {             printf("DS18B20设备不存在!\n");             DelayMs(500);     }     //发送读取64为ROM编码的命令     DS18B20_WriteByte(0x33);          //循环读取64位ROM编码     for(i=0;i<8;i++)     {             DS18B20_ROM[i]= DS18B20_ReadByte();             printf("DS18B20_ROM[%d]=0x%X\n",i,DS18B20_ROM[i]);     }          while(1)     {             /*2. 同时操作总线上所有的DS18B20开始转换温度*/             DS18B20_CheckDevice();   //发送复位脉冲、检测存在脉冲             DS18B20_WriteByte(0xCC); //跳过ROM序列检测             DS18B20_WriteByte(0x44); //启动一次温度转换(让总线上所有的DS18B20都转换温度)             DelayMs(500);  //等待线上所有的DS18B20温度转换完成                          /*3. 单个针对性读取每个DS18B20的温度*/             DS18B20_CheckDevice();   //发送复位脉冲、检测存在脉冲             DS18B20_WriteByte(0x55); //发送匹配ROM的命令             for(i=0;i<8;i++)         //发送64位编码             {                     DS18B20_WriteByte(DS18B20_ROM[i]);              }             DS18B20_WriteByte(0xBE);      //读取温度             temp=DS18B20_ReadByte();      //读取的温度低位数据             temp|=DS18B20_ReadByte()<<8;  //读取的温度高位数据             printf("temp1=%d.%d\n",temp>>4,temp&0xF);             printf("temp2=%f\n",temp*0.0625);                          DelayMs(500);     }    }
复制代码


发布于: 2022 年 08 月 14 日阅读数: 5
用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022.01.06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
STM32入门开发 编写DS18B20温度传感器驱动(读取环境温度、支持级联)_8月月更_DS小龙哥_InfoQ写作社区