单片机第六周重制版

:thermometer: DS18B20 温度读取模块笔记

一、 底层驱动核心 (OneWire.c)

底层驱动的核心在于时序控制读写逻辑。为了方便记忆,我们将其拆解为“三个基础动作”和“一个组合大招”。

1. 硬件引脚定义

蓝桥杯 CT107D 板子上,DS18B20 连接在 P1.4

C

sbit DQ = P1^4;  // 必须背下来的引脚定义

2. 核心延时函数 (关键)

单总线通信对时间非常敏感,STC15F2K60S2 速度快,延时需要特别注意。

C

// 单总线专用延时,不同于主函数的Delay
void Delay_OneWire(unsigned int t)  
{
    // STC15系列速度快,如果官方驱动给的是t*=12,
    // 在比赛提供的驱动中通常不需要改,直接用提供的即可。
    // 如果是自己写,注意不要让它跑太快。
    while(t--); 
}

3. 三个基础动作 (初始化、写、读)

这部分通常在比赛提供的资源包里有,只需理解,重点是记忆 init 的返回值判断

  • 初始化 (init_ds18b20): 复位 → 检测存在脉冲。

  • 写字节 (Write_DS18B20): 低位先行 (LSB)。

  • 读字节 (Read_DS18B20): 低位先行 (LSB)。

4. 组合大招:读取温度函数 (read_t) :glowing_star::glowing_star::glowing_star:

这是你需要重点记忆和手写的部分,它把底层的复杂操作封装成一个简单函数供主函数调用。

逻辑口诀: 复位跳过转换,复位跳过读取。

C

float read_t()
{
    unsigned char low, high; // 存放温度的低8位和高8位
    
    // --- 第一步:启动温度转换 ---
    init_ds18b20();      // 1. 复位
    Write_DS18B20(0xcc); // 2. 跳过ROM (因为总线上只有一个设备)
    Write_DS18B20(0x44); // 3. 发送温度转换命令
    
    // --- 第二步:读取温度数据 ---
    init_ds18b20();      // 4. 再次复位
    Write_DS18B20(0xcc); // 5. 跳过ROM
    Write_DS18B20(0xbe); // 6. 发送读取暂存器命令
    
    low = Read_DS18B20();  // 7. 先读低字节
    high = Read_DS18B20(); // 8. 后读高字节
    
    // --- 第三步:合成数据并转化 ---
    // high << 8 | low : 合成16位整数
    // / 16.0 : DS18B20的分辨率是0.0625,除以16等于乘以0.0625
    return ((high << 8) | low) / 16.0; 
}

二、 主函数调用逻辑 (Main.c)

主函数的关键在于消除上电初期的 85°C 异常显示以及数据的提取显示

1. 上电初始化 (消除 85°C) :glowing_star:

DS18B20 上电默认寄存器值为 85°C。为了避免开机瞬间显示 85,需要在进入 while(1) 之前先读一次并延时。

C

void main()
{
    read_t();      // 先读一次,触发转换(这次的值虽然不准或者是85,但我们不显示)
    Delay750ms();  // 等待转换完成(DS18B20最高精度转换需要750ms以上)
    
    System_Init(); // 硬件初始化(关蜂鸣器等)
    Timer0Init();  // 定时器初始化
    
    while (1)
    {
        Key_Proc();
        Seg_Proc();
        Led_Proc();
    }
}

2. 周期性读取与显示处理 (Seg_Proc)

为了不影响数码管扫描和按键,温度读取放在减速后的逻辑中。

C

void Seg_Proc()
{
    // 减速处理:每500ms读取一次温度,太快了数码管会闪烁,且传感器反应不过来
    if(Seg_Slow_Down) return;
    Seg_Slow_Down = 1; 
​
    // 1. 获取温度
    t = read_t(); 
    
    // 2. 数据分离 (显示 XX.X)
    // 假设温度是 25.6 度
    
    // 十位:25.6 / 10 = 2.56 -> 取整2 -> %10 -> 2
    Seg_Buf[0] = (unsigned char)t / 10 % 10; 
    
    // 个位:25.6 % 10 -> 5 (注意:float取模行为不同编译器不同,通常强转int再取模更稳)
    // 你的写法:(unsigned char)t % 10 是安全的
    Seg_Buf[1] = (unsigned char)t % 10;      
    
    // 小数位:25.6 * 10 = 256 -> (int)256 % 10 -> 6
    Seg_Buf[2] = (unsigned int)(t * 10) % 10; 
    
    // 3. 别忘了点亮小数点!
    // 你的代码定义了 Seg_Point 数组,需要在 Timer0Server 里配合使用
    // 假设你要在第2位数码管(下标1)点亮小数点:
    // Seg_Point[1] = 1; // 需要在初始化或这里手动赋值
}

三、 避坑与记忆清单 (Cheatsheet)

  1. 引脚定义sbit DQ = P1^4; (蓝桥杯板载)。

  2. 公式记忆

    • 读取顺序:先低后高 (low then high)。

    • 计算公式:((high << 8) | low) / 16.0

  3. 流程记忆

    • CC 44 (转换):Init → Skip(CC) → Convert(44)。

    • CC BE (读取):Init → Skip(CC) → Read(BE)。

  4. 上电避坑read_t() + Delay750ms() 放在 while(1) 之前。

  5. 类型转换:在拆分显示小数时,一定要把 t * 10 强制转换为整型 (unsigned int) 再取模,否则计算结果可能出错。