找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 733|回复: 14
打印 上一主题 下一主题
收起左侧

DS18B20读取温度时会突然变成85,然后马上恢复正常

[复制链接]
跳转到指定楼层
楼主
ID:1143673 发表于 2025-3-17 14:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
楼主正在做蓝桥杯单片机的模拟题,是4T15届模拟4的题目。需要用到DS18B20读取温度,但是温度显示会突然变成85,然后马上恢复正常,我尝试减慢温度读取的速度,但是无法根治问题,只是相同时间内出现85的情况减少了,同样是会出现问题。个人以为可以是底层有些问题,但是个人能力有限,想请教一下各位朋友,希望大家能够不吝赐教。工程文件以及相关信息放在附件里了。 4T15届模拟4.7z (373.8 KB, 下载次数: 0)


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:1133081 发表于 2025-3-18 06:47 | 只看该作者
DS18B20初始化后温度值寄存器里是初始数据“85”.要等约1秒才能读到正确温度,这是正常现象。可以在加电后初始化DS18B20,读一次DS18B20,延时1秒再读一次DS18B20,进入主循环后再显示温度。可以避免显示“85”。
回复

使用道具 举报

板凳
ID:1143673 发表于 2025-3-18 10:22 | 只看该作者
WL0123 发表于 2025-3-18 06:47
DS18B20初始化后温度值寄存器里是初始数据“85”.要等约1秒才能读到正确温度,这是正常现象。可以在加电后 ...

您好,我在主函数里面已经有一个循环防止上电就显示85,现在的情况是上电没有显示85,是正常的数值,然后过一会儿就显示85,然后马上恢复正常。
void main(void) {
  unsigned char i;
  System_Init(); // 关闭LED灯和蜂鸣器
  while (rd_temperature() == 85) {
    ;
  }

  Timer0Init();
  Timer1Init(); // 定时器1初始化
  while (1) {
    Key_Proc(); // 按键处理函数
    Seg_Proc(); // 数码管处理函数
    Led_Proc(); // LED显示函数及温度采集
  }
}
回复

使用道具 举报

地板
ID:744809 发表于 2025-3-18 10:56 | 只看该作者
可以进行数据校验,只有校验通过的数据才赋值给温度变量
回复

使用道具 举报

5#
ID:277550 发表于 2025-3-18 11:22 | 只看该作者
碰到85,再重读,……
回复

使用道具 举报

6#
ID:1133081 发表于 2025-3-18 11:47 | 只看该作者
cien_s 发表于 2025-3-18 10:22
您好,我在主函数里面已经有一个循环防止上电就显示85,现在的情况是上电没有显示85,是正常的数值,然后 ...

理论上while (rd_temperature() == 85)的代码没有错,但rd_temperature()函数的返回值是要经过转换的,否则返回的是16位数据。
回复

使用道具 举报

7#
ID:1143673 发表于 2025-3-18 14:59 | 只看该作者
WL0123 发表于 2025-3-18 11:47
理论上while (rd_temperature() == 85)的代码没有错,但rd_temperature()函数的返回值是要经过转换的,否 ...

您好,我的底层在读数据的时候有经过处理,然后我通过串口调试发现会读到一些不正常的数据。
#include "onewire.h"
#include "reg52.h"

sbit DQ = P1 ^ 4;

// 单总线内部延时函数
void Delay_OneWire(unsigned int t) {
  t *= 12;
  while (t--)
    ;
}

// 单总线写操作
void Write_DS18B20(unsigned char dat) {
  unsigned char i;
  for (i = 0; i < 8; i++) {
    DQ = 0;
    DQ = dat & 0x01;
    Delay_OneWire(5);
    DQ = 1;
    dat >>= 1;
  }
  Delay_OneWire(5);
}

// 单总线读操作
unsigned char Read_DS18B20(void) {
  unsigned char i;
  unsigned char dat;

  for (i = 0; i < 8; i++) {
    DQ = 0;
    dat >>= 1;
    DQ = 1;
    if (DQ) {
      dat |= 0x80;
    }
    Delay_OneWire(5);
  }
  return dat;
}

