2201_75403186 2026-03-08 19:58 采纳率: 0%
浏览 3

ds18b20+51单片机初始化失败,求找逻辑问题

初始化失败,数码管显示失败返回值-999,不能正常显示温度,代码仿照买开发板商家送的代码,其代码下载后单片机能正常显示,问deepseek说是硬件问题,请求各位看一下代码中出现的bug,应该不是硬件的问题

void delay_us(unsigned int us)
{
    // 先补偿函数调用和循环的指令开销(约1个机器周期)
    _nop_();
    
    // 核心循环:每轮循环≈1μs(适配11.0592MHz)
    while(us--)
    {
        // 1个_nop_()≈1.085μs,刚好覆盖1μs需求,且抵消循环指令的微小开销
        _nop_();
    }
}    
unsigned char Init(void)
{
    unsigned char timeout;
    //unsigned char ack_flag;  // 存在脉冲标志位
    //复位
    DQ=0; 
    delay_us(750);
    DQ=1;
    delay_us(30);       // 稍等片刻,让传感器准备拉低(可选,但建议保留)

    // 2. 等待传感器拉低总线(存在脉冲开始)
    timeout = 0;
    while (DQ && timeout < 100) {   // 最多等待约 100μs(根据实际调整)
        timeout++;
        delay_us(1);    // 每次等待约 1μs
    }
    if (timeout >= 100) return 0;   // 超时,无设备

    // 3. 等待传感器释放总线(存在脉冲结束)
    timeout = 0;
    while (!DQ && timeout < 300) {  // 最多等待约 300μs(存在脉冲最长 240μs)
        timeout++;
        delay_us(1);
    }
    if (timeout >= 300) return 0;   // 超时,异常

    return 1;   // 存在脉冲检测成功
  • 写回答

1条回答 默认 最新

  • 檀越@新空间 2026-03-08 19:59
    关注

    晚上好🌙🌙🌙
    本答案参考通义千问

    你提供的代码是用于 DS18B20 与 51 单片机 的初始化部分,但你在使用过程中遇到了 初始化失败 的问题,导致数码管显示返回值为 -999,这说明温度数据没有被正确读取。


    ✅ 一、问题分析

    根据你的描述,代码在开发板上可以正常运行,但在你的系统中不行,说明可能是以下原因之一:

    • 硬件连接问题(如 DQ 引脚未正确连接)
    • 延时函数不准确(影响 DS18B20 通信时序)
    • 初始化逻辑错误(例如未正确检测响应脉冲)
    • 单片机晶振频率不匹配(导致延时误差)

    你提到 DeepSeek 认为是硬件问题,但我们先从软件逻辑入手排查,因为你说“代码仿照买开发板商家送的代码”,而该代码在开发板上能正常运行,所以很可能只是延时函数或初始化逻辑存在细微差异


    🔍 二、关键逻辑问题分析

    1. 延时函数是否准确?

    你提供的 delay_us 函数是这样写的:

    void delay_us(unsigned int us)
    {
        _nop_();
        while(us--)
        {
            _nop_();
        }
    }
    

    这个延时函数的问题在于:它假设每个 _nop_() 等于 1μs,但实际上,在 51 单片机中,一个机器周期是 12 个时钟周期,如果使用的是 11.0592MHz 晶振,那么一个机器周期是:

    $$ \frac{1}{11.0592 \times 10^6} \times 12 = 1.085 \mu s $$

    也就是说,一个 _nop_()约 1.085 μs,而不是 1 μs。因此,你的延时函数实际延时时间会比预期大很多,这将导致 DS18B20 初始化失败


    🛠 三、解决方案(修改延时函数)

    ✅ 修改后的 delay_us 函数(更精确)

    void delay_us(unsigned int us)
    {
        unsigned int i;
        for (i = 0; i < us * 1.085; i++) { // 乘以 1.085 来补偿机器周期
            _nop_();
        }
    }
    

    或者,如果你希望保持整数运算,可以写成:

    void delay_us(unsigned int us)
    {
        unsigned int i;
        for (i = 0; i < us * 10; i++) { // 假设每个循环耗时约 10 个机器周期(即 10.85 μs)
            _nop_();
        }
    }
    

    注意: 如果你使用的是其他频率的晶振(如 12 MHz),请重新计算机器周期。


    ✅ 四、检查初始化逻辑

    你当前的 Init() 函数如下:

    unsigned char Init(void)
    {
        unsigned char timeout;
        DQ=0; 
        delay_us(750);
        DQ=1;
        delay_us(30);       // 稍等片刻,让传感器准备拉低(可选,但建议保留)
    
        // 2. 等待传感器拉低总线(存在脉冲开始)
        timeout = 0;
        while (DQ && timeout < 100) {   // 最多等待约 100μs(根据实际调整)
            timeout++;
            delay_us(1);    // 每次等待约 1μs
        }
        if (timeout >= 100) return 0;   // 超时,无设备
    
        // 3. 等待传感器释放总线(存在脉冲结束)
        timeout = 0;
        while (!DQ && timeout < 300) {  // 最多等待约 300μs(存在脉冲最长 240μs)
            timeout++;
            delay_us(1);
        }
        if (timeout >= 300) return 0;   // 超时,异常
    
        return 1;   // 存在脉冲检测成功
    }
    

    ❗️ 问题点:

    • DQ 是否定义为正确的 IO 口?
      请确认 DQ 是你定义的某个引脚(如 P1^0),并且在程序开头有定义:

      #define DQ P1^0
      
    • 延时函数是否正确?
      上面我们已经指出 delay_us 不够准确,会导致 DS18B20 无法正确响应

    • while (DQ && timeout < 100) 这里的判断是否合理?
      DS18B20 在复位后会拉低总线 约 60~240 μs,所以在 DQ == 0 的时候才表示响应了。
      所以你的这段代码是 错误的,应该改为:

      while (!DQ && timeout < 100) {  // 等待传感器拉低总线
          timeout++;
          delay_us(1);
      }
      

      并且 超时条件应为 timeout >= 100,表示没检测到响应。


    ✅ 五、推荐修改后的完整 Init() 函数

    unsigned char Init(void)
    {
        unsigned char timeout;
    
        DQ = 0;           // 主机拉低总线
        delay_us(750);    // 复位脉冲至少 480μs(实际 750μs 更安全)
    
        DQ = 1;           // 释放总线
        delay_us(30);     // 等待传感器响应
    
        // 等待传感器拉低总线(响应脉冲)
        timeout = 0;
        while (!DQ && timeout < 100) {
            timeout++;
            delay_us(1);
        }
    
        if (timeout >= 100) return 0;  // 无响应
    
        // 等待传感器释放总线(脉冲结束)
        timeout = 0;
        while (DQ && timeout < 300) {
            timeout++;
            delay_us(1);
        }
    
        if (timeout >= 300) return 0;  // 脉冲过长,可能出错
    
        return 1;  // 成功
    }
    

    ✅ 六、总结与建议

    | 问题 | 原因 | 解决方案 | |------|------|----------| | 延时函数不准确 | _nop_() 实际耗时大于 1μs | 使用更精确的延时函数(考虑机器周期) | | 初始化逻辑错误 | 错误地判断 DQ 是否为高 | 正确判断 DQ 是否为低,等待响应脉冲 | | 硬件连接问题 | DQ 引脚未正确连接 | 检查硬件连接,确保 DS18B20 接线正确 | | 晶振频率不匹配 | 延时函数基于错误频率 | 确保延时函数与实际晶振频率一致 |


    📌 七、建议测试步骤

    1. 用示波器检查 DQ 引脚是否有脉冲,确认 DS18B20 是否响应。
    2. 打印 Init() 返回值,看是否为 10
    3. 调试 delay_us 函数,用示波器验证延时是否符合预期。
    4. 检查 DS18B20 的电源和接地是否稳定,避免供电不足导致通信失败。

    如果你愿意,我可以进一步帮你调试整个 DS18B20 读取流程,包括读取温度值的代码。欢迎继续提问!

    评论

报告相同问题?

问题事件

  • 创建了问题 3月8日