晚上好🌙🌙🌙
本答案参考通义千问
你提供的代码是用于 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 接线正确 |
| 晶振频率不匹配 | 延时函数基于错误频率 | 确保延时函数与实际晶振频率一致 |
📌 七、建议测试步骤
- 用示波器检查 DQ 引脚是否有脉冲,确认 DS18B20 是否响应。
- 打印
Init() 返回值,看是否为 1 或 0。 - 调试
delay_us 函数,用示波器验证延时是否符合预期。 - 检查 DS18B20 的电源和接地是否稳定,避免供电不足导致通信失败。
如果你愿意,我可以进一步帮你调试整个 DS18B20 读取流程,包括读取温度值的代码。欢迎继续提问!