[其他ST产品] STM32F4_DS18B20红外温度传感器

[复制链接]
4164|61
 楼主| 大鹏2365 发表于 2023-6-29 19:17 | 显示全部楼层
实验程序详解
实验现象:

        开机的时候先检测DS18B20是否存在,如果没有,则提示错误。只有在检测到DS18B20之后才开始读取温度并显示在LCD上,如果发现了DS18B20,则程序每隔100ms左右读取一次数据,把温度显示在LCD上。
 楼主| 大鹏2365 发表于 2023-6-29 19:18 | 显示全部楼层
main.c
  1. #include "stm32f4xx.h"                 
  2. #include "delay.h"
  3. #include "usart.h"
  4. #include "LED.h"
  5. #include "lcd.h"
  6. #include "Key.h"
  7. #include "usmart.h"
  8. #include "DS18B20.h"

  9. //LCD状态设置函数
  10. void led_set(u8 sta)//只要工程目录下有usmart调试函数,主函数就必须调用这两个函数
  11. {
  12.         LED1=sta;
  13. }
  14. //函数参数调用测试函数
  15. void test_fun(void(*ledset)(u8),u8 sta)
  16. {
  17.         led_set(sta);
  18. }

  19. int main(void)
  20. {
  21.         u8 t=0;
  22.         short temperature;
  23.         delay_init(168);
  24.         uart_init(115200);
  25.        
  26.         LED_Init();
  27.         LCD_Init();
  28.         POINT_COLOR=RED;
  29.         LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
  30.         LCD_ShowString(30,70,200,16,16,"DS18B20 Test");
  31.         LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
  32.         LCD_ShowString(30,110,200,16,16,"2023/06/22");
  33.        
  34.         while(DS18B20_Init()) //初始化返回值是检验DS18B20能否被开发板检测到,返回1也就是未检测到DS18B20
  35.         {
  36.                 LCD_ShowString(30,130,200,16,16,"DS18B20 ERROR");
  37.                 delay_ms(200);
  38.                 LCD_Fill(30,130,239,130+16,WHITE); //清屏,清屏的范围是:x 30~239;y 130~130+16,也就是左边130行往下清两行
  39.                 delay_ms(200);
  40.         }
  41.         LCD_ShowString(30,130,200,16,16,"DS18B20 OK");       
  42.         POINT_COLOR=BLUE;
  43.         LCD_ShowString(30,150,200,16,16,"Temp:   . C");
  44.         while(1)
  45.         {
  46.                 if(t%10==0) //每100ms读取一次
  47.                 {
  48.                         temperature=DS18B20_Get_Temperature(); //调用读取温度函数,将读到的十进制数赋值给temperature
  49.                         if(temperature<0) //温度为负
  50.                         {
  51.                                 LCD_ShowChar(30+5*8,150,'-',16,0);  //显示负号
  52.                                 temperature=-temperature;  //负数变正数
  53.                         }
  54.                         else
  55.                         {
  56.                                 LCD_ShowChar(30+5*8,150,' ',16,0); //去掉负号
  57.                         }
  58.                         LCD_ShowNum(30+5*8+8,150,temperature/10,2,16); //显示整数位
  59.                         //30+5*8+8:30起始坐标,5*8表示Temp:占用的字节,8表示的是符号位占用的字节
  60.                         LCD_ShowNum(30+5*8+4*8,150,temperature%10,1,16); //显示小数部分
  61.                 }
  62.                 delay_ms(10);
  63.                 t++;
  64.                 if(t==20)
  65.                 {
  66.                         t=0;
  67.                         LED0=!LED0;
  68.                 }
  69.         }
  70. }


 楼主| 大鹏2365 发表于 2023-6-29 19:18 | 显示全部楼层
