STM32超声波测距测温Proteus仿真:从传感器驱动到系统调试全解析
2026/9/15 11:50:29 网站建设 项目流程

简介:本资源是一套基于STM32的超声波测距与数字温度测量系统Proteus仿真工程,面向嵌入式初学者及课程设计实践者,解决多传感器协同采集、单总线通信、定时器高精度测时及LCD人机交互等典型开发难点。资源包共244个文件,涵盖65个C语言源码(.c)、55个头文件(.h)、24个编译中间文件(.o/.d)及Proteus仿真工程(.pdsprj)、Keil工程配置(.uvprojx/.axf/.hex)、HAL库驱动模块(如tim、i2c、adc等)和实操截图(.jpg)与演示视频(.wmv),完整呈现从传感器驱动、数据处理到显示输出的全链路实现。已有900人学习下载,配套含可直接运行的Proteus仿真电路、带注释的STM32标准外设库/ HAL库混合代码、DS18B20单总线协议解析逻辑、HC-SR04回波时间捕获算法及1602 LCD动态刷新控制方案,特别适合用于课设验证、毕业设计原型开发与嵌入式综合能力训练。

1. 项目概述与核心价值

最近在整理一些嵌入式学习的经典项目,发现“超声波测距”几乎是每个玩STM32的朋友都绕不开的课题。但很多教程要么只讲测距,要么只讲测温,把两者结合起来,再放到Proteus里跑个仿真的完整案例,还真不多见。今天我就来详细拆解一下这个“基于STM32的超声波测距测温Proteus仿真”项目。这个项目听起来像是一个课程设计或者毕业设计的题目,但它背后的技术点非常扎实,涵盖了单片机IO控制、定时器高级应用、传感器数据采集、OLED显示以及仿真调试等多个核心技能。对于想从51单片机过渡到STM32,或者想深入理解传感器应用的开发者来说,这是一个绝佳的练手项目。它不仅能让你掌握HC-SR04这类常见模块的驱动原理,还能让你接触到温度传感器(比如DS18B20或PT100的简化模型)的读取,更重要的是,通过Proteus仿真,你可以在没有实物硬件的情况下,验证整个系统的逻辑,极大地降低了学习门槛和试错成本。接下来,我会从设计思路、电路搭建、代码编写到仿真调试,一步步带你走通整个流程,并分享一些我实际做仿真时踩过的坑和总结的技巧。

2. 系统整体设计与方案选型

2.1 核心需求与功能定义

这个项目的目标很明确:用一块STM32单片机,同时驱动一个超声波测距模块和一个温度传感器,并将测量结果实时显示出来。在Proteus仿真环境中,我们需要构建出这个系统的虚拟模型,并让程序逻辑正确运行。核心功能点可以拆解为以下几步:

  1. 测距功能:STM32向HC-SR04模块的Trig引脚发送一个至少10us的高电平脉冲,然后监听Echo引脚的高电平持续时间。根据声音在空气中的传播速度(约为340m/s),计算出发射点到障碍物的距离。公式很简单:距离 = (高电平时间 * 声速) / 2。这里除以2是因为声音走了来回两趟。
  2. 测温功能:需要选择一个适合仿真的温度传感器模型。DS18B20是单总线数字温度传感器,在Proteus中有成熟的模型,且驱动程序资源丰富,是首选。STM32通过一根IO口模拟单总线时序,读取DS18B20的内部寄存器,直接得到数字温度值。
  3. 显示功能:为了直观展示数据,我们需要一个显示设备。在仿真中,OLED(I2C或SPI接口)或LCD1602都是不错的选择。考虑到美观和显示信息量,我更喜欢使用0.96寸的OLED(SSD1306驱动,I2C接口),它体积小,显示效果好,Proteus库中也容易找到。
  4. 主控与仿真:主控芯片选择STM32F103C8T6,也就是常说的“蓝桥杯”或“最小系统板”核心芯片,资源足够,模型普及。整个系统在Proteus 8.x版本中搭建和仿真。

