拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32+Proteus仿真:DS18B20温度采集与串口LCD1602双路显示

简介这套基于STM32F103R6的DS18B20温度读取与显示仿真资源面向物联网、嵌入式方向初学者及单片机课程设计人群。工程在Proteus中完成硬件仿真涵盖了DS18B20单总线驱动、UART串口发送、LCD1602实时显示以及阈值报警联动当温度超过设定值时串口会向PC机发送当前温度蜂鸣器同时报警LCD1602持续刷新显示。整体项目既能用于验证温度采集逻辑也可作为综合嵌入式实验的参考模板。资源包共包含993个文件主要类型有C语言源码、头文件、启动汇编文件和链接脚本同时带Proteus工程及STM32编译配置产物便于直接打开分析。压缩包大小12.18MB结构清晰适合用VSPD虚拟串口配合调试助手观察数据。已有2525人学习下载建议具备基础C语言和STM32入门知识的学习者使用。1. 一次仿真把温度采集链路完整走通这个标题把嵌入式开发里最典型的几条线串在了一起单总线协议解析、GPIO模拟时序、USART异步通信以及字符型液晶的并行驱动。单独拆开每一项都有现成例程但合到一个基于 STM32F103R6 的工程里还要在 Proteus 里闭环跑起来涉及的问题就远不止“抄代码”这么简单——比如 DS18B20 的 60us 读写时隙在仿真环境里是否仍能成立、Proteus 的虚拟串口 COMPIM 怎么跟真实串口工具对接、LCD1602 的上电时序会不会把首屏刷花这些都是只玩开发板时遇不到的。本文不假设你已经有完整工程而是从时序源头开始把 51 单片机时代积累的经验迁移到 STM32 上再落进 Proteus 仿真的实际约束里最终拿到一份能在虚拟环境里稳定跑、也能平移到真板的温度读取与双路显示方案。适合正在做课程设计、毕业设计或者想快速验证传感器驱动逻辑的嵌入式开发者。2. 先从 DS18B20 时序说起STM32F103R6 为什么能用 GPIO 模拟单总线DS18B20 只有一根数据线供电、复位、读写全在这根线上完成。所谓单总线协议本质上是一组严格的时间窗口主机拉低总线产生复位脉冲从机以存在脉冲应答随后每一位的传输都是由主机产生下降沿作为起始再从这一位是 0 还是 1 来决定总线被拉低的时间长短。这套机制对时序的要求是微秒级的而对 STM32F103R6 这种主频 72MHz 的 Cortex-M3 来说用 GPIO 翻转加空循环延时来模拟完全可行因为一个 NOP 大约 13.9ns最紧张的读时隙要求主机在下降沿后 15us 内完成采样折算成指令周期还有上千条的余量。2.1 单总线协议的关键时间参数与 Proteus 仿真的适配问题先看官方数据手册里必须记牢的几个时间窗口它们是后面所有延时函数的设计依据参数说明典型值最大值复位脉冲主机拉低总线480us960us存在脉冲从机应答拉低60us240us写 0 时隙主机持续拉低60us120us写 1 时隙主机拉低后释放1us 拉低其余高60us 完成读时隙采样点主机在下降沿后采样15us 内未定义Proteus 里仿真 DS18B20 时有个特殊现象它的时序模型比真实芯片更“宽容”某些在真板上会失败的时序在仿真里反而能通过而反过来仿真里延时不达标也会读到 85℃ 的默认值或 CRC 错误。所以我建议以真板时序为准来写延时函数不要为了过仿真而刻意放宽参数。2.2 用 STM32F103R6 的 GPIO 模拟单总线的最小代码骨架DS18B20 的数据引脚需要开漏输出因为单总线要求主机释放总线时为高阻态由外部的 4.7kΩ 上拉电阻把电平拉高。STM32 的 GPIO 配置中采用开漏输出模式读数据前要把引脚切到输入模式。下面是初始化部分#define DS18B20_PIN GPIO_Pin_1 #define DS18B20_PORT GPIOB #define DS18B20_CLK RCC_APB2Periph_GPIOB static void DS18B20_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(DS18B20_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin DS18B20_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(DS18B20_PORT, GPIO_InitStructure); GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); }这里先配置成推挽输出并拉高是为了保证复位脉冲前总线处于确定的高电平状态。每次操作前调用这个函数操作完成后也要把引脚恢复为高电平。2.3 微秒级延时函数SysTick 还是空循环延时函数是整套时序的核心。我一般用 SysTick 做 1us 基准延时它不占用定时器外设而且可重入性比空循环好调static void delay_us(uint32_t nus) { uint32_t temp; SysTick-LOAD nus * 72 - 1; SysTick-VAL 0; SysTick-CTRL 0x01; do { temp SysTick-CTRL; } while ((temp 0x01) !(temp (1 16))); SysTick-CTRL 0x00; SysTick-VAL 0; }SysTick-LOAD 赋值为 72 的倍数时要注意 nus 取 0 时会进入死循环调用前确保参数大于 0。这个延时函数在 Proteus 仿真里同样有效因为 Proteus 对 Cortex-M 内核的指令周期模拟是准确的。2.4 复位与读写的完整时序实现以下代码是 DS18B20 驱动的最核心部分三个函数分别对应复位、写一个字节、读一个字节uint8_t DS18B20_Reset(void) { uint8_t presence; GPIO_ResetBits(DS18B20_PORT, DS18B20_PIN); delay_us(480); GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); delay_us(60); GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(DS18B20_PORT, GPIO_InitStructure); presence GPIO_ReadInputDataBit(DS18B20_PORT, DS18B20_PIN); delay_us(420); GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(DS18B20_PORT, GPIO_InitStructure); GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); return presence; } void DS18B20_WriteByte(uint8_t data) { for (uint8_t i 0; i 8; i) { GPIO_ResetBits(DS18B20_PORT, DS18B20_PIN); delay_us(2); if (data 0x01) { GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); } delay_us(60); GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); data 1; } } uint8_t DS18B20_ReadByte(void) { uint8_t data 0; for (uint8_t i 0; i 8; i) { data 1; GPIO_ResetBits(DS18B20_PORT, DS18B20_PIN); delay_us(2); GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(DS18B20_PORT, GPIO_InitStructure); if (GPIO_ReadInputDataBit(DS18B20_PORT, DS18B20_PIN)) { data | 0x80; } delay_us(60); GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(DS18B20_PORT, GPIO_InitStructure); } return data; }注意读字节函数里把引脚切回输出模式这个操作要放在延时之后、下一次循环开始之前完成否则下一次写时序的起始下降沿会被输入模式下的高阻态干扰。这一行位置错了读出来的数据会整体错位。提示完整的数据转换流程是先发 0xCC跳过 ROM 匹配再发 0x44启动温度转换等待 750ms 后再次复位并发送 0xCC 与 0xBE读取暂存器连续读 9 个字节前两个是温度值的低字节和高字节。3. Proteus 里搭工程从元件选型到 COMPIM 虚拟串口的完整接线Proteus 仿真 STM32 需要在原理图中放置 STM32F103R6、DS18B20、LCD1602、虚拟串口 COMPIM、上拉电阻和电源网络。很多人卡在第一步元件库里搜不到 STM32F103R6因为 Proteus 的模型命名可能带后缀搜“STM32F103R6”或直接输入“STM32F103”能找到对应系列。3.1 Proteus 元件对照与最小电路连接要点元件Proteus 关键字关键连接主控STM32F103R6PD0-OSC_IN、PD1-OSC_OUT 接 8MHz 晶振与两个 20pF 电容温度传感器DS18B20DQ 接 PB1VDD 接 5VGND 接地DQ 与 VDD 间接 4.7kΩ 上拉显示屏LM016L / LCD1602D0-D7 接 PA0-PA7RS 接 PB10RW 接 PB11E 接 PB12虚拟串口COMPIMRXD 接 PA9TXD 接 PA10物理串口选择实际端口电源网络要特别注意Proteus 里 STM32F103R6 的 VDD 和 VSS 引脚默认隐藏必须在 Design 菜单里打开 Power Rail Configuration把 VCC 设为 5V、VDD 设为 3.3V否则仿真会因为供电冲突直接报错。晶振旁边的两个电容在仿真中可省略但对理解真实硬件有帮助。3.2 虚拟串口 COMPIM 的配置方法COMPIM 是 Proteus 与宿主机串口通信的桥梁它把虚拟串口映射到 Windows 上的物理或虚拟 COM 口。常见做法是用虚拟串口软件比如 VSPD成对创建 COM3 和 COM4一端给 Proteus 用另一端接串口调试助手。COMPIM 的属性设置如下Physical Port选择 COM3必须是空闲的虚拟串口Physical Baud Rate9600与 STM32 的 USART 初始化一致Virtual Baud Rate同样设为 9600Data Bits8ParityNONEStop Bits1Flow ControlNONE这里最容易踩的坑是 Physical Baud Rate 和 Virtual Baud Rate 必须一致Proteus 不会自动做波特率转换。COM 口被占用时仿真一开始就会报“unable to open port”需要在设备管理器里先把可能占用串口的程序退出。3.3 USART1 初始化与 printf 重定向USART1 的收发引脚默认映射到 PA9(TX) 和 PA10(RX)在 Proteus 里不需要配置重映射直接用即可。初始化代码片段void USART1_Init(uint32_t baudrate) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); USART_InitStructure.USART_BaudRate baudrate; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, USART_InitStructure); USART_Cmd(USART1, ENABLE); }注意 TX 引脚要配置成复用推挽 GPIO_Mode_AF_PPRX 引脚配置成浮空输入。如果配置成 GPIO_Mode_Out_PP在 Proteus 里仿真时串口可能也能工作但真板上会有电平不确定的风险。初始化完成后配合下面的重映射就能直接使用 printf 打印日志int fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t)ch); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); return ch; }这里要包含 stdio.h并在 Keil 的 Target 选项里勾选 Use MicroLIB否则链接器会报 __stdout 未定义。4. LCD1602 并行驱动与温度数据的双路实时显示LCD1602 是标准的 HD44780 字符型液晶8 位并行模式下需要一根使能线、一根寄存器选择线和一根读写选择线再加上 8 根数据线。STM32F103R6 的 GPIO 数量完全够用不需要用 4 位模式节省引脚。我在这套方案里选了 8 位模式因为 Proteus 仿真下 8 位模式的时序更直观排查问题也方便。4.1 LCD1602 驱动代码初始化、清屏、写命令与写数据LCD1602 的初始化时序比多数人想的更讲究上电后要先等 15ms 以上再连续写三次 0x30 进入 8 位模式最后设置显示参数。以下是标准的初始化序列void LCD1602_Init(void) { delay_ms(20); LCD1602_WriteCmd(0x30); delay_ms(5); LCD1602_WriteCmd(0x30); delay_us(100); LCD1602_WriteCmd(0x30); LCD1602_WriteCmd(0x38); // 8位模式2行5x7点阵 LCD1602_WriteCmd(0x08); // 显示关闭 LCD1602_WriteCmd(0x01); // 清屏 delay_ms(2); LCD1602_WriteCmd(0x06); // 光标右移禁止整屏移动 LCD1602_WriteCmd(0x0C); // 显示开光标关闪烁关 }0x38 这个命令在不同教程里有细微差别有的写成 0x3C那是在开启 5x10 点阵对西文字符显示没有实际影响。写命令函数的核心逻辑是先拉低 RS 表示写命令拉低 RW 表示写操作然后把数据送到数据线上最后产生一个高电平脉冲作为使能信号。每次使能脉冲的宽度建议不小于 1us因为 HD44780 的数据建立时间要求是 40ns 级别但 Proteus 模型对这个参数比较敏感太短会导致写入失败。void LCD1602_WriteCmd(uint8_t cmd) { GPIO_ResetBits(GPIOB, RS_PIN | RW_PIN); GPIO_Write(GPIOA, cmd); GPIO_SetBits(GPIOB, EN_PIN); delay_us(2); GPIO_ResetBits(GPIOB, EN_PIN); delay_ms(2); }注意 GPIO_Write 是一次性把低 8 位写到 PA0-PA7要求在画原理图时数据线 D0-D7 必须按顺序接到 PA0-PA7不能交叉。如果为了布线方便把 D3 接到 PA5那代码里就需要手动按位拼装。4.2 温度数据转换从原始值到可读的浮点温度DS18B20 的温度寄存器是 12 位带符号数低 4 位是小数部分。转换时先判断符号位再对数值右移 4 位得到整数部分小数部分乘以 0.0625。为了在 LCD 和串口上都能直观显示我习惯把温度乘以 10 作为整数处理避免浮点库带来的代码体积膨胀int16_t DS18B20_GetTemp(void) { uint8_t tempL, tempH; int16_t raw, temp; DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0x44); delay_ms(750); DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); tempL DS18B20_ReadByte(); tempH DS18B20_ReadByte(); raw (tempH 8) | tempL; if (raw 0x8000) { raw ~raw 1; temp raw * 10 / 16; return -temp; } temp raw * 10 / 16; return temp; }这里返回值的单位是 0.1℃比如返回 235 表示 23.5℃。在串口端打印时分离整数和小数部分只需要两步temp / 10 和 temp % 10不需要 any 浮点格式化函数。4.3 主循环里同时刷新串口和屏幕主循环的逻辑很直接读温度、计算、分别刷 LCD 和串口。LCD1602 的第一行显示固定标题第二行显示当前温度int main(void) { int16_t temp; char buf[16]; SystemInit(); LCD1602_Init(); USART1_Init(9600); LCD1602_WriteString(0, 0, Temp:); while (1) { temp DS18B20_GetTemp(); sprintf(buf, %d.%d C , temp / 10, temp % 10); LCD1602_WriteString(0, 1, buf); printf(Temperature: %d.%d C\r\n, temp / 10, temp % 10); delay_ms(1000); } }sprintf 的缓冲区要足够大LCD1602 第二行一共 16 个字符buf 至少留 17 字节。当温度为负数时temp % 10 的结果可能是负数要取绝对值。这里的刷新周期我设为 1 秒因为 DS18B20 默认的 12 位转换时间就是 750ms再短没有意义。提示串口打印用 \r\n 而不用 \n在串口助手里显示会更整齐。如果收到的数据是乱码优先检查 USART 的波特率初始化以及 COMPIM 的 Physical Baud Rate 是否一致这两个地方是仿真串口乱码的绝大多数原因。5. 用虚拟终端验证整条链路与三个必调的仿真参数完成以上步骤后在 Proteus 里放置一个 VIRTUAL TERMINAL 元件把它的 RXD 引脚接到 STM32 的 PA9TXD 引脚GND 接好就能在仿真运行时直接看到串口输出的温度数据。这比接 COMPIM 更方便因为它不需要真实串口资源适合验证阶段使用等验证通过后再把 VIRTUAL TERMINAL 替换成 COMPIM接真实的串口调试助手。5.1 仿真必调的三个参数第一个是 DS18B20 的上拉电阻。在 Proteus 里如果直接用导线连接 DQ 而不放电阻仿真可能会通过但读回来的高电平驱动能力不足表现为温度值偶尔跳变或读取失败。务必放一个 4.7kΩ 的电阻从 DQ 接到 VCC这是单总线正常工作的前提。第二个是晶振配置。STM32F103R6 在 Proteus 里默认使用内部 RC 振荡器但 USART 的波特率要以外部晶振为基准计算。在工程配置里明确选择 8MHz 外部晶振并把 Keil 工程的 Xtal 值也设为 8.0MHz否则串口波特率会偏差到无法通信。第三个是 LCD1602 的使能脉冲宽度。如果屏幕显示花屏或首行全亮先把使能脉冲的延时从 2us 调到 5us如果这样仍然不行检查数据引脚有没有接反以及 RW 引脚有没有被意外拉高。5.2 用逻辑分析仪排查时序问题的具体方法Proteus 的 Logic Analyzer 是排查单总线时序问题的利器。把 DQ 引脚接进逻辑分析仪设置采样率为 1MHz 以上运行仿真后就能看到完整的时序波形。正常情况下复位脉冲应该是一段 480us 左右的低电平随后有一个约 60us 的应答脉冲。如果看不到应答说明 DS18B20 没有检测到有效的复位信号原因可能是复位脉冲时间不足、上拉电阻缺失或者 DQ 引脚接错位置。写时序的波形是多个宽度约 60us 的低电平片段读时序的波形会有明显的“释放总线”过程。用光标测量每个片段的宽度与第 2 节的参数表对照能快速定位是哪个环节的延时函数出了问题。这套方法在真板上也同样适用逻辑分析仪实际测量到的信号就是代码时序的最终检验。5.3 最后一段代码把温度数据做成带小数和符号的完整输出#include stdio.h void PrintTemperature(int16_t temp_raw) { if (temp_raw 0) { printf(-); temp_raw -temp_raw; } printf(%d.%d C\r\n, temp_raw / 10, temp_raw % 10); }给 PrintTemperature 传参时把 DS18B20_GetTemp 的返回值直接传进去。这一个函数同时服务于串口输出在 LCD 显示时则替换成对应的写入函数。至此这个基于 STM32F103R6 的 DS18B20 温度读取、UART 与 LCD1602 双路实时显示的完整方案在 Proteus 中就能稳定运行代码也具备直接迁移到真实硬件的基础。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门