DS18B20.c
  1. #include "stm32f4xx.h"           
  2. #include "DS18B20.h"
  3. #include "delay.h"

  4. //复位DS18B20
  5. void DS18B20_Reset(void)
  6. {
  7.         //复位的时序是:主机输出低电平,保持低电平至少480us,以产生复位脉冲。
  8.         //接着主机释放总线,4.7K上拉电阻将单总线拉高,延迟15~60us。
  9.         DS18B20_IO_OUT();  //整个单总线协议,除了应答信号是从机DS18B20发的,其余的信号都是主机发的,因此在发送这些信号时,都需要将主机的GPIO模式设置为输出模式
  10.         DS18B20_DQ_OUT=0;  //主机输出低电平
  11.         delay_us(750);     //保持低电平至少480us,以产生复位脉冲。这里设置750us
  12.         DS18B20_DQ_OUT=1;  //主机释放总线,4.7K上拉电阻将单总线拉高.
  13.         delay_us(15);      //延迟15~60us。这里设置为15us
  14. }
  15. //应答脉冲,也就是等待DS18B20的回应
  16. //返回1:开发板上未检测到DS18B20的存在
  17. //返回0:DS18B20存在
  18. u8 DS18B20_CheckExist(void)
  19. {
  20.         //应答信号的时序是:在复位脉冲时序结束后,DS18B20 拉低总线 60~240 us,以产生低电平应答脉冲。
  21.         u8 Existence=0;
  22.         DS18B20_IO_IN();   //IO引脚设置为输入模式,以便MCU可以接收到DS18B20的返回应答
  23.         while(DS18B20_DQ_IN && Existence<200)//按位与&&操作:两个条件都成立即可进入该while循环
  24.         //应答信号的时序规定:DS18B20需要拉低总线 60~240 us,以产生低电平应答脉冲。
  25.         //DS18B20_DQ_IN是DS18B20发送给主机的信号,如果为真,则意味着DS18B20没有拉低总线,也就没有产生低电平应答脉冲
  26.         //Existence<200:等待从机响应的时间,主机给一个复位信号,从机返回应答需要有一个缓冲时间,这里我们设置200us;换言之,如果超过200us,DS18B20还没有返回应答,则认为开发板没有接DS18B20
  27.         {
  28.                 Existence++;
  29.                 delay_us(1);
  30.         }//离开while循环时,DS18B20_DQ_IN一定是等于0,处于低电平,或者Existence>200,所以接下来需要判断究竟是哪个条件成立了
  31.         if(Existence>=200)//缓冲时间超过200us的情况
  32.                 return 1; //认为开发板上未检测到DS18B20的存在
  33.         else             //DS18B20存在
  34.                 Existence=0;  //为总线延迟240us做准备
  35.         while(!DS18B20_DQ_IN && Existence<240)
  36.                 //应答信号的时序规定:DS18B20需要拉低总线 60~240 us,以产生低电平应答脉冲。
  37.                 //该程序表示引脚收到低电平,并且通过循环延迟了240us
  38.         {
  39.                 Existence++;
  40.                 delay_us(1);
  41.         }
  42.         if(Existence>=240) //单总线时序规定应答信号需要拉低总线60~240us,这里设置的是239;大于240,违反时序,一定是return 0
  43.                 return 1;
  44.         return 0;
  45. }
  46. //从DS18B20读一个位
  47. //返回值:1/0
  48. u8 DS18B20_Read_Bit(void)
  49. {
  50.         //读一位的时序是:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,延时50us;读时序至少需要60us
  51.         //主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线的状态。
  52.         u8 data;
  53.         DS18B20_IO_OUT(); //主机读从机,IO引脚输出模式
  54.         DS18B20_DQ_OUT=0; //主机输出低电平
  55.         delay_us(2);      //主机输出低电平延时2us
  56.         DS18B20_DQ_OUT=1; //主机在读时序期间必须释放总线
  57.         DS18B20_IO_IN();  //主机转入输入模式
  58.         delay_us(12);     //主机转入输入模式延时12us
  59.         if(DS18B20_DQ_IN) //读取单总线当前的电平,读的值就是引脚输入的电平值
  60.                 data=1;
  61.         else
  62.                 data=0;
  63.         delay_us(50);     //延时50us
  64.         return data;
  65. }
  66. //从DS18B20读一个字节
  67. //返回值:读到的数据
  68. u8 DS18B20_Read_Byte(void)
  69. {
  70.         u8 i,j,data;
  71.         data=0;
  72.         for(i=1;i<=8;i++)  //读一个字节就是在读一位的基础之上循环8次得到的,低位在前
  73.         {
  74.                 j=DS18B20_Read_Bit(); //将读到的那一位给到j
  75.                 data=(j<<7)|(data>>1);
  76.                 //该代码这样理解:第一次data初始化为0,右移一位还是0;j<<7 会将读到的那一位给到最高位,1和0进行或运算,是1还是1,是0还是0
  77.                 //第二次循环的时候,data不再是0了,而是第一次得到的8位(最高位是第一次接收的值,其余位都是0);data>>1,表示第一次的最高位放到次高位上,
  78.         //最高位通过本次循环的j<<7来存放最高位,这样或运算以后,第一次接收的放在次高位,第二次接收的放在最高位
  79.                 //每循环一次,上一次的8位数据就会整体右移一位,腾出的最高位给本次接收的那一位存放;循环八次,即可得到数据,并且还是低位在前的。
  80.         }
  81.         return data;
  82. }
  83. //写一个字节到DS18B20
  84. //data:要写入的字节
  85. void DS18B20_Write_Byte(u8 data)
  86. {
  87.         //写1时序:主机输出低电平,延时2us,然后释放总线,延时60us
  88.         //写0时序:主机输出低电平,延时60us,然后释放总线,延时2us
  89.         //所有写时序至少60us
  90.         u8 j;
  91.         u8 testb; //该变量用于记录要写的字节是逻辑1还是逻辑0
  92.         DS18B20_IO_OUT(); //主机写字节到DS18B20,主机设置为输出模式
  93.         for(j=1;j<=8;j++)
  94.         {
  95.                 testb=data&0x01;  //单总线协议发送时是低位在前,所以先取出最低位进行发送
  96.                 data=data>>1;     //每取一次最低位,就将次低位右移到最低位,为下一次取最低位做准备
  97.                 //单总线协议下:逻辑1和逻辑0的写时序是不一样的,所以需要进行判断分开写
  98.                 if(testb)  //逻辑1
  99.                 {
  100.                         DS18B20_DQ_OUT=0;  //主机输出低电平
  101.                         delay_us(2);           //延时2us
  102.                         DS18B20_DQ_OUT=1;  //释放总线
  103.                         delay_us(60);           //延时60us
  104.                 }
  105.                 else           //逻辑0
  106.                 {
  107.                         DS18B20_DQ_OUT=0;  //主机输出低电平
  108.                         delay_us(60);           //延时60us
  109.                         DS18B20_DQ_OUT=1;  //释放总线
  110.                         delay_us(2);           //延时2us
  111.                 }
  112.         }
  113. }
  114. //温度转换
  115. void DS18B20_StartConvert(void)
  116. {
  117.         //温度转化的时序是:初始化(复位),然后跳过ROM,最后发送开始温度转换的数据帧
  118.         DS18B20_Reset();  //初始化(复位)
  119.         DS18B20_CheckExist();  //DS18B20应答
  120.         DS18B20_Write_Byte(DS18B20_SKIP_ROM); //写时序跳过ROM
  121.         DS18B20_Write_Byte(DS18B20_CONVERT_T); //发送开始温度转换的数据帧
  122. }
  123. //初始化DS18B20的IO口、DQ,同时检测DS18B20的存在
  124. //返回1:不存在
  125. //返回0:存在
  126. u8 DS18B20_Init(void)
  127. {
  128.         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);  //使能GPIOG时钟
  129.        
  130.         //GPIOG9
  131.         GPIO_InitTypeDef GPIO_InitStructure;
  132.         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //默认GPIO的模式是输出
  133.         GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
  134.         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
  135.         GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
  136.         GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  137.         GPIO_Init(GPIOG,&GPIO_InitStructure);
  138.        
  139.         DS18B20_Reset(); //复位时序
  140.         return DS18B20_CheckExist(); //返回值是检测DS18B20是否存在
  141. }
  142. //从DS18B20得到温度值
  143. //精度:0.1C
  144. //返回值:温度值(-550~+1250)
  145. //这里需要注意,精度是0.1C,并且测的的温度返回值是温度值(-550~+1250)
  146. //根据DS18B20芯片手册记录:该传感器的测量范围是:-55~+125℃ ,所以相当于返回值放大了10倍
  147. short DS18B20_Get_Temperature(void)
  148. {
  149.         //温度读取的时序是:最先进行温度转换,复位(初始化),然后跳过ROM,紧接着读暂存器,最后进行连续的读操作,分别读出低8位和高8位
  150.         u8 temp;
  151.         u8 TLSB,THSB;
  152.         short temperature; //short短整型是16位
  153.         DS18B20_StartConvert(); //进行温度转换
  154.         DS18B20_Reset();   //复位(初始化)
  155.         DS18B20_CheckExist(); //检查DS18B20是否存在
  156.         DS18B20_Write_Byte(DS18B20_SKIP_ROM); //跳过ROM
  157.         DS18B20_Write_Byte(DS18B20_READ_SCRATCHPAD); //读暂存器
  158.        
  159.         TLSB=DS18B20_Read_Byte(); //连续的读操作,读出低8位
  160.         THSB=DS18B20_Read_Byte(); //连续的读操作,读出高8位
  161.        
  162.         if(THSB>7)
  163.         {
  164.                 THSB=~THSB;
  165.                 TLSB=~TLSB;
  166.                 temp=0;  //表示温度为负
  167.                 //这里解释一下,为什么THSB>7,温度就是负的?
  168.                 //因为温度存储的寄存器是16位,高5位是温度状态位,高5位为0时,表示温度是正的;高5位为1时,表示温度是负的;
  169.                 //7的八位二进制是:0000 0111 ,对应的高5位正好是0,如果大于7,那么对应的二进制就是1111 1111 因为高5位是状态位,要么全为0,要么全为1
  170.         }
  171.         else
  172.         {
  173.                 temp=1;  //表示温度为正
  174.         }
  175.         temperature=THSB;
  176.         temperature=temperature<<8; //将低8位放到次低8位上
  177.         temperature=temperature+TLSB; //获得低8位
  178.         temperature=(double)temperature*0.625; //温度从二进制转换成十进制。我们表达温度不会说0000 0265摄氏度,我们只会说30摄氏度
  179.         //这里解释一下,为什么乘以0.625就能实现温度从二进制转换到十进制数?
  180.         //DS18B20的转换精度是9~12位,为了提高精度通常采用的都是12位,12位的最低位权是1/16=0.0625,所以说温度寄存器中的值都是以0.0625为步进的
  181.         //换言之就是:温度每波动一次都是要么+0.0625,要么-0.0625,而不会出现温度上升0.01,或者下降0.01的情况。
  182.         //所以温度寄存器中的值就是温度值的二进制乘以0.0625,得到的就是实际的十进制数;
  183.         //程序中之所以乘0.625是因为返回值返回的温度是-550~+1250,返回温度相对于实际的温度范围放大了十倍,所以转换的步进值也要放大10倍,即0.625
  184.         if(temp)
  185.         {
  186.                 return temperature;
  187.         }
  188.         else
  189.                 return -temperature; //这里默认只输入正的温度值
  190. }


 楼主| 大鹏2365 发表于 2023-6-29 19:18 | 显示全部楼层