// DS18B20初始化
bit init_ds18b20(void) {
  bit initflag = 0;

  DQ = 1;
  Delay_OneWire(12);
  DQ = 0;
  Delay_OneWire(80);
  DQ = 1;
  Delay_OneWire(10);
  initflag = DQ;
  Delay_OneWire(5);

  return initflag;
}

float rd_temperature() {
  unsigned char low, high;
  init_ds18b20();
  Delay_OneWire(20);
  Write_DS18B20(0xcc);
  Write_DS18B20(0x44);
  init_ds18b20();
  Delay_OneWire(20);
  Write_DS18B20(0xcc);
  Write_DS18B20(0xbe);
  low = Read_DS18B20();
  high = Read_DS18B20();
  return ((high << 8) | low) / 16.0;
}

Error_Temperature:1048.875000
Error_Temperature:281.562500
Error_Temperature:-2026.375000
回复

使用道具 举报

8#
ID:844772 发表于 2025-3-18 16:27 | 只看该作者
DS18B20是单总线结构啊,你读它的时候关中断没?
回复

使用道具 举报

9#
ID:1143523 发表于 2025-3-18 22:11 | 只看该作者
得延时一下,因为来不及读取就转换了
回复

使用道具 举报

10#
ID:92781 发表于 2025-3-19 08:57 | 只看该作者
上电显示85代表读取代码正确,一直显示85代表正负极反接,偶尔显示85代表两次读取之间有掉电情况
回复

使用道具 举报

11#
ID:1143673 发表于 2025-3-19 15:38 | 只看该作者
denghe 发表于 2025-3-18 22:11
得延时一下,因为来不及读取就转换了

您好,您说的延时哪里加延时呢,可以帮忙指点一下吗?是在底层函数里面吗?以下是我的底层驱动
#include "onewire.h"
#include "reg52.h"

sbit DQ = P1 ^ 4;

// 单总线内部延时函数
void Delay_OneWire(unsigned int t) {
  t *= 12;
  while (t--)
    ;
}

// 单总线写操作
void Write_DS18B20(unsigned char dat) {
  unsigned char i;
  for (i = 0; i < 8; i++) {
    DQ = 0;
    DQ = dat & 0x01;
    Delay_OneWire(5);
    DQ = 1;
    dat >>= 1;
  }
  Delay_OneWire(5);
}

// 单总线读操作
unsigned char Read_DS18B20(void) {
  unsigned char i;
  unsigned char dat;

  for (i = 0; i < 8; i++) {
    DQ = 0;
    dat >>= 1;
    DQ = 1;
    if (DQ) {
      dat |= 0x80;
    }
    Delay_OneWire(5);
  }
  return dat;
}

// DS18B20初始化
bit init_ds18b20(void) {
  bit initflag = 0;

  DQ = 1;
  Delay_OneWire(12);
  DQ = 0;
  Delay_OneWire(80);
  DQ = 1;
  Delay_OneWire(10);
  initflag = DQ;
  Delay_OneWire(5);

  return initflag;
}

float rd_temperature() {
  unsigned char low, high;
  init_ds18b20();
  Delay_OneWire(20);
  Write_DS18B20(0xcc);
  Write_DS18B20(0x44);
  init_ds18b20();
  Delay_OneWire(20);
  Write_DS18B20(0xcc);
  Write_DS18B20(0xbe);
  EA = 0;
  low = Read_DS18B20();
  high = Read_DS18B20();
  EA = 1;
  return (float)((high << 8) | low) * 0.0625;
}

我在主函数中是400多ms读取一次的,其中我加了一些检查读取到的温度数值是否正常的语句。
  1.   if (Seg_Slow_Down % 490 == 0) {
  2.     Temperature_Old = Temperature;
  3.     Temperature_Read = rd_temperature();
  4.     if (Temperature_Read > 50 || Temperature_Read < 10) {
  5.       error_time++;
  6.       printf("Error_Temperature:%f\r\n", Temperature_Read);
  7.       Temperature = Temperature_Old;
  8.     } else {
  9.       Temperature = Temperature_Read;
  10.     }
  11.   }
