第六周——DS18B20模块程序设计

:clipboard: 一、项目概述

1.1 项目功

  • 温度读取:使用DS18B20传感器实时采集温度

  • 温度显示:通过数码管显示当前温度(精确到0.1℃)

  • 温度设置:设置温度上限和下限值(范围:10~70℃)

  • 温度控制:根据温度值控制LED指示灯

  • 错误检测:设置参数校验和错误提示

1.2 硬件配置

  • 主控芯片:STC15F2K60S2(51单片机)

  • 温度传感器:DS18B20(单线数字温度传感器)

  • 输出设备

    • 8位数码管显示

    • 4个LED指示灯(L1、L2、L3、L4)

  • 输入设备:矩阵按键(模式切换、参数调节)


:bar_chart: 二、系统工作原理

2.2 系统工作流程

 TEXT1启动 → 初始化DS18B20 → 延时750ms
 2  ↓
 3读取温度值(t) → 显示处理 → Led状态控制
 4  ↓
 5按键输入 → 温度参数设置与验证
 6  ↓
 7循环执行
 8

2.3 温度范围划分

范围 条件 LED状态 LED_Pwm 说明
高于上限 t > t_Set_OK[0] L1亮 3 温度过高
正常范围 t_Set_OK[1] ≤ t ≤ t_Set_OK[0] L2亮 6 温度正常
低于下限 t < t_Set_OK[1] L3亮 9 温度过低
错误操作 Error_Flag=1 L4亮 - 设置参数错误

:wrench: 三、核心数据结构

3.1 全局变量声明

 C1//温度相关变量
 2float t;                          // 实时温度值
 3unsigned char t_Set[2]={30,20};   // 设置缓存 [0]=上限,[1]=下限
 4unsigned char t_Set_OK[2]={30,20}; // 生效参数 [0]=上限,[1]=下限
 5
 6//显示相关变量
 7unsigned char Seg_Buf[8]={...};   // 数码管数据缓存
 8unsigned char Seg_Point[8]={...}; // 小数点控制
 9unsigned char Seg_Mode;           // 0=显示模式,1=设置模式
 10unsigned char Seg_Pos;            // 数码管扫描位置
 11unsigned int Seg_Slow;            // 数码管更新速度控制
 12
 13//LED控制变量
 14unsigned char ucLed[8];           // LED状态数组
 15unsigned char Led_Pwm;            // LED亮度等级(0-10)
 16unsigned char Led_Num;            // LED PWM计数
 17
 18//按键相关变量
 19unsigned char Key_Val, Key_Down, Key_Up, Key_Old;
 20unsigned char Key_Slow;           // 按键减速计数
 21bit Key_Flag;                     // 长按标志
 22unsigned int Key_Tick;            // 按键计时
 23
 24//错误与标志
 25bit Seg_Star_Flag;                // 闪烁标志(500ms翻转)
 26bit Error_Flag;                   // 错误标志
 27unsigned int Timer_500ms;         // 500ms计时器
 28

:video_game: 四、按键功能模块

4.1 按键映射表

按键值 功能 说明
12 模式切换 显示模式 ↔ 设置模式
13 参数切换 上限 ↔ 下限
14 参数增加 t_Set[index]++
15 参数减少 t_Set[index]–
16 恢复默认 重置为初始值

4.2 按键处理逻辑

短按 (Key_Tick ≤ 500ms)

 C1按键按下 → 立即执行一次操作 → 按键抬起结束
 2

长按 (Key_Tick > 500ms)

 C1按键按下 → 500ms后开始连续执行 → 按键抬起结束
 2

按键消抖机制

 C1// 消抖算法:只检测按键状态变化
 2Key_Down = Key_Val & (Key_Val ^ Key_Old);  // 下降沿(按下)
 3Key_Up = ~Key_Val & (Key_Val ^ Key_Old);   // 上升沿(抬起)
 4Key_Old = Key_Val;                          // 保存状态
 5
 6// 减速:每10ms采样一次
 7if(Key_Slow) return;
 8Key_Slow = 1;  // 置位减速标志
 9