DS18B20.h
  1. #ifndef _DS18B20__H_
  2. #define _DS18B20__H_
  3. #include "sys.h"

  4. //IO方向设置
  5. //该宏定义方式是通过位段的方式来定义的,通过调用GPIO的模式寄存器来设置相关位,其中00:输入模式 01:输出模式
  6. //位段操作分两步:第一步将所要操作位清空,使用与&操作符完成;第二步将清空的位设置为相应的值,按设置的值输出相应的模式
  7. //GPIO状态寄存器是32位寄存器,每两个位控制一个GPIO引脚,所以总共控制15个引脚
  8. //本次我们操作的对象是PG9引脚,也就对应于寄存器的18位、19位
  9. #define DS18B20_IO_IN()  {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=0<<9*2;}        //PG9输入模式
  10. //将18/19位清空,使用&操作符完成;2*9表示每两个位控制一个引脚,先找到PG9所在的两个位
  11. //3的32位二进制是:                        0000 0000 0000 0000 0000 0000 0000 0011
  12. //左移18位得到的是:                        0000 0000 0000 1100 0000 0000 0000 0000
  13. //取反得到的是:                            1111 1111 1111 0011 1111 1111 1111 1111
  14. //和最初的32位二进制按位与: 0000 0000 0000 0000 0000 0000 0000 0011 此时就设置了18 19位00,表示将这两位清空
  15. //0的32位二进制是:          0000 0000 0000 0000 0000 0000 0000 0000
  16. //0左移18位,|或操作符:     0000 0000 0000 0000 0000 0000 0000 0000 此时就将18位、19位设置成了00,表示输入模式
  17. #define DS18B20_IO_OUT() {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=1<<9*2;}         //PG9输出模式
  18. //同理设置PG9输出模式,将第18 19位设置为01即可。

  19. //IO操作函数
  20. #define DS18B20_DQ_OUT PGout(9)  //PG9设置为输出模式
  21. #define DS18B20_DQ_IN PGin(9)    //PG9设置为输入模式

  22. //DS18B20数据帧
  23. #define DS18B20_SKIP_ROM 0xCC
  24. #define DS18B20_CONVERT_T 0x44
  25. #define DS18B20_READ_SCRATCHPAD 0xBE


  26. void DS18B20_Reset(void);
  27. u8 DS18B20_CheckExist(void);
  28. u8 DS18B20_Read_Bit(void);
  29. u8 DS18B20_Read_Byte(void);
  30. void DS18B20_Write_Byte(u8 data);
  31. void DS18B20_StartConvert(void);
  32. u8 DS18B20_Init(void);
  33. short DS18B20_Get_Temperature(void);

  34. #endif