复制代码
回复

使用道具 举报

12#
ID:1143673 发表于 2025-3-19 15:40 | 只看该作者
glinfei 发表于 2025-3-18 16:27
DS18B20是单总线结构啊,你读它的时候关中断没?

您好,请问单总线结构要关中断的吗?我之前是没有关中断的,我尝试在读取温度函数前后,关开中断,但是还是会读取到异常的数值,只是现在读取到的数值都是-0.062500了。
Error_Temperature:-0.062500
Error_Temperature:-0.062500
Error_Temperature:-0.062500
回复

使用道具 举报

13#
ID:530941 发表于 2025-3-19 17:14 | 只看该作者
你的需要把结果校验一下,有时候读出来的值有问题的


//=========================DS18B20温度传感器=========================//
#define DS18B20_DQ_IN PIND & (1 << PD6)
/*******************************************************************************
* 函 数 名         : DS18B20_Reset
* 函数功能                   : 复位DS18B20
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void DS18B20_Reset(void)
{
    DDRD &= ~(1 << PD6);  // 端口输入
    PORTD &= ~(1 << PD6); // 高阻态
    delay_ms(1);
    DDRD |= (1 << PD6); // 端口输出,因为是输入高阻态,设置成输出时直接就会输出低电平,拉低DQ
    delay_us(500);
    DDRD &= ~(1 << PD6); // 拉高DQ,释放总线
    delay_us(10);
}
/**
* @brief  单总线初始化
* @param  无
* @retval 从机响应位,0为响应,1为未响应
*/
uchar DS18B20_Check(void)
{
    uchar i;
    uchar AckBit = 1;
    uchar TIMEOUT_MS = 500; // 进行超时判断

    DS18B20_Reset();

    while (DS18B20_DQ_IN && TIMEOUT_MS > 0)
    {
        TIMEOUT_MS--;
        AckBit = DS18B20_DQ_IN;
    }
    delay_ms(1);
    return AckBit;//如何DS18B20存在返回0,否则返回1
}
/*******************************************************************************
* 函 数 名         : DS18B20_Init
* 函数功能                   : 初始化DS18B20的IO口 DQ 同时检测DS的存在
* 输    入         : 无
* 输    出         : 1:不存在,0:存在
*******************************************************************************/
uchar DS18B20_Init(void)
{
    uchar a;
    a = DS18B20_Check();
    return a;
}
/*******************************************************************************
* 函 数 名         : DS18B20_Write_Byte
* 函数功能                   : 写一个字节到DS18B20
* 输    入         : dat:要写入的字节
* 输    出         : 无
*******************************************************************************/
void DS18B20_Write_Byte(uchar dat)
{
    uchar j;
    uchar testb;
    for (j = 1; j <= 8; j++)
    {
        testb = dat & 0x01;
        dat = dat >> 1;
        if (testb)
        {
            DDRD |= (1 << PD6); // 拉低DQ
            delay_us(2);
            DDRD &= ~(1 << PD6); //输入高阻态,拉高DQ
            delay_us(60);
        }
        else // Write 0
        {
            DDRD |= (1 << PD6); // 拉低DQ
            delay_us(60);
            DDRD &= ~(1 << PD6); //输入高阻态,拉高DQ
            delay_us(2);
        }
    }
}
/*******************************************************************************
* 函 数 名         : DS18B20_Read_Bit
* 函数功能                   : 从DS18B20读取一个位
* 输    入         : 无
* 输    出         : 1/0  1001 0000
*******************************************************************************/
uchar DS18B20_Read_Bit(void) // read one bit
{
    uchar data;
    DDRD |= (1 << PD6); // 拉低DQ
    delay_us(2);
    DDRD &= ~(1 << PD6); //输入高阻态,拉高DQ
    delay_us(2);
    if (DS18B20_DQ_IN)
    {
        data = 1;
    }
    else
    {
        data = 0;
    }
    delay_us(50);
    return data;
}