2.2 硬件方案选型与考量

为什么选这些器件?这里有一些背后的思考:

  • STM32F103C8T6:这是ARM Cortex-M3内核的入门神器。72MHz主频,64KB Flash,20KB RAM,拥有丰富的定时器、通信接口。相比于51单片机,它的定时器功能强大太多,可以直接用输入捕获功能来测量Echo引脚的高电平脉宽,精度和可靠性远超用外部中断+延时循环的土办法。而且HAL库或标准库让开发更规范。
  • HC-SR04超声波模块:这是最通用、最廉价的测距模块。工作电压3.3V-5V,与STM32的3.3V电平兼容。需要注意的是,它的Echo引脚输出是5V TTL电平,如果直接接STM32的IO口(耐压通常3.6V),有损坏风险。在实物电路中需要电平转换或分压,但在Proteus仿真中,我们可以忽略这个电平兼容问题,因为仿真模型是理想的。这是一个仿真与实物的关键差异点。
  • DS18B20温度传感器:数字输出,精度±0.5℃,满足常规测温需求。单总线协议节省IO口。Proteus中的“DS18B20”模型行为与实物高度一致,非常适合用于仿真验证驱动代码。
  • OLED (SSD1306):I2C接口只需两根线(SCL,SDA),接线简单。自身发光,在仿真中显示清晰。有成熟的开源驱动库(如ssd1306.c/h),移植方便。
  • Proteus仿真:选Proteus是因为它在数字电路和单片机仿真领域的权威性。它允许我们在软件层面完成整个硬件电路的搭建和程序联调,对于原理验证、算法调试、教学演示来说,效率极高。尤其是对于STM32这种需要硬件调试器的芯片,仿真能避免频繁烧录。

注意:仿真与实物的鸿沟。务必记住,Proteus仿真是一个理想环境。它简化了电源噪声、信号抖动、电平转换、传感器物理特性等现实问题。仿真成功的代码,在实物上可能还需要调整延时、添加滤波、处理电平匹配。因此,仿真是强大的学习工具,但不是最终的验证工具。

3. Proteus仿真电路设计与搭建

3.1 元件清单与查找

打开Proteus ISIS,我们需要从库中搜索并放置以下关键元件:

  • 单片机STM32F103C8T6。在Proteus 8.9及以上版本中,ARM Cortex-M系列芯片通常位于Microprocessor ICs->ARM->Cortex-M3分类下。如果找不到确切型号,STM32F103C6STM32F103C8通常可以通用,核心外设基本一致。
  • 超声波模块:Proteus没有现成的“HC-SR04”模型。我们需要用基本元件来模拟它。通常的做法是使用一个脉冲发生器PULSE)来模拟Echo引脚的回波信号。更复杂的模拟会用到运放和逻辑电路,但对于学习驱动原理,一个可控的脉冲源足够了。
  • 温度传感器:直接搜索DS18B20,在Data ConvertersSensors分类下可以找到。
  • 显示屏:搜索OLEDSSD1306。我常用的是OLED 128x64(I2C接口)。也可以在Optoelectronics分类下找到。
  • 其他:电阻、按键(用于复位或功能键)、电源(POWER/GROUND)。为了调试方便,还可以放置虚拟终端(Virtual Terminal, 位于Virtual Instruments模式)来打印串口数据。

3.2 电路原理图连接详解