4.3 温度参数设置流程

 TEXT1模式切换(K12) → Seg_Mode=1(进入设置)
 2  ↓
 3参数编辑 ← K14增加 / K15减少 / K13切换上下限 →
 4  ↓
 5确认保存(K12) → 参数校验
 6  ├─ 通过 → t_Set_OK[index] = t_Set[index] → Seg_Mode=0 → Error_Flag=0
 7  └─ 失败 → 恢复原值 → Error_Flag=1 → 点亮L4
 8

4.4 参数校验条件 (第109行、132行、148行)

 C1// 校验条件:三个都满足才能保存
 2if (t_Set[t_Set_Detex] <= 70 &&      // ① 不超过70℃
 3    t_Set[t_Set_Detex] >= 10 &&      // ② 不低于10℃
 4    t_Set[0] > t_Set[1])              // ③ 上限 > 下限
 5{
 6    Error_Flag = 0;
 7    t_Set_OK[t_Set_Detex] = t_Set[t_Set_Detex];
 8}
 9else
 10{
 11    Error_Flag = 1;
 12    t_Set[t_Set_Detex] = t_Set_OK[t_Set_Detex];  // 恢复原值
 13}
 14

:television: 五、数码管显示模块

5.1 显示模式切换

模式0:温度显示 (第187-195行)

 C1┌─────────────────────────┐
 2│ [11] - [ ] [ ] [ ] [T1][T0].[T-1] [11] │
 3│  "℃"   熄灭   熄灭   熄灭  十  个  0.1℃  "℃" │
 4└─────────────────────────┘
 5
 6例如:25.3℃ 显示为
 7Seg_Buf: [11, 10, 10, 10, 2, 5, 3, 11]
 8         [℃,  -,  -,  -,  2, 5, ., 3℃]
 9

代码实现

 C1Seg_Point[5] = 1;                          // 小数点在第5位
 2Seg_Buf[0] = 11;                           // 摄氏度符号
 3Seg_Buf[4] = (unsigned char)t / 10 % 10;   // 十位
 4Seg_Buf[5] = (unsigned char)t % 10;        // 个位
 5Seg_Buf[6] = (unsigned int)(t*10) % 10;    // 小数位
 6Seg_Buf[7] = 11;                           // 摄氏度符号
 7

模式1:参数设置 (第197-210行)

 C1┌─────────────────────────┐
 2│ [12] [ ] [ ] [Max10][Max0] [13] [Min10][Min0] │
 3│  "H"  -   -   最大值十  最大个  "━"  最小值十 最小个 │
 4└─────────────────────────┘
 5
 6其中:
 7• [12] = "H" 表示模式提示
 8• [13] = "━" 分隔符
 9• 闪烁显示当前编辑项
 10

闪烁实现 (第207-208行):

 C1// 每500ms翻转一次(Seg_Star_Flag)
 2// 选中项显示数字,未选中显示10(熄灭)
 3Seg_Buf[3*t_Set_Detex+3] = Seg_Star_Flag ? t_Set[t_Set_Detex]/10%10 : 10;
 4Seg_Buf[3*t_Set_Detex+4] = Seg_Star_Flag ? t_Set[t_Set_Detex]%10 : 10;
 5

5.2 数码管扫描机制

 C1定时器每1ms中断一次
 2  ↓
 3Seg_Pos循环 0→1→2→3→4→5→6→7→0
 4  ↓
 5每8ms显示一位数字(8×1ms)
 6  ↓
 7人眼看起来 8 位全部点亮(动态显示)
 8

:light_bulb: 六、LED指示灯控制模块

6.1 LED与温度的对应关系

 C1void Led_Proc()
 2{
 3    // ① 驱动等级设置
 4    if(t > t_Set_OK[0])              Led_Pwm = 3;    // 高温
 5    if(t < t_Set_OK[1])              Led_Pwm = 9;    // 低温
 6    if(t_Set_OK[1] <= t <= t_Set_OK[0]) Led_Pwm = 6; // 正常
 7    
 8    // ② LED亮度控制(PWM方式)
 9    // ...LED亮度通过PWM占空比控制(1/3、1/2、9/10)
 10}
 11

6.2 LED点亮逻辑

