STC15+DS18B20 Proteus仿真温度采集与串口输出详解

发布时间:2026/9/2 2:52:27
STC15+DS18B20 Proteus仿真温度采集与串口输出详解 简介这是一套基于STC15W4K32S4单片机读取DS18B20温度并通过串口发送的完整工程面向单片机入门与进阶学习者适合在Proteus仿真与Keil开发环境中对照学习。压缩包共37个文件、约233KB包含 main.c、ds18b20.c/h、uart.c/h 等C源码Proteus仿真工程pdsprj/pdsbak与Keil工程uvproj以及编译生成的hex烧录文件其中obj/lst为编译中间文件bat为辅助脚本文件类型覆盖从源码到仿真、烧录的完整链路。已有3201人学习。通过本工程可掌握STC15系列单总线时序、DS18B20读写流程信号线接P3.6口、串口1波特率配置与数据发送理解UART协议在无时钟同步下的通信方式还能在Proteus中实时观察温度变化与串口输出学习软硬件联调的思路直接用于课程设计、毕业设计或项目原型验证。 做单片机开发的人应该都懂光看数据手册和理论教程永远学不会真正的时序控制。DS18B20这颗温度传感器单总线协议看着简单但实际写驱动、调时序、验证数据每一步都是坑。我这次用STC15W4K32S4在proteus仿真里完整走了一遍“读取DS18B20温度 → 串口输出”的流程配合keil写源码、仿真调参、虚拟串口查数据踩了不少坑也算积累了不少经验。这篇就把整个项目从方案设计到代码实现、仿真搭建、问题排查全部拆开来讲适合正在学51单片机、准备做温度采集类课设或想入门proteus仿真校验的同学直接参考。1. 项目概述与整体方案设计1.1 为什么选STC15W4K32S4 DS18B20这个组合先说选型逻辑。STC15W4K32S4属于STC15系列的增强型8051内核单片机1T工作模式主频可以跑到30MHz左右实际手册上STC15W4K32S4最高支持24MHz~30MHz范围仿真和实体芯片有一些差异SRAM有4KFlash 32K带两路串口和丰富的外设。相比传统STC89C52它在Proteus仿真里的优势很明显内部时钟源可选、不需要外部晶振还能直接使用定时器2做串口波特率发生器让代码更贴近现代8051的开发习惯。DS18B20这颗传感器估计没人不知道一线总线通信两根线搞定供电和数据传输测温范围-55℃到125℃12位分辨率下的精度虽然标称±0.5℃但实际看使用环境会有一些偏差。选它的原因无外乎三点一是单总线接口省IO口二是数据直接是数字量不用ADC三是Proteus里有成熟的仿真模型适合和单片机配合做完整的系统验证。这个组合放在一起最典型的使用场景就是温度监控终端单片机周期性读取DS18B20的温度值经过格式化处理后通过串口把数据发到上位机或者串口调试助手。在Proteus里做一套这样的仿真不需要硬件焊接成本低、调试方便特别适合课设和入门级项目。1.2 整体数据流与系统架构整个系统的数据流向不复杂画出来其实就是三个环节DS18B20把环境温度转换为16位二进制温度值通过单总线DQ线传给单片机STC15W4K32S4使用GPIO模拟单总线时序读出原始温度值后做符号判断和温度换算单片机把换算后的温度值拼成ASCII字符串通过UART串口TXD引脚发送。Proteus仿真中温度的变化可以通过修改DS18B20模型的温度值来模拟仿真模型上点击可以调整串口侧我用虚拟终端Virtual Terminal直接看输出也可以用COM物理接口模块连接电脑串口调试助手看你需要哪种验证方式。电源方面STC15W4K32S4每个电源引脚都要接上VCC和GNDProteus仿真环境必须明确标出电源网络否则仿真会报错这个细节后面细说。2. DS18B20温度读取的核心逻辑时序与驱动代码2.1 单总线协议的基础原理DS18B20和单片机之间只有一根数据线DQ这意味着通信双方必须严格约定时序称为单总线协议。所有通信都从单片机发出的复位脉冲开始主机拉低总线至少480μs然后释放总线DS18B20会在15~60μs内拉低总线输出一个存在脉冲presence pulse告诉主机“我在线”。这一步做不好后面所有读写都会失败。存在脉冲之后主机执行ROM命令0xCC跳过ROM0x44启动温度转换转换需要一定时间12位分辨率下典型转换时间是750ms。转换完成后再发复位脉冲、跳过ROM、发送读暂存器命令0xBE就可以逐字节读出温度数据、校验数据等暂存器内容。这里有个容易踩的坑Proteus仿真模型对时序的宽容度比真实硬件要高一些但这不代表你可以随便写延时。如果你在一个实际STC15W4K32S4芯片上跑1T 8051的指令执行速度是传统12T的12倍同样的延时函数在89C52上跑得好好的换到STC15上可能时序就乱了。所以延时函数必须结合主频重新计算不能用以前的老代码直接搬运。2.2 初始化、读位、写字节的代码实现Keil C51工程里我先定义一个DS18B20操作的头文件用sbit声明DQ引脚连到单片机的P1口#ifndef DS18B20_H #define DS18B20_H #include STC15W4K32S4.h sbit DS18B20_DQ P1^0; unsigned char DS18B20_Init(void); void DS18B20_WriteByte(unsigned char dat); unsigned char DS18B20_ReadByte(void); float DS18B20_ReadTemper(void); #endif初始化函数是单总线通信的“握手信号”也是排查问题的第一步。标准的代码框架如下unsigned char DS18B20_Init(void) { unsigned char presence 0; DS18B20_DQ 0; Delay_OneWire_Us(500); // 拉低480μs以上 DS18B20_DQ 1; // 释放总线 Delay_OneWire_Us(70); // 等待DS18B20拉低总线 presence DS18B20_DQ; // 读取存在脉冲 Delay_OneWire_Us(410); // 剩余时序补全 return presence; }注意最后返回的是DQ的电平值。如果为0说明DS18B20正确响应了存在脉冲如果是1则说明总线上没有器件。Proteus仿真中DS18B20引脚接错、没有接上拉电阻或者电源没接好都会导致存在脉冲异常这个返回值就是第一道排查窗口。写字节和读字节的时序要牢记每个bit的读写窗口都是60μs位与位之间至少要有1μs的恢复时间void DS18B20_WriteByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DS18B20_DQ 0; Delay_OneWire_Us(10); DS18B20_DQ dat 0x01; Delay_OneWire_Us(50); DS18B20_DQ 1; dat 1; } } unsigned char DS18B20_ReadByte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; DS18B20_DQ 0; Delay_OneWire_Us(5); DS18B20_DQ 1; Delay_OneWire_Us(8); if (DS18B20_DQ) dat | 0x80; Delay_OneWire_Us(50); } return dat; }这段代码看着简单但里面“写1”和“读位采样”的延时区间需要特别小心。写1时的低电平时间不能超过15μs否则DS18B20会把这个bit当成写0读位采样则必须在拉低总线后的15μs内完成太晚了器件已经释放总线读到的数据就不准了。我当时调这个采样点延时从3μs到15μs挨个试了一遍最稳定的区间其实集中在7~10μs。2.3 温度计算与负温度处理DS18B20返回的温度原始值是16位有符号数分辨率与配置的转换精度有关。默认12位精度下LSB代表0.0625℃。换算公式很简单float DS18B20_ReadTemper(void) { unsigned char low, high; int raw_temp; float temperature; if (DS18B20_Init() ! 0) // 存在脉冲异常处理 return -999.0; DS18B20_WriteByte(0xCC); // 跳过ROM DS18B20_WriteByte(0x44); // 启动温度转换 Delay_OneWire_Ms(750); // 等待转换完成 DS18B20_Init(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读暂存器 low DS18B20_ReadByte(); high DS18B20_ReadByte(); raw_temp (high 8) | low; if (raw_temp 0x8000) // 负温度处理 raw_temp raw_temp - 0x10000; temperature raw_temp * 0.0625; return temperature; }这个函数返回的-999.0是一个明显不合理的温度值用来给主函数做错误判断。实际编码时我建议即使只做正温度范围也务必处理符号位因为DS18B20在-0.5℃时会输出0xFFF8这样的数据不处理的话换算后会变成一个很大的正数串口输出就会看到异常跳变。另外Delay_OneWire_Ms(750)在高主频下要注意不要用普通的循环延时代替最好用定时器或者精确的延时函数否则整个读取周期会偏差比较大。3. STC15串口通信与Keil代码实现3.1 串口初始化的关键参数计算STC15W4K32S4的串口1推荐使用定时器2做波特率发生器因为这样定时器1可以留作他用而且设置清晰。我用的系统时钟是12MHz目标波特率是9600bps。STC15系列1T模式下波特率计算公式为波特率 系统时钟 / 4 / (65536 - 重载值)推导一下重载值65536 - 重载值 12000000 / 4 / 9600 312.5取整后重载值约65536 - 312 65224对应十六进制0xFEC8。如果时钟是11.0592MHz重载值就会是65536 - 288 65248所以换主频后重载值必须重新算。实际经验是Proteus仿真中对波特率误差不太敏感但接真实串口设备时要尽量用整数分频或误差小于2%的配置。我的初始化代码如下void UART1_Init(void) { SCON 0x50; // 模式18位UART允许接收 AUXR | 0x04; // T2为波特率发生器 AUXR | 0x01; // T2运行在1T模式 T2L 0xC8; // 12MHz - 9600bps T2H 0xFE; ES 1; EA 1; }3.2 温度数据的格式化与输出逻辑串口发送部分我写了两个函数一个是单字节发送一个是字符串发送。温度数值是float类型直接发二进制不方便上位机解析所以我用sprintf把它转成ASCII字符串再发送这样串口调试助手直接就能看到可读内容void UART1_SendByte(unsigned char dat) { SBUF dat; while (!TI); TI 0; } void UART1_SendString(char *str) { while (*str) { UART1_SendByte(*str); } }主循环中考虑到DS18B20的转换时间比较长我用了一个500ms的周期标志位来控制采样频率避免串口数据刷新太快导致刷屏。实际输出的字符串格式类似Temp 26.31 C如果只发这一句串口助手看到的是一行一行的温度数据。如果加上时间戳或加入固定帧头比如“TEMP:26.31”后续做上位机解析会更容易这个可以根据自己的需求扩展。3.3 主循环、错误处理与防卡死主函数的逻辑结构很简单但我在错误处理上花了点心思void main(void) { float temperature; UART1_Init(); UART1_SendString(System Init OK\r\n); while (1) { temperature DS18B20_ReadTemper(); if (temperature -999.0) { UART1_SendString(DS18B20 Error!\r\n); } else { char buf[20]; sprintf(buf, Temp %.2f C\r\n, temperature); UART1_SendString(buf); } Delay_OneWire_Ms(500); } }这里有个我踩过的坑DS18B20_ReadTemper内部已经包含了750ms转换等待主循环再延时500ms意味着整个温度刷新周期是1.25秒左右。对温度监控这种慢变系统来说完全够用但如果以后改为读取湿度传感器或者其他需要更高频率的传感器就不要再额外加延时用定时器做分频逻辑更合理。另外STC15W4K32S4的IO口默认是高阻态还是准双向口不同型号和不同烧录配置有区别。我在代码里没显式配置P1M0/P1M1默认准双向口状态可以正常工作但如果你在Proteus仿真的时候发现DQ引脚电平异常可以先检查一下IO模式寄存器配置尤其是在复制别人工程的时候最容易漏掉这一步。4. Proteus仿真环境搭建与联调4.1 Proteus电路连接与关键元件参数Proteus里新建工程后从元件库添加STM32不对是添加STC15W4K32S4单片机、DS18B20温度传感器、上拉电阻、虚拟串口终端和电源端子。画电路的时候有几个点必须注意单片机的VCC引脚要全部接到5VGND引脚全部接地。STC15W4K32S4可能有多个电源引脚Proteus仿真中漏接一个电源脚程序运行会静默失败很难排查DS18B20的第2脚DQ要接一个4.7kΩ的上拉电阻到VCC。这个电阻是实际硬件中必需的仿真中如果不接单总线时序依然可能因为模型原因“碰巧能跑通”但你要是拿着这套图去打板就废了串口TXD引脚连接到虚拟终端的RXD单片机的RXD连接到虚拟终端的TXD两个设备的交叉连接不能搞反。关于STC15W4K32S4在Proteus里的型号新版本Proteus 8.x的库里有STC15系列模型我用的版本能直接找到STC15W4K32S4。如果找不到可以找同系列封装接近的型号替代但此时必须仔细核对引脚定义尤其是串口引脚的编号否则仿真根本跑不起来。4.2 Keil工程配置与Hex文件加载Keil里新建C51工程单片机型号一栏如果找不到STC15W4K32S4一般使用STC MCU Database里的STC15W4K32S4选项或者接近的STC15系列型号。编译前需要做两件事在Options for Target里把晶振频率调整成12MHz和proteus里的单片机属性保持一致勾选Create HEX File这样编译后才会生成Hex文件。Proteus中双击单片机元件在Program File里加载Keil生成的Hex文件。有一点容易被忽略加载完Hex文件后需要把单片机元件的Clock Frequency同样设置为12MHz。很多人在Proteus里忘了改这个参数导致仿真时序和代码里延时函数假设的主频不一致串口波特率全乱套。4.3 虚拟终端与串口助手的连接方式Proteus里查看串口输出最直接的办法是放一个Virtual Terminal虚拟终端然后连到单片机的串口引脚上。启动仿真后虚拟终端会显示单片机发来的ASCII字符串这就是最直观的验证。如果想和真实的串口调试助手联动可以用Proteus的COMPIM组件把它配置成物理串口模式。但说实话在纯仿真阶段虚拟终端已经够用COMPIM还需要额外的虚拟串口软件配合设置繁琐且容易出诡异问题初学阶段不建议折腾。虚拟终端默认显示的是ASCII字符波特率要设置成和单片机一致我这里是9600否则屏幕上全是乱码。另外虚拟终端支持在仿真中右键“Edit Properties”调节字体和缓冲区大小调试大流量数据时缓冲区可以适当加大。5. 常见问题与排查技巧实录5.1 串口输出乱码或没有输出这是整个项目里最常碰到的问题我把它排第一位。出现乱码80%以上是波特率不匹配单片机实际波特率和虚拟终端波特率不一致。注意Proteus中单片机的主频设置、Keil里调用延时函数时假设的主频、波特率寄存器计算用的主频这三者必须完全一致。我在调试中就出现过Keil工程设置的12MHz、Proteus里单片机却保持默认1MHz的情况结果是串口数据完全不可读。如果完全没有输出先用电压探针或者逻辑探针看TXD引脚有没有电平变化。没变化说明程序可能根本没跑到串口发送部分这时候先查主循环是否死循环在DS18B20初始化里最简单的方法是在串口发送函数前加一个LED翻转程序跑没跑、跑到哪里一眼就能看出来。5.2 DS18B20初始化失败或温度恒为85℃DS18B20初次上电后如果不发转换命令直接读暂存器读到的可能是85℃这个上电默认值。这个问题在proteus仿真里也常出现原因是读时序的采样点太早或太晚导致读到的字节全是0xFF最终算出来一个离谱的温度。初始化失败则要检查复位时序低电平时间有没有满足480μs以上释放后等待存在脉冲的时间窗口是否在60μs以内。仿真环境对时间精度要求低于真实硬件但如果延时函数里用了错误的循环模型比如用传统12T的延时思路写给1T单片机时序偏差会大得离谱。我这里提供一个排查表格方便你对照检查现象可能原因排查方向串口完全无数据单片机没运行检查电源、Hex加载、复位引脚串口乱码波特率不一致核对主频、T2重载值、终端波特率温度恒为85℃没发温度转换命令检查是否发送0x44并等待750ms温度读数跳变异常负温度未处理检查符号位扩展逻辑DS18B20返回错误上拉电阻缺失或时序不对检查4.7k上拉、复位脉冲宽度5.3 Proteus仿真与真实硬件之间的差异说实话Proteus仿真DS18B20确实能跑通但和真实硬件还是有几个明显区别需要心里有数仿真模型不涉及实际电气噪声所以抗干扰问题在仿真中完全暴露不了DS18B20模型的时序容差比较宽有些在仿真里能正常执行的代码放到真实芯片上可能因为延时精度不够而失败Proteus里的DS18B20温度值是通过模型属性手动改的不会像真实环境那样连续平滑变化验证逻辑可以验证传感器精度就不行了。所以我的一般做法是先用Proteus把整体逻辑跑通然后再把代码烧录到STC15W4K32S4真实芯片上做连线验证。仿真阶段的价值在于快速验证协议理解和代码框架真机阶段的价值在于验证时序和稳定性两者各司其职。5.4 一个容易被忽略的Proteus电源设置问题STC15W4K32S4在Proteus里不止一个VCC和GND引脚如果你在原理图上用的是默认电源网络一定要在Design菜单里设置正确。我见过不少人仿真跑不起来最后发现是单片机电源网络悬空这种问题排查起来非常隐蔽。主流做法是在电路里放置一个POWER端子命名为VCC接地端命名为GND所有电源网络都统一用这两个名字。DS18B20的VDD、上拉电阻的上端、单片机的VCC引脚全部连到VCC网络这样就不会出现“某个引脚没接电”的情况。最后说两句这个项目做下来我最深的体会是DS18B20的时序代码不是背下来就完事了一定得自己动手调一遍尤其是延时参数和主频的关系。很多初学者照着网上的51代码改一改放到STC15上跑发现温度读不出来就怀疑芯片坏了其实大概率是主频不一致导致时序计算全部偏离。如果你打算扩展到多路温度采集、OLED显示、PID控温这类更复杂的应用这一套“Proteus仿真 Keil工程 串口验证”的调试模式还能继续复用。建议先把单个DS18B20的时序摸透再往上叠功能不然出问题的时候你会分不清是时序的锅还是新增逻辑的锅。本文还有配套的精品资源点击获取