下图是核心部分的连接思路示意(注:此处用文字描述连接关系,因禁止Mermaid图表):

  1. STM32最小系统:放置STM32F103C8T6,连接VDD(3.3V)和VSS(GND)。连接复位电路(一个10K电阻上拉到3.3V,一个按键接到地)。连接晶振(8MHz),但对于基础仿真,可以不接外部晶振,使用内部HSI时钟,Proteus会默认处理。

  2. HC-SR04模拟电路

    • Trig引脚:连接到STM32的一个GPIO,例如PA1(输出模式)。
    • Echo引脚:这里是我们搭建模拟电路的关键。我们不直接连接STM32,而是先连接一个单稳态触发器或直接用脉冲发生器。更简单的教学方法是:放置一个“DCLOCK”信号源(在Generator Mode中),将其输出端连接到STM32的另一个GPIO,例如PA2(配置为输入捕获模式)。在仿真时,我们可以手动或通过程序控制这个信号源产生一个与距离成比例的高电平脉冲。但为了更贴近真实驱动逻辑,我推荐另一种方法:使用一个受控的电压源和比较器来模拟,但这比较复杂。对于初学者,理解Echo就是一个高电平脉宽信号即可,我们可以用STM32的另一个IO口(如PA3)软件模拟这个脉宽,来测试主程序的测量逻辑,但这失去了仿真的部分意义。因此,一个折中且有效的方案是:在代码中,我们仍然编写完整的HC-SR04驱动函数(控制Trig,等待Echo),但在Proteus中,我们暂时不连接真实的Echo信号源,而是通过调试手段(如断点、变量观察)来验证Trig触发逻辑,Echo的脉宽则用一个固定值或变量在代码中模拟。待测距逻辑正确后,再考虑用更复杂的电路模拟回波。
  3. DS18B20连接:将DS18B20的DQ(数据)引脚连接到STM32的一个GPIO,例如PA4VDD接3.3V,GND接地。注意:Proteus中的DS18B20模型通常需要外接一个4.7K的上拉电阻到VDD,这一点和实物一致。

  4. OLED连接:找到I2C接口的OLED模型。将SCL(时钟线)连接到STM32的PB6(I2C1_SCL),SDA(数据线)连接到PB7(I2C1_SDA)。VCC接3.3V,GND接地。

  5. 调试接口:将STM32的PA9(TX)和PA10(RX)连接到虚拟终端的RXDTXD,用于串口打印调试信息。虚拟终端的波特率设置为115200。

3.3 仿真环境配置要点

  • 芯片固件加载:双击STM32芯片,在Program File一栏,选择你后续由Keil或STM32CubeIDE编译生成的.hex文件。这是仿真的关键一步。
  • 时钟设置:在STM32的属性中,可以设置Crystal Frequency为8MHz。但更重要的是,在代码的系统初始化中,要正确配置时钟树。对于仿真,使用默认的内部时钟(HSI)通常最简单,能避免因时钟配置错误导致的仿真卡死。
  • 电源网络:确保所有VDD/VCC网络都标号为3.3VVCC,所有VSS/GND都接地。使用电源端子(POWER)和地端子(GROUND)来明确标识。

4. 软件驱动与代码实现解析

4.1 开发环境与工程搭建

我使用STM32CubeIDE进行开发,因为它集成了STM32CubeMX图形化配置和IDE,HAL库用起来比较顺手。当然,用Keil MDK加标准库或HAL库也一样。

  1. 新建工程:选择MCU型号STM32F103C8Tx
  2. 时钟配置:为了简单,先使用内部HSI RC振荡器(8MHz)作为系统时钟源。在Clock Configuration标签页,将SYSCLK设置为72MHz(这是F103的典型值,通过PLL倍频实现)。
  3. 引脚配置
    • PA1: 设置为GPIO_Output, 用于HC-SR04的Trig。
    • PA2: 设置为TIM2_CH1, 并配置为输入捕获模式,用于测量Echo脉宽。这是硬件测量的关键,比软件循环等待精确得多。
    • PA4: 设置为GPIO_Output, 开漏输出(Open-Drain),用于DS18B20的DQ线。单总线要求开漏模式以便实现“线与”。
    • PB6,PB7: 设置为I2C1_SCLI2C1_SDA, 用于OLED。
    • PA9,PA10: 设置为USART1_TXUSART1_RX, 异步模式,用于调试串口。
  4. 外设参数配置
    • 定时器TIM2:用于输入捕获。预分频器(PSC)设置为71,这样计数器时钟为72MHz/(71+1)=1MHz,即每计数一次为1us。捕获极性为上升沿。使能捕获中断。
    • I2C1:速度模式选择标准模式(100kHz)即可。
    • USART1:波特率115200,字长8位,无校验。
  5. 生成代码:生成初始化代码后,我们就可以在工程里编写业务逻辑了。