LED 表示 点亮条件
L1 高温告警 ucLed[0] = (t > t_Set_OK[0]) ? 1 : 0
L2 温度正常 ucLed[1] = ((t >= t_Set_OK[1]) && (t <= t_Set_OK[0])) ? 1 : 0
L3 低温告警 ucLed[2] = (t < t_Set_OK[1]) ? 1 : 0
L4 错误操作 ucLed[3] = Error_Flag

PWM亮度调节机制 (第268-272行)

 C1// 0~9循环计数,PWM周期为10ms
 2if(++Led_Num == 10) Led_Num = 0;
 3
 4// 根据Led_Pwm占空比决定亮度
 5if(Led_Num < Led_Pwm)  // 占空比 = Led_Pwm/10
 6{
 7    Led_Disp(Seg_Pos, ucLed[Seg_Pos]);  // 点亮LED
 8}
 9// 否则 LED 自动熄灭
 10
 11// 例如:Led_Pwm=6 → 占空比60% → 亮度为满亮的60%
 12

:alarm_clock: 七、定时器中断处理

7.1 定时器初始化 (第231-241行)

 C1void Timer0_Init(void)  // 1ms中断一次 @12MHz
 2{
 3    AUXR &= 0x7F;   // 12T模式
 4    TMOD &= 0xF0;   // 定时器0模式2
 5    TL0 = 0x18;     // 装载初值
 6    TH0 = 0xFC;     // 装载初值
 7    TF0 = 0;        // 清除溢出标志
 8    TR0 = 1;        // 启动定时器
 9    ET0 = 1;        // 使能中断
 10    EA = 1;         // 全局中断使能
 11}
 12
 13// 中断周期 = (256-0xFC18)*12/12MHz = 1ms
 14

7.2 定时器中断服务函数 (第244-273行)

 TEXT1每1ms进入中断一次
 2  ├─ Key_Slow++  (每10ms采样一次按键)
 3  ├─ Seg_Slow++  (每500ms更新一次显示)
 4  ├─ Seg_Pos++   (动态扫描8位数码管,每125μs一位)
 5  ├─ Timer_500ms++ (500ms闪烁计时)
 6  ├─ Key_Tick++  (长短按判断计时)
 7  └─ Led_Num++   (PWM计数)
 8

具体计时

计时器 周期 用途
Key_Slow 10ms 按键减速采样
Seg_Slow 500ms 数码管温度更新
Timer_500ms 500ms 闪烁翻转
Led_Num 10ms(0~9) LED PWM
Seg_Pos 8ms(0~7) 数码管扫描

:electric_plug: 八、DS18B20单线通信协议简介

8.1 OneWire协议特点

  • 通信线数:仅1条数据线

  • 传输方式:半双工同步通信

  • 总线空闲:上拉电阻保持高电平

  • 数据表示

    • 逻辑1:主机释放线路,从机在~60μs内发送

    • 逻辑0:主机拉低线路60μs+

8.2 DS18B20温度读取流程

 TEXT1┌────────────────────────────────────────────────┐
 2│                  DS18B20温度读取流程                │
 3└────────────────────────────────────────────────┘
 4
 5① 初始化Reset
 6   主机拉低线路480μs → 释放线路 → 从机在15~60μs拉低60~240μs
 7
 8② ROM指令(跳过)
 9   发送 0xCC 字节 → 跳过ROM匹配
 10
 11③ 启动转换
 12   发送 0x44 字节 → DS18B20开始温度转换
 13
 14④ 等待转换完成
 15   根据分辨率延时 (12bit分辨率约750ms)
 16
 17⑤ 读取温度
 18   发送 0xBE 字节 → 读取Scratchpad寄存器 (9字节)
 19   └─ 字节0(LSB) + 字节1(MSB) = 16bit温度值
 20
 21⑥ 温度处理
 22   温度值 = (MSB<<8) | LSB
 23   实际温度 = 温度值 * 0.0625
 24

8.3 温度数据格式 (12bit分辨率)

 TEXT1  高字节(MSB)         低字节(LSB)
 2┌─────────────┐ ┌─────────────┐
 3│ S S S ℃℃℃℃ │ │ 0.5 0.25 0.125 0.0625 │
 4│─── ────── │ │ ────────────── │
 5└─────────────┘ └─────────────┘
 6 符号 整数部分      小数部分
 7
 8例如:25.3℃
 9• 整数部分:25 = 0x19
 10• 小数部分:0.3 ≈ 0x5(0x0101 = 0.3125℃)
 11• MSB = 0x19, LSB = 0x50
 12• 温度值 = 0x1950 = 6480
 13• 实际温度 = 6480 * 0.0625 = 25.3℃
 14