小小蚂蚁举千斤 发表于 2023-6-30 15:23 | 显示全部楼层
DS18B20还是比较经典的温度传感器,测量数据还是比较稳定的
szt1993 发表于 2023-6-30 17:22 | 显示全部楼层
这个温度传感器是模拟量数据传输吧,PG9这个引脚貌似没有看到说明
Undshing 发表于 2023-7-1 22:33 | 显示全部楼层
不同厂家有没有可能生产出相同序列号产品啊?
sdlls 发表于 2023-7-5 10:05 | 显示全部楼层
DS18B20不是红外测量温度的
uiint 发表于 2023-7-5 11:15 | 显示全部楼层
为什么不使用ntc测量问题的呢              
wwppd 发表于 2023-7-5 11:21 | 显示全部楼层
则合格可以支持多少路的DS18B20?
bartonalfred 发表于 2023-7-5 13:52 | 显示全部楼层
DS18B20这个是怎么测量温度呢
mattlincoln 发表于 2023-7-5 14:35 | 显示全部楼层
最大的测量范围是多少?              
AloneKaven 发表于 2023-7-5 18:18 | 显示全部楼层
单总线的时间真是很难把握啊
biechedan 发表于 2023-7-6 11:43 | 显示全部楼层
测量DS18B20时序非常重要。
i1mcu 发表于 2023-7-6 11:51 | 显示全部楼层
可以做体温测量吗?              
beacherblack 发表于 2023-7-6 16:21 | 显示全部楼层
这个红外测量使用mlx90614吧
pentruman 发表于 2023-7-6 17:21 | 显示全部楼层
DS18B20测量的温度不是很精确。
Jacquetry 发表于 2023-7-6 22:59 | 显示全部楼层
单总线对时序把握很严格
Undshing 发表于 2023-7-10 23:38 | 显示全部楼层
单总线最麻烦的就是时间把握了
LLGTR 发表于 2023-7-11 09:11 | 显示全部楼层
则及格可以支撑几多路的ds18b20?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部