/*******************************************************************************
* 函 数 名         : DS18B20_Read_Byte
* 函数功能                   : 从DS18B20读取一个字节
* 输    入         : 无
* 输    出         : 一个字节数据
*******************************************************************************/
uchar DS18B20_Read_Byte(void)    // read one byte
{        
        uchar i,j,dat;
        dat=0;
        for (i=0;i<8;i++)
        {
                j=DS18B20_Read_Bit();
        if(j==1)
        {
            dat|=(0x01<<i);
        }
        }                                                    
        return dat;
}
// 0xCC进行设备寻址(只有一个从机时使用)
// 0x44温度变换(读取温度前要执行温度变换,将温度值放到暂存器里)
// 0xBE温度读取(将暂存器里的温度读出)
//DS18B20指令
#define DS18B20_SKIP_ROM                        0xCC
#define DS18B20_CONVERT_T                        0x44
#define DS18B20_READ_SCRATCHPAD         0xBE

/*******************************************************************************
* 函 数 名         : DS18B20_Start
* 函数功能                   : 开始温度转换
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void DS18B20_Start(void)// ds1820 start convert
{                                                                            
        DS18B20_Check();         
        DS18B20_Write_Byte(DS18B20_SKIP_ROM);// skip rom
        DS18B20_Write_Byte(DS18B20_CONVERT_T);// convert
}

/*******************************************************************************
* 函 数 名         : DS18B20_GetTemperture
* 函数功能                   : 从ds18b20得到温度值
* 输    入         : 无
* 输    出         : 温度数据
*******************************************************************************/
uchar DS18B20_Crc(uchar *addr, uchar len)
{
    uchar crc = 0, inbyte, i, mix;
    while (len--)
    {
        // inbyte 存储当前参与计算的新字节
        inbyte = *addr++;

        for (i = 8; i; i--)
        {
            // 将新字节与CRC从低位到高位, 依次做异或运算, 每次运算完CRC右移一位
            // 如果运算结果值为1, 则将CRC与 1000 1100 作异或
            // 第3,4位代表异或运算, 第7位其实就是运算结果移入的1
            mix = (crc ^ inbyte) & 0x01;
            crc >>= 1;
            if (mix)
            {
                crc ^= 0x8C;
            }
            inbyte >>= 1;
        }
    }
    return crc;
}
float DS18B20_GetTemperture(void)
{
    uint temp;
    uchar a, b;
    static float value;
    uchar tab[9], CRC8;

    DS18B20_Start(); // ds1820 start convert

    DS18B20_Check();
    DS18B20_Write_Byte(DS18B20_SKIP_ROM);        // skip rom
    DS18B20_Write_Byte(DS18B20_READ_SCRATCHPAD); // convert
    tab[0] = a = DS18B20_Read_Byte();            // LSB
    tab[1] = b = DS18B20_Read_Byte();            // MSB
    tab[2] = DS18B20_Read_Byte();                //
    tab[3] = DS18B20_Read_Byte();                //
    tab[4] = DS18B20_Read_Byte();                //
    tab[5] = DS18B20_Read_Byte();                //
    tab[6] = DS18B20_Read_Byte();                //
    tab[7] = DS18B20_Read_Byte();                //
    tab[8] = DS18B20_Read_Byte();                // CRC
    CRC8 = DS18B20_Crc(tab,8);
    if (CRC8 != tab[8])                          // CRC校验
    {
        return value;
    }

    temp = (b << 8) | a;
    if ((temp & 0xf800) == 0xf800)
    {
        temp = (~temp) + 1;
        value = temp * (-0.0625);
    }
    else
    {
        value = temp * 0.0625;
    }
    return value;
}
//=========================DS18B20温度传感器=========================//
回复

使用道具 举报

14#
ID:1110477 发表于 2025-3-20 05:33 | 只看该作者
看来要好好读手册了
回复

使用道具 举报

15#
ID:844772 发表于 2025-3-25 17:30 | 只看该作者
cien_s 发表于 2025-3-19 15:40
您好,请问单总线结构要关中断的吗?我之前是没有关中断的,我尝试在读取温度函数前后,关开中断,但是还 ...

我的意思是它对时序特别敏感,如果被打断就读错,开中断肯定会有问题。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表