:memo: 九、程序执行流程

9.1 主程序流程 (main函数)

 C1main()
 2  ├─ re_tempterure()       // ① 初始化DS18B20并读取一次温度
 3  ├─ Delay750ms()          // ② 等待750ms确保系统稳定
 4  ├─ System_Init()         // ③ 初始化GPIO、LED、数码管等
 5  ├─ Timer0_Init()         // ④ 启动定时器中断
 6  └─ while(1)              // ⑤ 主循环
 7      ├─ Key_proc()        // 按键处理(10ms执行一次)
 8      ├─ Seg_Proc()        // 显示处理(500ms更新一次)
 9      └─ Led_Proc()        // LED控制(实时执行)
 10

9.2 时序关系

 TEXT1主线程                   中断线程(1ms)
 2├─ Key_proc()           ┌─ Key_Slow++
 3├─ Seg_Proc()    ←→     ├─ Seg_Slow++
 4├─ Led_Proc()           ├─ Timer_500ms++
 5├─ ...等待              ├─ Key_Tick++
 6└─ 循环                 ├─ Led_Num++
 7                        └─ Seg_Pos++
 8

:graduation_cap: 学习重点总结

核心知识点

:white_check_mark: 单线通信协议:掌握OneWire基本时序
:white_check_mark: 定时器中断:理解多个计时器共存的设计
:white_check_mark: 动态扫描显示:8位数码管分时复用显示
:white_check_mark: 按键处理:消抖、短按、长按区分
:white_check_mark: 参数验证:设置值与生效值的分离设计
:white_check_mark: 状态机编程:显示模式与设置模式切换
:white_check_mark: PWM调光:LED亮度调节方法

调试技巧

  1. 温度显示不对:检查re_tempterure()函数和DS18B20通信

  2. 按键无响应:检查Key_Read()和中断是否启用

  3. 数码管显示错乱:检查Seg_Buf数组索引和扫描位置

  4. LED闪烁:检查PWM占空比和计时器频率

  5. 参数无法保存:检查校验条件是否满足(第109行)


:books: 代码框架参考

 C1// 完整程序框架
 2#include <...>  // 头文件
 3
 4// 全局变量(见第3.1节)
 5
 6// 各模块函数
 7void Key_proc();      // 按键处理
 8void Seg_Proc();      // 显示处理
 9void Led_Proc();      // LED控制
 10void Timer0_Init();   // 定时器初始化
 11void Timer0_Server(); // 定时器中断服务
 12
 13void main()
 14{
 15    // 初始化阶段
 16    re_tempterure();
 17    Delay750ms();
 18    System_Init();
 19    Timer0_Init();
 20    
 21    // 主循环:定时中断驱动
 22    while(1)
 23    {
 24        Key_proc();   // 键盘驱动
 25        Seg_Proc();   // 显示更新
 26        Led_Proc();   // LED控制
 27    }
 28}
 29

:telephone_receiver: 快速参考表

LED状态对应表

 TEXT1温度        L1(高温)  L2(正常)  L3(低温)  L4(错误)
 2t>30℃        ✓         ✗        ✗        ✗
 320≤t≤30℃     ✗         ✓        ✗        ✗
 4t<20℃        ✗         ✗        ✓        ✗
 5参数错误      ✗         ✗        ✗        ✓
 6

按键功能速查

 TEXT1K12: 显示 ↔ 设置(模式切换)
 2K13: 上限 ↔ 下限(参数切换)
 3K14: 参数 ++ (增加)
 4K15: 参数 -- (减少)
 5K16: 恢复默认值
 6

定时器分频表

 TEXT1定时器      周期     用途
 2Key_Slow    10ms    按键采样
 3Seg_Slow    500ms   温度更新
 4Timer_500ms 500ms   闪烁翻转
 5Led_Num     1~10ms  LED PWM
 6Seg_Pos     1~8ms   数码管扫描
 7
1 个赞