4.2 HC-SR04驱动实现(基于输入捕获)

这是项目的难点和亮点。我们用TIM2的输入捕获功能来测量Echo高电平时间。

// 在main.c或单独的驱动文件里 uint32_t IC_Val1 = 0, IC_Val2 = 0; uint32_t Difference = 0; uint8_t Is_First_Captured = 0; // 是否是第一次捕获标志 float Distance = 0; // 启动一次测距 void HCSR04_Start(void) { HAL_GPIO_WritePin(Trig_GPIO_Port, Trig_Pin, GPIO_PIN_SET); delay_us(12); // 发送至少10us的高脉冲,这里给12us HAL_GPIO_WritePin(Trig_GPIO_Port, Trig_Pin, GPIO_PIN_RESET); } // TIM2输入捕获中断回调函数 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) { if (Is_First_Captured == 0) { // 第一次捕获,是上升沿 IC_Val1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 读取捕获值 Is_First_Captured = 1; // 设置标志位 __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING); // 改为下降沿捕获 } else { // 第二次捕获,是下降沿 IC_Val2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); __HAL_TIM_SET_COUNTER(htim, 0); // 重置计数器 if (IC_Val2 > IC_Val1) { Difference = IC_Val2 - IC_Val1; } else { Difference = (0xFFFF - IC_Val1) + IC_Val2; // 处理计数器溢出 } Distance = Difference * 0.034 / 2; // 声音速度340m/s = 0.034cm/us, 除以2 Is_First_Captured = 0; // 重置标志位 __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING); // 改回上升沿捕获 HAL_TIM_IC_Stop_IT(htim, TIM_CHANNEL_1); // 停止捕获 } } } // 在主循环中调用测距 void main_loop(void) { HCSR04_Start(); // 触发超声波 HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // 启动输入捕获中断 HAL_Delay(100); // 等待测量完成,100ms测量一次 // 此时Distance变量已经更新为最新距离(单位:厘米) }

代码解析与注意事项

  • delay_us(12):这个微秒级延时需要用定时器或HAL_Delay(毫秒级)以外的精准方法实现。通常用TIM定时器做一个微秒延时函数,或者使用DWT(数据观察点)单元。在仿真中,对延时精度要求不高,可以用简单的循环实现,但实物必须用精准延时。
  • 输入捕获原理:定时器TIM2的通道1被配置为输入捕获模式。当Echo引脚出现上升沿时,定时器当前计数值被锁存到捕获/比较寄存器(CCR1),并产生中断。我们在中断中读取这个值(IC_Val1),然后立刻将捕获极性改为下降沿。当Echo变为低电平时,再次锁存计数值(IC_Val2)。两次值之差就是高电平时间的计数值。
  • 计数器溢出处理:定时器是16位的,最大值65535。如果脉宽超过65535us(65.5ms),计数器会归零。代码中的else分支就是为了处理IC_Val2小于IC_Val1(即发生了溢出)的情况。
  • 中断与主循环的协调:测距是异步的。主循环触发测量后,需要等待中断处理完成。这里用HAL_Delay(100)是一种简单粗暴的同步方式,确保有足够时间完成一次测量。更好的方法是用一个状态标志位,在中断里置位,在主循环里查询。

4.3 DS18B20驱动实现(单总线协议)

DS18B20的驱动就是严格模拟单总线时序。时序要求非常严格,特别是复位和读写位的时间隙。

