ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

普中51A2温度报警实战:DS18B20单总线时序与状态机设计

2026/9/12 10:29:44 拓冰建站 浏览量
普中51A2温度报警实战:DS18B20单总线时序与状态机设计 简介这是一个基于普中51A2开发板的温度报警系统完整工程面向51单片机初学者与课程设计人群。系统利用DS18B20采集温度经LCD1602分两行显示第一行显示设定的温度上下限第二行显示实时温度用户通过四个独立按键可分别调节上限加、上限减、下限加、下限减当温度超过上限或低于下限时蜂鸣器立即报警。资源共24个文件主要包含C语言源码、对应头文件、可直接烧录的hex文件、Keil工程及Proteus仿真文件等压缩包仅68KB其中Keil工程已配置好编译选项打开即可编译Proteus仿真文件可直观模拟温度变化时的报警过程便于结合源码逐行调试。已有1095人学习下载适合用来自学51单片机外设驱动、按键处理和报警逻辑设计也可直接作为课程设计参考方案。1. 普中51A2 温度报警卡住你的不是报警逻辑是单总线时序普中51A2单片机温度报警是51课程设计里出现频率最高的题目之一也是“单片机原理及应用”期末验收的标准动作DS18B20测温、LCD1602显示、按键设阈值、蜂鸣器报警。多数人卡住的是DS18B20单总线时序——复位检测不到存在脉冲或温度恒为0°C、85°C调两天才发现是延时参数没对齐。这里按普中51A2板载资源把链路走通硬件接线、读写时序、报警状态机、阈值掉电保存。代码按STC89C5211.0592MHz写延时给了换算方法换晶振或开6T模式时自己改。适合三类人做课程设计的学生、想跑通完整外设链路的入门者、被实验箱读不出数折磨的实验员。读完能出数也能答得上答辩老师那句“阈值设30°C为什么28°C才停报警”。2. 硬件选型与引脚映射DS18B20 为什么是普中51A2 的默认答案2.1 三种测温方案的取舍先立结论这个题目用DS18B20不是唯一解但在普中51A2 的硬件条件下几乎是全局最优解。STC89C52 没有片上 ADC选 LM35 这类模拟输出就得让板载 ADC0804 做并行读取P1 口八根线全被占掉而 LCD1602 的数据口已经占了 P0剩下的引脚要同时分给按键、蜂鸣器、LED布局会非常局促。NTC 热敏电阻成本最低但每个板子的分压电阻值不一致标定一次要烧掉一个下午课程设计验收根本耗不起。测温方案输出形式占用资源精度适配场景DS18B20单总线数字1 个 IODQ±0.5°C课程设计、工业表头LM35 ADC0804模拟电压转并行数字P1 全口 片选控制约 1°C8 位量化教学演示 A/D 转换NTC 分压电阻模拟电压1 个 IO需标定1~2°C成本敏感的大批量产品DS18B20 的三点优势决定了它的地位单总线只占一根口线数字输出绕开了 STC89C52 没有 ADC 的短板分辨率最高 0.0625°C-55°C 到 125°C 的量程对室内温控绰绰有余。对这块板子来说还有第四个隐性优势——板上已经焊好了三针座和 4.7k 上拉电阻不需要自己搭外围电路插上就能用。2.2 普中51A2 板载资源与引脚分配原则下面这组引脚是普中51A2 做温度报警的默认分配也是市面上多数例程的接法。普中科技出过多个批次蜂鸣器、按键的丝印偶尔会互换上板前先对照手里那块板的原理图确认一遍。// 普中51A2 温度报警所有外设引脚定义集中放一个头文件换批次只改这里 sbit DQ P3^7; // DS18B20 数据线接三针座中间脚 sbit BEEP P2^3; // 蜂鸣器经 ULN2003 驱动低电平响 sbit FAN P2^4; // 继电器/散热风扇选配低电平动作 sbit KEY_MODE P3^0; // 模式切换按键按下为低 sbit KEY_UP P3^1; // 阈值加 sbit KEY_DOWN P3^2; // 阈值减 sbit LCD_RS P2^5; // LCD1602 寄存器选择 sbit LCD_RW P2^6; // LCD1602 读写选择 sbit LCD_E P2^7; // LCD1602 使能外设引脚说明LCD1602 DB0~DB7P0.0~P0.7板载排阻上拉直接接蜂鸣器 BEEPP2.3低电平驱动LED 低四位P2.0~P2.3报警指示可复用独立按键 K1~K4P3.0~P3.3需软件消抖分配思路有三个原则蜂鸣器、LED 这类只有两种状态的外设放在同一组口线方便一次性刷新DS18B20 放 P3 口而不要放 P0因为 P0 是漏极开路结构要靠板载排阻才能稳定输出高电平和 LCD 总线共口容易互相干扰P3 口的准双向特性和单总线天然兼容按键单独占一组不跟任何总线复用避免扫描按键时误触发外设。这样分配后四个口全部用满P0 显示、P2 控制和指示、P3 收传感器数据和按键。上板前的动作是先确认最小系统四件套——电源、晶振、复位、下载——再焊传感器否则排错时会把电源问题和时序问题混在一起。2.3 传感器摆放位置直接影响读数插 DS18B20 时让金属壳朝板子外侧。测温时要让传感器离开板载 7805 稳压器那个管子待机就有十几度的温升贴它太近测出来的温度会系统性偏高 3~5°C而且这种偏差是线性的——读数越接近 30°C你越难怀疑到是布局问题。温度报警这种题目传感器位置不对导致的“假报警”比代码 bug 更难排查。3. 读温度DS18B20 单总线时序与负温解析3.1 复位脉冲与存在脉冲检测DS18B20 的每一次读写都以“复位—应答”开头。主机把 DQ 拉低 480~960us 再释放从机检测到这个低电平后会在 15~60us 内把 DQ 拉低 60~240us 作为存在脉冲。程序只要在释放总线后延时一段时间再去采样电平就能判断总线上有没有设备。// DQ 延时单循环约 7 个机器周期11.0592MHz 下约 7.6us // 注意参数是循环次数不是微秒数写注释时按时间换算记录 void DelayDs18(unsigned int n) { while (n--) { _nop_(); _nop_(); _nop_(); _nop_(); } } bit DS18B20_Reset(void) { bit presence; DQ 0; DelayDs18(70); // 拉低约 530us必须大于 480us DQ 1; DelayDs18(12); // 释放后约 91us 采样落在存在脉冲窗口内 presence DQ; // 读到 0 表示总线上有设备 DelayDs18(40); // 等待存在脉冲结束约 300us return presence; }存在脉冲丢失时不要急着查代码。先量 DQ 脚的直流电平VCC/GND 接反时 DQ 会表现成一直高或一直低这是普中51A2 上最常见的接错方式。DS18B20 对“低电平时间偏长”没有严格上限所以DelayDs18(70)这个粗略延时足够可靠真正有上限要求的是后面读时隙里的采样位置那个地方不能用这个函数糊弄。3.2 读写一个 bit采样点落在哪决定成败写时隙必须覆盖完整的总时隙。写 1 和写 0 的差别只在于“起始低电平之后有没有在 15us 内释放总线”写 0 要持续拉低 60~120us写 1 则在 15us 内拉高。读时隙是另一个坑主机拉低 1us 后释放从机在 15us 内驱动总线主机必须在这个窗口内采样太早读到的是自己释放后的电平太晚从机已经结束输出。void DS18B20_WriteBit(bit b) { DQ 0; _nop_(); // 起始低电平约 1us if (b) { DQ 1; // 写 115us 内释放总线 } DelayDs18(8); // 维持时隙约 60us DQ 1; _nop_(); // 恢复空闲高电平 } bit DS18B20_ReadBit(void) { bit b; DQ 0; _nop_(); // 起始低电平 1us 以上 DQ 1; // 释放总线 _nop_(); // 两条 NOP 后采样约 4.3us _nop_(); b DQ; // 从机驱动窗口内 DelayDs18(8); // 补足 60us 时隙 return b; }51 的_nop_()是一个机器周期11.0592MHz 下约 1.085us两条 NOP 后采样点落在释放后 4~5us处在从机数据的有效窗口内。这里有一个隐藏变量STC89C52 可以用 STC-ISP 设置成 6T 模式机器周期减半所有延时要加倍如果烧录器软件里误选了 6T现象就是存在脉冲正常、读出来全是 0xFF 或 85°C。烧录前先确认频率和倍速设置这是实验箱上出问题频率最高的原因之一。3.3 字节读写与温度转换完整流程void DS18B20_WriteByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DS18B20_WriteBit(dat 0x01); // 低位先发 dat 1; } } unsigned char DS18B20_ReadByte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; if (DS18B20_ReadBit()) dat | 0x80; // 读到位则置最高位 } return dat; } float DS18B20_GetTemperature(void) { unsigned char low, high; int raw; DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 跳过 ROM单设备时省去匹配 64 位序列号 DS18B20_WriteByte(0x44); // 启动温度转换 // 12 位分辨率下转换最长 750ms期间 DQ 持续为低。 // 同步等待会卡死主循环工程上建议改为主循环轮询 // 本轮发 0x44下一轮1s 后再发 0xBE 读取读到的就是上轮结果。 while (DQ 0); // 转换完成后 DQ 被释放为高 DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读暂存器 low DS18B20_ReadByte(); // 第 0 字节温度低 8 位 high DS18B20_ReadByte(); // 第 1 字节符号位 高 8 位 raw (high 8) | low; return raw / 16.0; // 12 位分辨率下每个 LSB 0.0625°C }raw必须声明成int而不是unsigned int。温度是带符号补码负温时最高位为 1如果用无符号类型-55°C 会被解析成 65481显示直接崩掉。raw / 16.0等价于乘 0.0625写成除法更容易看出位权。51 没有硬件浮点库函数浮点运算慢但温度报警是秒级刷新浮点开销完全可以接受如果后续要做 PID 温控再改成定点也不迟。3.4 命令序列与两个“幽灵温度”步骤命令字节值说明初始化Reset—拉低 480~960us跳过 ROMSKIP ROM0xCC单设备总线可省略序列号匹配启动转换CONVERT T0x4412 位模式耗时 750ms再次初始化Reset—每次读操作前都要复位跳过 ROMSKIP ROM0xCC—读暂存器READ SCRATCHPAD0xBE连续读 9 字节前 2 字节是温度读到 85°C 是 DS18B20 的经典特征。85°C 不是传感器坏了而是芯片上电复位后的寄存器默认值——如果 0x44 的转换没有成功执行直接发 0xBE 读到的就是默认 0x0550折合 85°C。出现这个值先检查 0x44 前面有没有做复位。另一个“幽灵温度”是 0°C这是复位命令没生效时从机根本不应答所有位读回高电平的结果。两个都指向同一个问题——时序的起始状态不对而不是算法逻辑错。4. 报警逻辑与显示状态机迟滞、按键设参和 LCD 刷新4.1 用状态机代替裸比较消灭临界抖动直接写if (temp TH) BEEP 0;会有一个肉眼可见的问题温度在阈值附近漂移时蜂鸣器反复响停实验课上会被老师当成故障。标准解法是迟滞比较也叫回差控制进入报警和退出报警用两个不同阈值。这其实就是“当单片机遇上状态机”的典型场景——把报警逻辑做成三态而不是一堆 if 堆叠。#define TH_DEFAULT 300 // 默认高温阈值 30.0°C #define TL_DEFAULT 100 // 默认低温阈值 10.0°C #define HYS 20 // 迟滞 2.0°C定点下为 20 int TH TH_DEFAULT; // 温度以 0.1°C 为单位的定点数 int TL TL_DEFAULT; typedef enum { ALARM_OFF, ALARM_HIGH, ALARM_LOW } ALARM_STATE; ALARM_STATE state ALARM_OFF; void AlarmUpdate(int temp_10x) // 入参温度 × 10如 25.3°C 传入 253 { switch (state) { case ALARM_OFF: if (temp_10x TH) state ALARM_HIGH; else if (temp_10x TL) state ALARM_LOW; break; case ALARM_HIGH: if (temp_10x TH - HYS) state ALARM_OFF; // 低于 28.0°C 才解除 break; case ALARM_LOW: if (temp_10x TL HYS) state ALARM_OFF; // 高于 12.0°C 才解除 break; } BEEP (state ! ALARM_OFF) ? 0 : 1; // 低电平驱动蜂鸣器 FAN (state ALARM_HIGH) ? 0 : 1; // 高温联动风扇 }把温度乘以 10 存成int既躲开浮点比较又方便显示时拆位。HYS的单位是 0.1°C20 就是 2.0°C。进入报警和退出报警的阈值差就是迟滞带这一段区间内状态保持上一次的结果蜂鸣器不会抖动。顺手把高温报警联动到 FAN 引脚就完成了从“报警”到“最简闭环温控”的升级——开风扇降温、降到迟滞带以下停风扇这就是闭环温度控制实验的核心骨架蓝桥杯单片机赛题里的温控题也是同一套逻辑。4.2 按键设参模式切换、消抖与上限保护typedef enum { MODE_RUN, MODE_SET_H, MODE_SET_L } UI_MODE; UI_MODE ui_mode MODE_RUN; void Delay10Ms(unsigned char n); // 系统延时10ms 级 void KeyScan(void) { if (KEY_MODE 0) { // 按下为低电平 Delay10Ms(1); // 10ms 消抖 if (KEY_MODE 0) { ui_mode; if (ui_mode MODE_SET_L) ui_mode MODE_RUN; while (KEY_MODE 0); // 等待松开防止一次按键触发多次 } } if (ui_mode MODE_RUN) return; // 运行模式加减键无效 if (KEY_UP 0) { Delay10Ms(1); if (KEY_UP 0) { if (ui_mode MODE_SET_H) TH 5; // 每次加 0.5°C else TL 5; if (TH 1250) TH 1250; // 不超过 125.0°C while (KEY_UP 0); } } if (KEY_DOWN 0) { Delay10Ms(1); if (KEY_DOWN 0) { if (ui_mode MODE_SET_H) TH - 5; else TL - 5; if (TL -550) TL -550; // 不低于 -55.0°C while (KEY_DOWN 0); } } }加减步进是 5也就是 0.5°C按 10 次才能跨过 5°C精度够了。上位保护 1250 对应 125.0°C下限 -550 对应 -55.0°C两个值正好卡在 DS18B20 的物理量程上防止用户在设置界面把阈值设进传感器够不到的范围。这种“模式循环 加减设参”的交互在单片机课程设计里是通用模板蓝桥杯客观题也常拿它考状态切换的时序判断。想进一步优化手感可以把松手检测while (KEY_UP 0)改成计时累加按住超过 500ms 后每 100ms 自动连加一次代码量不大但答辩时是个加分点。4.3 LCD1602 显示策略与刷新节流void DisplayUpdate(int temp_10x) { unsigned char buf[16]; char minus ; unsigned int v; if (temp_10x 0) { minus -; v (unsigned int)(-temp_10x); } else { v (unsigned int)temp_10x; } buf[0] T; buf[1] :; buf[2] ; buf[3] minus; buf[4] 0 v / 100; // 十位 buf[5] 0 (v / 10) % 10; // 个位 buf[6] .; buf[7] 0 v % 10; // 一位小数 buf[8] ; buf[9] C; // 调用 LCD 写函数将 buf[0..9] 送入第一行 }模式第一行第二行按键作用MODE_RUNT: 25.3 CH: 30.0 L: 10.0无MODE_SET_HSET H: 30.0UP/DOWN 修改改高温阈值MODE_SET_LSET L: 10.0UP/DOWN 修改改低温阈值重复调用sprintf是 51 上最常见的 RAM 杀手链接后光这一句就要吞掉约 1KB 代码空间STC89C52 的 8KB Flash 在塞满 DS18B20、LCD、IAP 之后就很紧张。手动拆位十行代码解决。刷新节流的原则是不要每一次温度转换完都刷屏12 位转换要 750ms1 秒刷一次足够如果按键扫描和 LCD 刷新抢主循环按键会明显变迟钝读温度的时间点也会漂移间接拖垮单总线时序。5. 负温拆字、IAP 掉电保存和上电验证顺序5.1 用 STC89C52 片内 IAP 把阈值存进“EEPROM”答辩高频问题“断电后我设置的阈值还在吗”RAM 里存的变量一断电就没了。STC89C52 没有独立 EEPROM但 STC 把用户 Flash 尾部划出一块区域模拟 EEPROM用 IAP 寄存器访问。烧录时在 STC-ISP 里设 EEPROM 大小代码里就能按绝对地址读写。sfr IAP_DATA 0xC2; sfr IAP_ADDRH 0xC3; sfr IAP_ADDRL 0xC4; sfr IAP_CMD 0xC5; sfr IAP_TRIG 0xC6; sfr IAP_CONTR 0xC7; unsigned char EE_Read(unsigned int addr) { unsigned char dat; IAP_CONTR 0x80; // EN_IAP 置 1允许访问 IAP_ADDRH (unsigned char)(addr 8); IAP_ADDRL (unsigned char)addr; IAP_CMD 0x01; // 读命令 IAP_TRIG 0x5A; // 触发序列顺序不能反 IAP_TRIG 0xA5; _nop_(); dat IAP_DATA; IAP_CONTR 0x00; // 用完关掉防误写 Flash return dat; }写入前必须先整扇区擦除STC89 的扇区是 512 字节擦除命令是IAP_CMD 0x03。所以保存策略是把 TH、TL 两个 int 存在同一扇区的第 0、2 字节上电时读出来先判断是否在合法范围100~1250 之间不在就回退默认值。这个合法性判断必须做——新片第一次上电 EEPROM 里全是 0xFF直接拿去当阈值会让系统默认进入报警状态。每次按键设完阈值调用一次“擦扇区 写两字节”别在运行循环里反复擦写Flash 有寿命。5.2 上电验证顺序从存在脉冲到断电保存按这个顺序调能定位 90% 的问题。第一步只跑DS18B20_Reset()返回 0 再看时序返回 1 先查供电。第二步校温度冰水混合物应是 0°C手心里握住应是 30°C 左右不要用打火机直接烧传感器金属壳超过 125°C 会超出芯片量程。第三步把 TH 设在当前温度附近 0.5°C 以内观察进入报警和退出报警的两个边界点是否差 2°C。第四步改阈值后断电重启确认 LCD 显示的是新保存的值。最后一个容易被忽视的坑写在编译烧录前如果你烧录后第一次上电显示 85.0°C先查 0x44 后面的等待条件很多人把while (DQ 0)写成了while (DQ 1)——转换期间 DQ 一直被拉低等低电平的逻辑会直接跳过等待读回来的自然是复位默认值。本文还有配套的精品资源点击获取