这次我们来看一个基于 STM32L432KC 微控制器的简易超声波测距装置。这个项目的核心不是复杂的算法而是如何用一块低成本、低功耗的 MCU 和常见的 HC-SR04 超声波模块快速搭建一个可用的测距系统。对于嵌入式初学者、电子爱好者或需要快速验证测距功能的开发者来说这是一个非常直接的实践案例。本文将带你从零开始理解超声波测距原理完成 STM32L432KC 的硬件连接、软件编程并最终实现一个能够稳定测量距离并在串口显示结果的装置。整个过程重点关注代码的可读性、测量的稳定性以及如何在实际环境中进行校准和优化。如果你手头有 STM32 开发板和 HC-SR04 模块跟着做一遍就能跑通。1. 核心能力速览能力项说明核心控制器STM32L432KC (ARM Cortex-M4 低功耗)测距模块HC-SR04 超声波传感器测距原理测量超声波发射到接收回波的时间差根据声速计算距离有效测距范围通常为 2cm ~ 400cm (实际效果受环境、模块精度影响)输出方式通过 USART 串口输出距离数据到 PC 端串口助手供电需求3.3V 或 5V (STM32开发板供电注意HC-SR04逻辑电平)开发环境STM32CubeIDE / Keil MDK关键外设GPIO (输出触发、输入捕获)、定时器 (高精度计时)、USART (通信)适合场景嵌入式学习、课程设计、避障小车原型、简单物体检测2. 适用场景与使用边界这个简易超声波测距装置最适合以下几类人群和场景嵌入式初学者通过一个完整的“传感器-控制器-输出”项目学习 STM32 的 GPIO、定时器、中断和串口编程。快速原型验证在开发机器人、智能小车或安防设备初期需要快速验证测距功能是否可行。电子课程设计或毕业设计作为一个功能明确、硬件成本低、代码量适中的选题。它的能力边界也很清晰精度有限HC-SR04 模块本身精度通常在毫米到厘米级且声速受温度、湿度影响。不适合需要亚毫米级高精度测量的场合。测量对象对平坦、坚硬的表面反射效果最好。对棉花、泡沫等吸音材料或角度过大的斜面可能无法测距或误差极大。环境干扰多个超声波模块同时工作可能互相干扰。嘈杂的声学环境也可能影响测量。非接触式只能测量传感器正前方障碍物的距离无法识别物体材质、形状。重要提醒本项目用于学习和功能验证。若用于实际产品需考虑更专业的传感器、温度补偿算法以及严格的电磁兼容和安全设计。3. 环境准备与前置条件在开始编程和接线之前请确保你已准备好以下软硬件环境。硬件清单STM32L432KC 开发板一块如 NUCLEO-L432KC。HC-SR04 超声波模块一个。杜邦线若干母对母、公对母根据开发板接口而定。USB 数据线一根用于给开发板供电和程序下载/调试。PC 电脑一台。(可选) 逻辑电平转换模块如果开发板只有3.3V电平而你的HC-SR04是5V逻辑版本则需要一个5V转3.3V的电平转换器用于Echo回波信号线。软件清单集成开发环境 (IDE)STM32CubeIDE 或 Keil MDK (uVision)。本文以 STM32CubeIDE 为例因为它集成了STM32CubeMX图形化配置工具对新手更友好。串口调试助手如 Putty、SecureCRT、或者 IDE 自带的串口终端。用于查看测距结果。STM32CubeProgrammer用于程序烧录STM32CubeIDE 已集成烧录功能可备用。4. 硬件连接与电路原理HC-SR04 模块通常有 4 个引脚VCC、Trig (触发)、Echo (回波)、GND。连接示意图如下HC-SR04 STM32L432KC NUCLEO 板 VCC ----------- 5V 或 3.3V 引脚 (注意模块电压) Trig ----------- 任意 GPIO 输出引脚 (如 PA5) Echo ----------- 任意 GPIO 输入引脚 (如 PA6) GND ----------- GND关键连接说明VCC 与 GND为模块供电。务必确认你的 HC-SR04 模块的工作电压。老版本多为5V新版本有3.3V/5V兼容的。如果是5V模块接到开发板的5V引脚如果是3.3V模块接到3.3V引脚。NUCLEO-L432KC 板上有标注的 5V (VIN) 和 3.3V (3V3) 输出。Trig (触发)连接到一个配置为推挽输出的 GPIO 引脚。STM32 通过向此引脚发送一个至少 10us 的高电平脉冲来触发模块发射超声波。Echo (回波)连接到一个配置为浮空输入或上拉输入的 GPIO 引脚。该引脚在模块接收到回波后会输出一个高电平脉冲其宽度与距离成正比。这是最需要注意的地方如果 HC-SR04 是 5V 逻辑输出而 STM32L432KC 的 GPIO 引脚只能容忍 3.3V则必须通过电平转换电路如分压电阻或电平转换芯片将 5V 降至 3.3V 后再接入 PA6否则可能损坏 MCU如果是 3.3V 逻辑的模块则可以直接连接。电平转换简易方案针对5V Echo信号使用两个电阻组成分压电路Echo 信号先串联一个 1kΩ 电阻然后连接到 STM32 的 GPIO同时在 GPIO 引脚与 GND 之间并联一个 2kΩ 电阻。这样可以将 ~5V 电压分压到 ~3.3V。5. STM32CubeMX 工程配置我们使用 STM32CubeMX 进行图形化配置生成初始化代码框架。创建新工程打开 STM32CubeIDE选择 “Start new STM32 project”搜索并选择 MCU 型号 “STM32L432KCUx”。配置系统时钟 (SYS)在 “Pinout Configuration” 标签页左侧找到 “System Core” - “SYS”。将 “Debug” 设置为 “Serial Wire” (这是 ST-LINK 调试接口所需)。配置时钟树 (RCC)找到 “RCC”。将 “High Speed Clock (HSE)” 设置为 “Crystal/Ceramic Resonator”。如果板载外部高速晶振。进入 “Clock Configuration” 标签页。输入 HSE 频率如 8MHz然后通过 PLL 将系统时钟 (SYSCLK) 配置到最高 80MHz。APB1 和 APB2 总线时钟也会自动配置。配置 GPIO 引脚在芯片引脚图上找到你计划使用的引脚例如 PA5 和 PA6。单击 PA5选择 “GPIO_Output”。在左侧的 GPIO 配置中可以设置用户标签为 “TRIG”。单击 PA6选择 “GPIO_Input”。用户标签设置为 “ECHO”。根据是否需要内部上拉在 GPIO 设置中选择 “Pull-up” 或 “No pull-up and no pull-down”。配置定时器 (TIM) 用于高精度计时超声波测距需要微秒级的时间测量。我们将使用一个通用定时器如 TIM2的输入捕获功能或者更简单的方式在代码中利用定时器的计数器直接计算时间。这里我们采用定时器中断计数器读取的方式更直观。找到 “TIM2”或其他空闲的通用定时器。在 “Parameter Settings” 中Prescaler (PSC) 设置为 80-1。如果系统时钟是80MHz分频后计数器时钟为 1MHz (1us计数一次)。Counter Period (ARR) 设置为 65535-1 (16位定时器的最大值) 或更大值如果定时器是32位。Counter Mode Up。auto-reload preload Disable。在 “NVIC Settings” 中不要使能定时器中断。我们只用它的计数器。配置串口 (USART) 用于输出找到 “USART2”NUCLEO板通常通过 ST-LINK 的虚拟串口连接到 USART2。将 “Mode” 设置为 “Asynchronous”。在 “Parameter Settings” 中波特率设置为 115200 Bits/s其他默认8位数据无校验1停止位。引脚 PA2 和 PA3 会自动配置为 USART_TX 和 USART_RX。生成工程代码转到 “Project Manager” 标签页。设置 “Project Name” 和 “Project Location”。“Toolchain / IDE” 选择 “STM32CubeIDE”。点击 “Generate Code”。6. 核心代码编写与解析工程生成后我们主要在main.c和stm32l4xx_it.c如果用到中断中添加业务逻辑。6.1 宏定义与全局变量在main.c的/* USER CODE BEGIN PV */区域定义引脚和变量。/* USER CODE BEGIN PV */ // 超声波模块引脚定义 (根据你的实际连接修改) #define TRIG_PIN GPIO_PIN_5 #define TRIG_PORT GPIOA #define ECHO_PIN GPIO_PIN_6 #define ECHO_PORT GPIOA // 声速定义 (单位厘米/微秒)在20°C干燥空气中约为 0.0343 cm/us #define SOUND_SPEED_CM_US 0.0343 // 也可以定义为 34300 cm/s然后在计算时转换时间单位 volatile uint32_t pulse_start 0; volatile uint32_t pulse_end 0; volatile uint8_t echo_received 0; float distance_cm 0; /* USER CODE END PV */6.2 超声波触发与回波捕获函数在/* USER CODE BEGIN 0 */区域编写驱动函数。/* USER CODE BEGIN 0 */ #include stdio.h // 用于 printf // 微秒级延时函数简易实现精度受系统时钟和优化影响用于触发脉冲 void Delay_us(uint16_t us) { uint32_t ticks us * (SystemCoreClock / 1000000) / 5; // 粗略计算循环次数 while(ticks--) { __NOP(); } } // 发送触发脉冲 void Ultrasonic_Trigger(void) { HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET); // 拉高 Trig Delay_us(12); // 保持高电平至少10us这里给12us HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET); // 拉低 Trig } // 获取高电平脉冲宽度单位微秒 // 方法轮询等待 Echo 引脚上升沿和下降沿利用定时器计数器计算时间差。 uint32_t Get_Pulse_Width_us(void) { uint32_t start_time 0, end_time 0; uint32_t timeout 0; // 等待 Echo 变为高电平 (上升沿) timeout 100000; // 超时计数防止死等 while (HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN) GPIO_PIN_RESET) { if (timeout-- 0) return 0; // 超时返回0 } start_time __HAL_TIM_GET_COUNTER(htim2); // 记录定时器当前值 // 等待 Echo 变为低电平 (下降沿) timeout 100000; while (HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN) GPIO_PIN_SET) { if (timeout-- 0) return 0; // 超时返回0 } end_time __HAL_TIM_GET_COUNTER(htim2); // 计算时间差 (us)因为定时器时钟是1MHz // 注意处理定时器溢出的情况简单项目可暂不考虑或确保测量距离内不会溢出 if (end_time start_time) { return (end_time - start_time); } else { // 计数器溢出计算溢出后的值 return (0xFFFFFFFF - start_time end_time); // 假设是32位定时器 } } // 重定向 printf 到串口 (方便打印) int _write(int file, char *ptr, int len) { HAL_UART_Transmit(huart2, (uint8_t*)ptr, len, HAL_MAX_DELAY); return len; } /* USER CODE END 0 */6.3 主循环逻辑在main()函数的while (1)循环中添加测距和打印逻辑。/* USER CODE BEGIN WHILE */ HAL_TIM_Base_Start(htim2); // 启动定时器2计数器 printf(Ultrasonic Ranging System Started.\r\n); while (1) { Ultrasonic_Trigger(); // 发送触发脉冲 HAL_Delay(60); // 等待至少60ms防止发射信号对回波的干扰 uint32_t pulse_width Get_Pulse_Width_us(); // 获取高电平时间微秒 if (pulse_width 0) { // 计算距离距离 (时间 * 声速) / 2 // 时间单位是 us声速是 cm/us除以2是因为声音走了来回两倍路程 distance_cm (pulse_width * SOUND_SPEED_CM_US) / 2.0; printf(Pulse Width: %lu us, Distance: %.2f cm\r\n, pulse_width, distance_cm); } else { printf(Measurement timeout or error.\r\n); } HAL_Delay(200); // 主循环延时控制测量频率 } /* USER CODE END WHILE */7. 功能测试与效果验证编译与下载在 STM32CubeIDE 中点击 “Build” 按钮编译工程。使用 USB 线连接开发板点击 “Debug” 按钮将程序下载到 MCU 并进入调试模式或直接点击 “Run” 运行。串口监视退出调试模式如果进入了。在 IDE 的 “Window” - “Show View” - “Other…” 中搜索 “Serial Monitor” 并打开。或者在电脑上打开独立的串口调试助手如 Putty。选择正确的串口号在设备管理器中查看 STM32 ST-LINK 虚拟串口的 COM 号。设置波特率为 115200数据位 8停止位 1无校验无流控。连接串口。观察结果将超声波传感器模块正面朝向空旷处或一个固定距离的物体如墙壁。观察串口终端输出的信息。你应该能看到类似以下的输出Ultrasonic Ranging System Started. Pulse Width: 588 us, Distance: 10.09 cm Pulse Width: 588 us, Distance: 10.09 cm ...改变传感器与物体之间的距离输出值应相应变化。校准与优化基准测试用一个卷尺测量实际距离与串口输出的距离对比。计算误差。调整声速SOUND_SPEED_CM_US常量是基于 20°C 空气的。如果环境温度差异大可以根据公式V 331.4 0.6 * T(m/s T为摄氏温度) 重新计算并更新该常量。处理异常值在实际应用中可以连续测量多次然后取中值或平均值以滤除偶然的干扰脉冲。检查超时如果总是测量超时检查 Echo 引脚连接、电平转换是否正确以及Get_Pulse_Width_us函数中的超时值是否合理。8. 资源占用与性能观察CPU 占用主循环中主要的耗时操作是HAL_Delay(200)即每 200ms 测量一次CPU 大部分时间处于空闲或低功耗状态。Get_Pulse_Width_us函数中的轮询等待会短暂占用 CPU。对于 STM32L432KC 来说负载极低。定时器资源我们占用了一个通用定时器 (TIM2) 作为高精度时钟源。它被配置为自由运行模式不产生中断只提供计数器值。GPIO 资源占用两个 GPIO 引脚。USART 资源占用一个 USART 用于调试输出。内存占用代码量小全局变量少对 RAM 和 Flash 的占用可忽略不计。功耗STM32L432KC 本身是超低功耗 MCUHC-SR04 模块在非触发状态下功耗也很低。整个系统非常适合电池供电的便携设备。性能提升方向使用定时器的输入捕获中断来测量 Echo 高电平宽度可以更精确且不阻塞 CPU。使用 DMA 传输串口数据进一步降低 CPU 干预。实现温度传感器 (如 DS18B20) 实时补偿声速。9. 常见问题与排查方法问题现象可能原因排查方式解决方案串口无任何输出1. 串口线未连接或端口错误。2. 波特率设置错误。3. 代码未成功运行。1. 检查设备管理器中的串口号。2. 确认串口助手波特率为115200。3. 检查程序是否下载成功MCU是否复位。1. 选择正确的COM口。2. 双方波特率保持一致。3. 重新下载程序按复位键。串口一直输出 “Measurement timeout or error.”1. Echo 引脚始终为低电平。2. 触发信号未发出。3. 模块供电不足或损坏。4. 物体超出测距范围或表面不反射。1. 用逻辑分析仪或示波器检查 Trig 和 Echo 信号。2. 用万用表测量 VCC 和 GND 电压。3. 将模块对准近距离的平整墙壁测试。1. 检查 Trig/Echo 连线确认电平转换电路如果需要。2. 确保供电电压符合模块要求5V或3.3V。3. 更换模块测试。测量距离固定不变或明显错误1. 声速常量设置错误。2. 定时器分频系数计算错误导致时间单位不对。3. 测量对象是软质或斜面。1. 用尺子测量一个固定距离如20.0cm对比输出反推实际声速或时间系数。2. 检查Get_Pulse_Width_us函数返回的时间值是否合理。1. 根据实测结果校准SOUND_SPEED_CM_US或计算时的系数。2. 检查定时器时钟配置确认计数器加1是否代表1us。3. 对硬质平面进行测量。测量值跳动大不稳定1. 环境噪声干扰。2. 电源纹波大。3. 软件中没有滤波算法。观察多次测量的原始脉冲宽度值。1. 在软件中实现多次测量取中值或平均值滤波。2. 为模块电源增加滤波电容。3. 避免在嘈杂的声学环境中使用。程序下载失败1. 调试器连接问题。2. 芯片被写保护。1. 检查 USB 线、驱动。2. 查看 IDE 中的调试配置。1. 重新插拔 USB 线重启 IDE。2. 使用 STM32CubeProgrammer 连接并解除保护。10. 最佳实践与扩展建议最佳实践首次上电先不接 Echo 线用示波器或逻辑分析仪观察 Trig 引脚是否有正确的 10us 以上脉冲。这是确认 MCU 控制逻辑正确的第一步。电平安全始终将“5V Echo 信号连接至 3.3V MCU”作为重点检查项务必使用电平转换或分压电路。电源去耦在 HC-SR04 的 VCC 和 GND 引脚附近并联一个 10uF 电解电容和一个 0.1uF 陶瓷电容以稳定电源减少干扰。软件滤波在产品代码中务必实现软件滤波。例如连续采样 5 次去掉最大最小值后求平均能极大提升读数稳定性。错误处理增加对超长回波超出量程、超短回波低于最小测距的判断和处理。扩展建议增加显示将距离值显示在 LCD 屏如 OLED或数码管上做成一个独立的测距仪。报警功能设置一个距离阈值当测量值低于该阈值时让一个 LED 闪烁或蜂鸣器鸣叫实现简易报警器。多传感器使用多个超声波模块通过 GPIO 分时复用或不同的定时器通道实现多方向测距用于机器人环境感知。无线传输通过蓝牙模块 (如 HC-05) 或 WiFi 模块 (如 ESP8266) 将距离数据发送到手机或云端。低功耗优化利用 STM32L4 的低功耗模式让 MCU 在两次测量之间进入 Sleep 或 Stop 模式HC-SR04 模块也只在测量时上电大幅延长电池寿命。这个基于 STM32L432KC 的超声波测距项目麻雀虽小五脏俱全。它串联了 GPIO 控制、定时器计时、串口通信等嵌入式核心知识点。通过动手实现它你不仅能得到一个可工作的测距工具更能透彻理解传感器与控制器的交互方式。建议在成功实现基础功能后尝试上述一两个扩展功能这对提升工程能力非常有帮助。