// 微秒延时函数(需要根据系统时钟精确实现,此处为示意) void delay_us(uint16_t us) { // 实现略,可用SysTick或循环实现 } // 初始化DS18B20(复位脉冲+存在脉冲) uint8_t DS18B20_Init(void) { uint8_t presence = 1; DQ_OUTPUT(); // 设置DQ为输出模式 HAL_GPIO_WritePin(DS18B20_GPIO_Port, DS18B20_Pin, GPIO_PIN_RESET); delay_us(480); // 拉低480us以上 DQ_INPUT(); // 设置DQ为输入模式(释放总线) delay_us(60); // 等待15-60us if (HAL_GPIO_ReadPin(DS18B20_GPIO_Port, DS18B20_Pin) == 0) { presence = 0; // 检测到存在脉冲 } delay_us(420); // 等待存在脉冲结束 return presence; // 0=成功,1=失败 } // 从DS18B20读取一个字节 uint8_t DS18B20_ReadByte(void) { uint8_t data = 0; for (int i = 0; i < 8; i++) { DQ_OUTPUT(); HAL_GPIO_WritePin(DS18B20_GPIO_Port, DS18B20_Pin, GPIO_PIN_RESET); delay_us(2); // 拉低至少1us DQ_INPUT(); // 释放总线 delay_us(8); // 等待15us内采样 if (HAL_GPIO_ReadPin(DS18B20_GPIO_Port, DS18B20_Pin)) { data |= (1 << i); } delay_us(50); // 等待该时间隙结束 } return data; } // 向DS18B20写入一个字节 void DS18B20_WriteByte(uint8_t data) { for (int i = 0; i < 8; i++) { DQ_OUTPUT(); HAL_GPIO_WritePin(DS18B20_GPIO_Port, DS18B20_Pin, GPIO_PIN_RESET); delay_us(2); // 拉低至少1us if (data & (1 << i)) { DQ_INPUT(); // 如果是写1,则释放总线 } delay_us(60); // 保持至少60us DQ_INPUT(); // 释放总线,产生上升沿 delay_us(2); } } // 读取温度值 float DS18B20_ReadTemp(void) { uint8_t temp_l, temp_h; uint16_t temp; float temperature; if (DS18B20_Init() == 0) { // 初始化成功 DS18B20_WriteByte(0xCC); // 跳过ROM命令 DS18B20_WriteByte(0x44); // 开始温度转换 HAL_Delay(750); // 等待转换完成,12位精度需750ms DS18B20_Init(); DS18B20_WriteByte(0xCC); // 跳过ROM DS18B20_WriteByte(0xBE); // 读暂存器命令 temp_l = DS18B20_ReadByte(); // 温度低字节 temp_h = DS18B20_ReadByte(); // 温度高字节 temp = (temp_h << 8) | temp_l; temperature = temp * 0.0625; // 12位精度,分辨率0.0625°C return temperature; } return -999; // 读取失败 }

实操心得:单总线时序的魔鬼在细节里。DS18B20的驱动失败,十有八九是时序问题。delay_us函数的精度至关重要。在STM32上,不要用HAL_Delay(毫秒级)来做微秒延时。推荐使用SysTick定时器或者TIM定时器来实现精准的微秒延时函数。在Proteus仿真中,由于CPU速度是理想的,简单的for循环延时可能也能工作,但这会养成坏习惯。务必在实物上验证时序。

4.4 OLED显示驱动与主程序逻辑

OLED显示我们使用现成的ssd1306驱动库。在STM32CubeIDE中,可以通过Manage Embedded Software Packages安装SSD1306的中间件,或者直接移植开源驱动。 主程序的逻辑就清晰了:

int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM2_Init(); MX_I2C1_Init(); MX_USART1_UART_Init(); ssd1306_Init(); // 初始化OLED ssd1306_Fill(Black); // 清屏 ssd1306_SetCursor(0, 0); ssd1306_WriteString("Distance:", Font_7x10, White); ssd1306_SetCursor(0, 20); ssd1306_WriteString("Temp:", Font_7x10, White); ssd1306_UpdateScreen(); float distance_cm = 0; float temperature_c = 0; char disp_buf[20]; while (1) { // 1. 读取温度 temperature_c = DS18B20_ReadTemp(); // 2. 触发并等待一次测距完成(这里简化处理,实际应用状态机更好) HCSR04_Start(); HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); HAL_Delay(100); // 等待测量 distance_cm = Distance; // 从全局变量获取距离 // 3. 更新OLED显示 ssd1306_SetCursor(70, 0); sprintf(disp_buf, "%.1f cm", distance_cm); ssd1306_WriteString(disp_buf, Font_7x10, White); ssd1306_SetCursor(70, 20); sprintf(disp_buf, "%.1f C", temperature_c); ssd1306_WriteString(disp_buf, Font_7x10, White); ssd1306_UpdateScreen(); // 4. 串口打印调试信息 printf("Distance: %.1f cm, Temperature: %.1f C\r\n", distance_cm, temperature_c); HAL_Delay(500); // 主循环延时 } }

