ARTICLE DETAIL

建站实战干货

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

基于STM32与Proteus的超声波测距测温仿真系统设计与实现

2026/9/3 8:42:31 拓冰建站 浏览量
基于STM32与Proteus的超声波测距测温仿真系统设计与实现 简介本资源是一套面向嵌入式初学者与STM32进阶学习者的完整仿真项目实践包聚焦超声波测距与数字温度测量双功能融合开发解决传感器驱动、多外设协同及LCD实时显示等典型嵌入式系统工程问题。资源共244个文件涵盖65个C语言源码.c与55个头文件.h支撑STM32 HAL库底层驱动与应用逻辑含24个编译中间文件.o/.d、11个调试配置.dbgconf及2个Proteus仿真工程.pdsprj完整复现从代码编写、Keil编译到Proteus联调的全流程另附操作截图.jpg、原理说明.txt及可直接烧录的hex/axf镜像文件。压缩包大小为17.81MB结构清晰、模块解耦便于理解定时器测距算法、DS18B20单总线通信协议及1602 LCD字符显示控制等核心知识点。目前已有900人学习下载配套内容覆盖全部关键函数实现与仿真验证结果是掌握STM32传感器应用开发的高实用性入门范例。1. 项目缘起从零到一构建一个看得见的测距测温系统最近在整理一些嵌入式教学和项目验证的素材发现很多朋友在入门STM32和传感器时常常会遇到一个困境硬件还没到手或者焊接调试过程中总出幺蛾子代码逻辑对不对心里完全没底。特别是像超声波测距、温度采集这类需要硬件实时交互的应用纯靠脑补或者串口打印几个数字总感觉少了点直观性。这时候仿真工具的价值就凸显出来了。Proteus作为一款经典的电子设计自动化软件其电路仿真和微控制器协同仿真的能力对于嵌入式学习者来说是个绝佳的“沙盒”。它允许你在电脑上搭建虚拟的电路加载编译好的单片机程序并观察整个系统的动态运行效果比如LED闪烁、屏幕显示、传感器数值变化等整个过程可视化了。所以我决定动手做一个“基于STM32的超声波测距测温Proteus仿真”项目。核心目标很明确在Proteus环境中用STM32F103C8T6蓝桥杯、正点原子最小系统板同款作为主控虚拟驱动HC-SR04超声波模块测量距离同时模拟一个温度传感器比如DS18B20或模拟PT100的电压信号进行温度采集并将结果实时显示在虚拟的LCD屏上。这个项目不依赖任何实体硬件却能完整呈现从传感器原理、STM32外设驱动定时器、GPIO、ADC、数据处理到人机交互的全链路非常适合用于前期方案验证、算法调试和教学演示。通过这个仿真你可以清晰地看到STM32如何发出一个10us以上的高电平触发信号HC-SR04如何回响一个与距离成正比的高电平脉冲STM32的输入捕获如何精准测量这个脉冲宽度并换算成厘米或毫米。对于测温部分你可以理解如何通过ADC读取模拟电压或者按照单总线协议读取数字温度值并进行滤波和校准。这一切都在你的屏幕前动态上演。2. 仿真核心HC-SR04超声波模块的工作原理与Proteus建模要让仿真跑起来首先得吃透HC-SR04这个经典超声波模块在真实世界和虚拟世界中的行为。2.1 物理世界中的HC-SR04时序就是一切HC-SR04模块有四个引脚VCC、Trig触发、Echo回响、GND。其工作流程是标准化的触发单片机向Trig引脚发送一个至少10微秒的高电平脉冲。发射与接收模块内部自动发出8个40kHz的超声波脉冲并开始检测回波。回响当模块接收到返回的回波后会在Echo引脚输出一个高电平脉冲。计算距离这个高电平脉冲的宽度正比于超声波从发射到接收所经过的时间。根据声速约340m/s即0.034cm/μs距离距离(cm) (高电平时间(μs) * 0.034) / 2。除以2是因为时间是往返的。这里的关键是时序。在STM32的程序中我们通常用一个GPIO如PA0连接Trig周期性例如100ms发出一个10-20us的脉冲。同时将Echo引脚如PA1配置为输入模式并启用输入捕获功能来精确测量Echo高电平的持续时间。2.2 Proteus世界中的HC-SR04用虚拟器件模拟物理行为Proteus元件库中没有现成的、行为完全仿真的HC-SR04模型。这是仿真项目第一个要解决的“坑”。我们不能直接用原理图符号必须找到替代方案来模拟其输入输出特性。我经过多次尝试找到了一个稳定可靠的方案使用一个“脉冲发生器”配合一个“电压控制脉冲宽度”器件来模拟Echo引脚的行为。具体建模思路如下Trig输入这很简单就是STM32的一个普通GPIO输出引脚连接到虚拟器件的输入脚。Echo输出模拟这是核心。我们需要一个器件当它检测到Trig的上升沿代表触发后能延迟一段时间代表超声波飞行时间然后产生一个高电平脉冲且脉冲宽度与“距离”参数成正比。实现方法在Proteus的“Generator Mode”工具中选择一个“PULSE”脉冲发生器。但我们需要的是受控的脉冲。更优雅的方法是使用“Voltage Controlled Pulse Width”模型在“Miscellaneous”类别里可能找到类似功能的器件或者用更基础的“Schmitt Trigger”和“Monostable”单稳态触发器组合搭建。简化方案对于教学和原理演示一个取巧但非常直观的方法是使用Proteus提供的“Signal Generator”直接模拟Echo信号。你可以手动设置一个固定宽度的方波来模拟固定距离的回波但这失去了交互性。高级方案为了实现动态距离仿真我采用了“模拟电压控制延迟”的思路。用一个可调电阻POT-HG的分压值来模拟“距离”参数。该电压输入到一个电压控制振荡器VCO或类似的延时电路其输出延迟时间正比于输入电压。然后用这个延迟去触发一个单稳态触发器产生一个固定宽度的Echo脉冲或者脉冲宽度也受电压控制模拟不同距离下回波强度的变化不HC-SR04的Echo脉宽只与时间有关与强度无关。最终这个Echo脉冲连接到STM32的输入捕获引脚。注意在Proteus中搭建这个模拟电路需要一些模拟电路基础。一个更简单的替代方案是直接使用Proteus VSM SDK或脚本功能创建一个自定义模型但这门槛较高。对于大多数学习者我建议采用“可调电阻模拟距离单片机程序内部根据ADC读取的电阻电压值来虚拟计算出一个Echo高电平时间并在代码层面模拟Echo引脚的变化”的软硬件协同仿真思路。不过这要求修改单片机代码失去了纯粹硬件仿真的意义。因此我最终选择用基本逻辑器件搭建了一个行为级模型虽然原理图稍复杂但一次搭建永久使用且行为完全贴合数据手册。最终我的Proteus图纸中HC-SR04部分是一个由Schmitt Trigger、Monostable Multivibrator和一些逻辑门组成的子电路它有两个外部接口Trig输入和Echo输出还有一个用于调节模拟距离的滑动变阻器输入。这样在仿真运行时我拖动滑动变阻器就能实时改变Echo脉冲的宽度STM32测量到的“距离”也随之变化非常直观。3. 温度测量模块的仿真选型DS18B20 vs. 模拟传感器测温部分的选择更多样。在真实项目中根据成本、精度、温度范围可以选择DS18B20单总线数字、PT100/PT1000模拟电阻、热电偶模拟电压等。在Proteus仿真中我们需要考虑的是模型的支持度和仿真便利性。3.1 数字温度传感器DS18B20协议仿真DS18B20是Proteus官方库直接支持的元件搜索“DS18B20”即可。它的优点是仿真模型成熟行为准确直接支持单总线协议。在仿真中你只需要将其数据脚连接到STM32的一个GPIO需配置为开漏输出模式并编写对应的单总线读写时序驱动程序即可。在仿真中使用DS18B20的注意事项上拉电阻和实物一样必须在数据线上接一个4.7kΩ左右的上拉电阻到VCC否则总线无法拉高。电源模式Proteus中的DS18B20模型支持寄生电源和外部电源模式。为了简单可靠建议使用外部电源模式VDD接3.3V或5V。时序精度STM32的单总线驱动代码对延时非常敏感。在仿真中由于计算机性能影响仿真速度可能不是实时的但Proteus会尽力保证时序逻辑的正确性。你的延时函数delay_us()需要相对准确。建议使用STM32的SysTick定时器或通用定时器来实现微秒级延时而不是简单的循环计数后者在仿真中可能不稳定。模型参数你可以双击DS18B20元件在其属性中设置初始温度值。在仿真运行过程中你也可以动态修改这个值来模拟温度变化测试你的代码响应。使用DS18B20仿真的好处是它完全复现了真实项目中最繁琐的部分——单总线协议调试。如果你的仿真能成功读取DS18B20的温度那么代码移植到实物上成功率极高。3.2 模拟温度传感器PT100与ADC采集仿真如果你希望练习STM32的ADC采集那么模拟传感器是个好选择。PT100是一种铂热电阻其电阻值随温度变化而变化近似线性。在Proteus中没有现成的PT100模型但我们可以用可变电阻POT-HG 或 POT-LIN来模拟。仿真电路搭建放置一个可变电阻如10kΩ POT将其一端接VCC一端接GND中间抽头连接到STM32的ADC输入通道如PA0。通过一个简单的电阻分压电路将可变电阻的变化转化为电压变化送入ADC。在程序中你需要一个“电阻-温度”的换算表或公式。对于PT100在0-100°C范围内可以用近似线性公式R(t) R0 * (1 A * t)其中R0100ΩA≈0.00385。你需要根据电路计算电压与电阻的关系再推导出电压与温度的关系。使用模拟传感器仿真的核心在于标定在仿真中你可以将可变电阻调整到对应特定温度如0°C、100°C的阻值记录下ADC读取到的原始值ADC_Value。通过这两点或多点就可以在代码中建立一个线性映射关系Temperature k * ADC_Value b。这个过程本身就是传感器应用中的重要环节。实操心得对于教学仿真我强烈推荐DS18B20方案。原因有三第一模型现成无需搭建复杂电路第二单总线驱动是嵌入式面试和开发的常见考点仿真练习价值大第三避免了模拟电路中的噪声、参考电压精度等次要问题让学习者更专注于STM32编程和主逻辑。在本项目中我选择了DS18B20作为测温模块。4. STM32程序设计与关键代码剖析仿真项目的硬件模型搭建好后重头戏就是STM32的固件程序。程序需要在Keil MDK或STM32CubeIDE中编写、编译生成.hex或.elf文件然后加载到Proteus的STM32芯片模型中。4.1 工程架构与外设初始化我使用STM32CubeMX进行初始化配置这样能快速生成正确的时钟、GPIO和中断配置避免低级错误。MCUSTM32F103C8T6。时钟HSI 8MHz经PLL倍频到72MHz系统时钟。GPIOTrig_Pin(PA0) 推挽输出用于触发超声波。Echo_Pin(PA1) 浮空输入用于捕获回响脉冲。同时将该引脚配置为定时器如TIM2的输入捕获通道1。DS18B20_Pin(PA2) 推挽输出实际驱动时需切换为开漏用于单总线通信。LCD_RS, LCD_RW, LCD_EN, LCD_D[4:7] 根据你选择的LCD模型如16x2字符LCD连接对应GPIO。定时器TIM2 用于输入捕获。配置为上升沿捕获预分频器设置为72-1使得计数器每1微秒递增一次72MHz / 72 1MHz。这样捕获到的计数值直接就是微秒数方便计算。TIM3 用于产生超声波触发周期例如每200ms触发一次和DS18B20通信所需的微妙级延时基准。SysTick 用于毫秒级延时供LCD驱动等使用。ADC如果采用模拟测温 配置一个通道如PA0连续扫描模式启用DMA传输会更高效。中断 使能TIM2的捕获/比较中断和更新中断用于处理Echo信号。4.2 HC-SR04驱动核心输入捕获与距离计算这是代码中最精妙的部分。我们利用TIM2的输入捕获功能来测量Echo高电平的宽度。// 变量定义 volatile uint32_t IC_Val1 0, IC_Val2 0; volatile uint32_t Difference 0; volatile uint8_t Is_First_Captured 0; // 是否是第一次捕获 volatile float Distance_cm 0; // TIM2中断处理函数 void TIM2_IRQHandler(void) { if (TIM2-SR TIM_SR_CC1IF) { // 捕获/比较1中断 if (!Is_First_Captured) { // 第一次捕获是上升沿 IC_Val1 TIM2-CCR1; // 读取捕获值 Is_First_Captured 1; // 标记已捕获上升沿 // 改变捕获极性为下降沿 TIM2-CCER ~TIM_CCER_CC1P; } else { // 第二次捕获是下降沿 IC_Val2 TIM2-CCR1; // 读取捕获值 // 计算差值考虑计数器溢出 if (IC_Val2 IC_Val1) { Difference IC_Val2 - IC_Val1; } else { Difference (0xFFFF - IC_Val1) IC_Val2; // 假设16位计数器 } // 计算距离时间(us) * 声速(0.034 cm/us) / 2 Distance_cm (Difference * 0.034) / 2.0; // 重置状态准备下一次测量 Is_First_Captured 0; // 将捕获极性改回上升沿 TIM2-CCER | TIM_CCER_CC1P; // 清除中断标志 TIM2-SR ~TIM_SR_CC1IF; } } if (TIM2-SR TIM_SR_UIF) { // 更新中断计数器溢出 // 如果发生溢出且已经捕获了上升沿但还没捕获下降沿说明Echo脉冲太长可能超出量程 if (Is_First_Captured) { Distance_cm 999.9; // 设置为超量程值 Is_First_Captured 0; TIM2-CCER | TIM_CCER_CC1P; // 重置为上升沿捕获 } TIM2-SR ~TIM_SR_UIF; // 清除更新中断标志 } }关键点解析双沿捕获通过动态改变输入捕获通道的极性上升沿/下降沿用一个定时器通道就能完成脉冲宽度的测量。计数器溢出处理TIM2是16位计数器最大计数值65535对应65.535ms。HC-SR04最大测量距离约4米对应Echo脉冲最大宽度约23.5ms小于65ms所以一次溢出内通常能完成测量。但为了健壮性必须处理溢出情况否则如果物体非常近脉冲极短或测量出错可能引发逻辑错误。变量类型Distance_cm定义为volatile是因为它在中断中被修改在主循环中被读取防止编译器优化导致数据不一致。4.3 DS18B20单总线驱动与温度读取DS18B20的驱动关键在于严格按照时序图编写复位、写一位、读一位的函数。这里篇幅所限只给出核心框架和注意事项。// 微秒级延时函数基于TIM3实现 void DWT_Delay_us(volatile uint32_t microseconds) { uint32_t clk_cycle_start DWT-CYCCNT; // 72MHz时钟1个周期约13.9ns1us约72个周期 microseconds * 72; while ((DWT-CYCCNT - clk_cycle_start) microseconds); } // DS18B20复位 uint8_t DS18B20_Reset(void) { uint8_t presence 0; Set_Pin_Output(DS18B20_GPIO_Port, DS18B20_Pin); HAL_GPIO_WritePin(DS18B20_GPIO_Port, DS18B20_Pin, 0); // 拉低总线 DWT_Delay_us(480); // 保持低电平480us以上 Set_Pin_Input(DS18B20_GPIO_Port, DS18B20_Pin); // 释放总线上拉电阻拉高 DWT_Delay_us(70); // 等待15-60us后DS18B20会拉低总线60-240us if (!HAL_GPIO_ReadPin(DS18B20_GPIO_Port, DS18B20_Pin)) { presence 1; // 检测到存在脉冲 } DWT_Delay_us(410); // 等待存在周期结束 return presence; } // 读取温度值 float DS18B20_ReadTemp(void) { uint8_t temp_l, temp_h; int16_t temp_raw; float temperature; if (DS18B20_Reset()) { DS18B20_WriteByte(0xCC); // 跳过ROM命令 DS18B20_WriteByte(0x44); // 开始温度转换 DWT_Delay_ms(750); // 等待转换完成12位精度需750ms DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读取暂存器 temp_l DS18B20_ReadByte(); temp_h DS18B20_ReadByte(); temp_raw (temp_h 8) | temp_l; temperature temp_raw * 0.0625; // 12位精度LSB为0.0625°C return temperature; } return -999; // 错误值 }踩坑实录在Proteus仿真中DS18B20的时序要求似乎比实物更“苛刻”。最初我用简单的for循环做延时仿真经常失败。后来换用定时器或DWTData Watchpoint TraceCortex-M3内核的周期计数器实现的精确微秒延时成功率100%。仿真环境对时序的“较真”恰恰帮我们写出了更健壮的代码。4.4 主循环逻辑与LCD显示主循环的任务很清晰周期性地触发超声波、读取温度、刷新显示。int main(void) { // HAL初始化、外设初始化... LCD_Init(); LCD_PrintString(Dist: cm); LCD_SetCursor(1, 0); LCD_PrintString(Temp: C); // 启动定时器每200ms触发一次超声波 HAL_TIM_Base_Start_IT(htim3); // TIM3用于触发周期 while (1) { // 在TIM3的中断里触发超声波这里主循环主要处理显示 static uint32_t last_disp_time 0; if (HAL_GetTick() - last_disp_time 300) { // 每300ms更新一次显示 last_disp_time HAL_GetTick(); // 显示距离 LCD_SetCursor(0, 6); if (Distance_cm 400.0 || Distance_cm 2.0) { // HC-SR04有效范围约2cm-400cm LCD_PrintString(---); } else { char dist_str[10]; sprintf(dist_str, %3.1f, Distance_cm); LCD_PrintString(dist_str); } // 读取并显示温度 float temp DS18B20_ReadTemp(); LCD_SetCursor(1, 6); if (temp -100) { // 读取错误 LCD_PrintString(Err); } else { char temp_str[10]; sprintf(temp_str, %2.1f, temp); LCD_PrintString(temp_str); } } // 可以加入其他任务如按键处理、数据上传等 } }5. Proteus仿真搭建与联合调试全流程硬件和软件都准备好了最后一步就是在Proteus中把它们连接起来并让程序跑起来。5.1 原理图绘制要点单片机在Proteus元件库中搜索“STM32F103C8”选择有TQFP48封装的型号。放置后双击元件在“Program File”一栏选择你编译生成的.hex文件。在“Clock Frequency”中填入72MHz与代码配置一致。电源与地放置“POWER”和“GROUND”符号为所有器件提供电源。HC-SR04模拟电路按照第2.2节设计的子电路进行连接。将Trig端连接到STM32的PA0Echo端连接到PA1。别忘了给这个模拟电路也接上电源和地。DS18B20从库中拖出数据脚接PA2VDD接3.3V或5VGND接地并在数据脚和VDD之间接一个4.7kΩ的上拉电阻。LCD搜索“LM016L”兼容HD44780的16x2字符LCD。按照你代码中的引脚定义连接RS、RW、E、D4-D7到STM32的对应GPIO。通常RW接地只写模式VSS接地VDD接5VVEE对比度调节通过一个10kΩ电位器接电源和地。调试接口可以放置一个“VIRTUAL TERMINAL”虚拟终端连接到STM32的串口引脚如PA9-TX用于打印调试信息这在初期排查问题时非常有用。5.2 仿真运行与调试技巧点击Proteus左下角的“运行”按钮仿真开始。你会看到STM32芯片上出现一个绿色的箭头表示程序正在运行。观察现象LCD屏幕应该会显示“Dist: xxx cm”和“Temp: xx.x C”的初始值。拖动HC-SR04模拟电路中的滑动变阻器LCD上的距离值应该实时变化。双击DS18B20元件在属性框中修改“Temperature”值LCD上的温度显示也应该随之改变可能需要短暂延迟。逻辑分析仪这是Proteus调试的利器。在左侧工具栏选择“Virtual Instruments” - “Logic Analyzer”。将其通道A连接到Trig引脚PA0通道B连接到Echo引脚PA1。运行仿真然后打开逻辑分析仪窗口你可以清晰看到Trig的周期性脉冲和Echo的响应脉冲并能测量脉冲宽度与代码计算的距离相互印证。示波器同样在虚拟仪器中可以用示波器观察DS18B20数据线上的单总线波形检查复位脉冲、读写时序是否符合规范。常见问题与排查LCD不显示检查电源、对比度电位器调节、引脚连接是否正确特别是E使能信号的时序在代码中是否正确。距离值不变或为0检查Trig和Echo引脚在Proteus和代码中是否对应检查TIM2的输入捕获配置是否正确用逻辑分析仪看是否有Echo信号产生检查滑动变阻器是否真的改变了模拟电路的延迟。温度读取失败或为-999检查DS18B20数据线的上拉电阻用示波器看复位和读写时序检查微秒延时函数是否准确尝试降低STM32的主频比如先降到8MHz进行测试排除时序过于紧张的问题。程序运行异常检查.hex文件路径是否正确检查STM32的晶振配置Proteus中STM32可以不用外接晶振使用内部HSI即可但需与代码一致检查中断优先级配置是否冲突。这个基于仿真的项目其最大价值不在于结果本身而在于这个从原理理解、电路建模、代码编写到联合调试的完整闭环过程。它强迫你去思考每一个信号的来龙去脉去验证每一行代码的实际效果。当你在屏幕上看到随着你的操作而变化的距离和温度值时你对STM32、对传感器、对嵌入式系统的理解就已经从书本和代码落到了可视化的逻辑流之中。这比单纯看开发板跑例程印象要深刻得多。本文还有配套的精品资源点击获取