5. Proteus仿真调试与问题排查实录

5.1 仿真运行步骤

  1. 在STM32CubeIDE或Keil中编译工程,生成.hex.elf文件。
  2. 在Proteus中,双击STM32芯片,在Program File属性中加载上一步生成的.hex文件。
  3. 点击Proteus左下角的运行按钮(Play)。
  4. 观察OLED屏幕是否显示数据。打开虚拟终端,查看串口打印的信息。

5.2 常见仿真问题与解决方案

在仿真过程中,你几乎一定会遇到下面这些问题:

问题现象可能原因排查与解决思路
程序不运行,芯片红色1. 未加载正确的.hex文件。
2. 时钟配置错误(如HSI未使能,PLL配置错)。
3. 复位电路未连接或连接错误。
1. 确认.hex文件路径正确,重新加载。
2. 检查代码中SystemClock_Config()函数,确保在仿真初期使用HSI。可以在main()最开始加一个LED闪烁测试程序。
3. 检查原理图中复位引脚(NRST)是否通过10K电阻上拉到3.3V,并有按键到地。
OLED不显示或显示乱码1. I2C引脚(PB6,PB7)配置错误或未连接。
2. OLED驱动初始化失败(地址错误、时序问题)。
3. 未调用ssd1306_UpdateScreen()
1. 在Proteus中右键OLED,选择“属性”,查看I2C地址是否与代码中一致(通常0x78或0x7A)。
2. 使用Proteus的I2C调试器(在Debugging Tools中)监控I2C总线数据。
3. 确保初始化后先清屏再显示。
DS18B20始终读取失败(返回-999)1. 单总线引脚未配置为开漏输出(Open-Drain)。
2. 上拉电阻未连接或阻值不对。
3.时序精度不够,这是最常见原因
4. Proteus中DS18B20模型故障。
1. 在CubeMX中检查DQ引脚配置。
2. 确认原理图中DQ线有4.7K上拉电阻到3.3V。
3.重点检查delay_us()函数的精度。在仿真中,可以适当放宽延时时间(比如将所有delay_us参数乘以1.5倍)试试。
4. 尝试替换另一个DS18B20模型,或检查其属性中的电源电压是否设置正确。
超声波测距值始终为0或异常大1. TIM2输入捕获未正确配置或未使能中断。
2. Echo引脚在Proteus中没有信号输入。
3. 中断处理函数HAL_TIM_IC_CaptureCallback未被调用。
4. 距离计算公式错误或单位换算错误。
1. 在CubeMX中复查TIM2配置:通道1是否为输入捕获模式?是否使能了全局中断?
2.这是仿真特有的问题:实物有HC-SR04产生Echo信号,仿真需要模拟。一个临时测试方法是:在代码中手动给Distance变量赋一个固定值(如25.5),看OLED是否能显示。先确保显示和计算逻辑正确。
3. 在stm32f1xx_it.c中确保TIM2_IRQHandler函数调用了HAL_TIM_IRQHandler
4. 检查公式:Distance = Difference * 0.034 / 2;单位是厘米。Difference是微秒数。
虚拟终端无输出1. 串口引脚连接错误(TX接RXD,RX接TXD)。
2. 虚拟终端波特率设置与代码不一致。
3.printf未重定向到串口。
1. 确认STM32的TX(PA9)接虚拟终端的RXD,RX(PA10)接TXD。
2. 双击虚拟终端,检查波特率是否为115200。
3. 在代码中需要重写_writefputc函数,将输出指向HAL_UART_Transmit。这是STM32使用printf的关键一步。
仿真运行极其缓慢1. 计算机性能不足。
2. 电路中使用了过于复杂的模型或激励源。
3. 程序中有大量HAL_Delay或软件空循环。
1. 关闭不必要的软件,降低Proteus的仿真速度(在System->Animation Options中调整)。
2. 简化电路,特别是用于模拟Echo的脉冲发生器,频率不要设得太高。
3. 优化代码,减少不必要的延时。

5.3 高级调试技巧:模拟Echo信号

为了让超声波测距在仿真中“动”起来,我们可以创建一个简单的信号源来模拟Echo引脚随距离变化的脉宽。

  1. 在Proteus中放置一个“PULSE”电压源。
  2. 双击配置其属性:
    • Initial (Low) Voltage: 0
    • Pulsed (High) Voltage: 3.3
    • Start (Secs): 0
    • Pulse Width: 我们需要这个宽度可变。可以链接到一个电压控制脉冲宽度的高级源,但这很复杂。
  3. 更实用的方法:使用“模拟电压源”和“电压控制开关”配合一个简单的RC电路来产生与“距离”成比例的延时,但这超出了基础教学范围。对于学习和代码验证,我强烈建议采用“软件模拟”法:即暂时不连接硬件Echo,而是在代码中,根据一个预设的“模拟距离”,计算出对应的脉宽,然后用一个GPIO(如PA3)输出这个脉宽,并直接将PA3连接到TIM2的输入捕获引脚(PA2)进行测试。这样,你就能完整地测试从Trig触发到距离计算、显示的整个软件链路。

6. 项目优化与扩展思路

当基础功能实现后,可以考虑以下优化,让项目更接近实际应用:

  1. 数据滤波:超声波和温度传感器读取都可能存在偶然误差或干扰。可以加入软件滤波算法,如中值滤波(连续采样N次,取中间值)或滑动平均滤波(取最近N次的平均值),使显示数据更稳定。
  2. 温度补偿:声速受温度影响很大。公式V = 331.4 + 0.6 * T(其中T为摄氏温度)。可以在距离计算中,引入DS18B20测得的温度值进行补偿,提高测距精度。
  3. 非阻塞式设计:当前主循环用HAL_Delay(100)等待测距完成,效率低。可以改为状态机。例如,设置一个measure_state变量,在IDLE状态触发Trig,进入WAITING_ECHO状态,在输入捕获中断完成测量后,状态回到IDLE并更新数据。这样主循环可以高效地处理显示和其他任务。
  4. 增加报警功能:设定一个距离阈值(如小于10cm)或温度阈值(如高于50°C),当超过阈值时,让一个LED闪烁或蜂鸣器报警。
  5. 移植到实物:这是最终考验。你需要准备STM32最小系统板、HC-SR04、DS18B20、OLED屏。注意:
    • HC-SR04的Echo引脚需要接一个1kΩ和2kΩ电阻分压,将5V降到约3.3V后再接入STM32。
    • DS18B20的DQ线务必接4.7kΩ上拉电阻
    • delay_us函数必须用定时器精确实现。
    • 可能需要对中断优先级进行调整,避免冲突。

这个项目从仿真到实物,贯穿了嵌入式开发的完整流程:需求分析、方案设计、软件编码、仿真调试、硬件实现。它麻雀虽小,五脏俱全。我个人的体会是,仿真帮你理清了逻辑,扫清了概念障碍;而实物的调试,才是真正磨练你解决问题能力的地方。当你看到OLED屏上稳定地显示出距离和温度,并且用手在超声波模块前移动时数字随之变化,那种成就感是仿真无法给予的。最后一个小建议,在写驱动时,一定要模块化,把HC-SR04、DS18B20、OLED的驱动分别放在独立的.c/.h文件里,这样代码结构清晰,也方便你以后移植到其他项目中去。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询