
1. HC-SR04超声波模块工作原理HC-SR04超声波测距模块是嵌入式项目中常用的低成本测距方案。这个模块只有四个引脚VCC、GND、Trig和Echo。它的工作原理其实很简单但需要精确的时序控制。模块工作时首先需要给Trig引脚一个至少10微秒的高电平脉冲。这个脉冲就像是发令枪告诉模块现在开始发射超声波模块收到这个信号后内部会发射8个40kHz的超声波脉冲同时Echo引脚会拉高。当模块检测到超声波回波时Echo引脚会拉低。这个高电平的持续时间就是从发射超声波到接收到回波的时间差。计算距离的公式很简单距离 (高电平时间 × 声速) / 2。为什么要除以2因为超声波是往返距离我们只需要单程距离。声速在常温下大约是340m/s但更精确的计算需要考虑环境温度的影响。实际项目中我们常用一个简化公式距离(cm) 高电平时间(us) / 58。我在实际项目中发现HC-SR04有几个需要注意的地方测量周期最好大于60ms避免上次测量的回波干扰下次测量模块最小测量距离约2cm最大测量距离约4-5米测量角度约15度被测物体最好正对模块柔软或吸音材料可能无法有效反射超声波2. STM32输入捕获模式详解STM32的定时器输入捕获功能是测量脉冲宽度的利器。相比用外部中断定时器的方式输入捕获不仅精度更高还能减轻CPU负担。我们以TIM10为例详细讲解如何配置和使用这个功能。首先需要配置TIM10的基本参数时钟源选择内部时钟预分频器(PSC)设置为71这样当主频为72MHz时计数器时钟为1MHz每个计数1us自动重装载值(ARR)设置为6553516位定时器的最大值计数模式选择向上计数输入捕获通道的配置更为关键选择输入捕获通道TIM10只有通道1设置输入滤波器和分频器一般不需要滤波设置捕获极性初始设置为上升沿捕获开启捕获中断输入捕获的工作原理是这样的当指定边沿上升沿或下降沿到来时定时器会立即将当前计数器值(CNT)锁存到捕获比较寄存器(CCR)中并产生中断。我们可以通过交替设置上升沿和下降沿捕获来测量高电平的持续时间。在实际编程中我遇到过几个常见问题忘记清除中断标志位导致重复进入中断没有正确处理计数器溢出情况中断优先级设置不当导致测量不准确没有及时改变捕获极性导致错过边沿3. 完整代码实现与解析下面我们结合HAL库详细讲解如何实现超声波测距的全流程。我会把代码分成几个关键部分并解释每个细节。首先是硬件初始化部分void MX_TIM10_Init(void) { htim10.Instance TIM10; htim10.Init.Prescaler 71; // 1MHz计数频率 htim10.Init.CounterMode TIM_COUNTERMODE_UP; htim10.Init.Period 65535; htim10.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_IC_Init(htim10) ! HAL_OK) { Error_Handler(); } TIM_IC_InitTypeDef sConfigIC; sConfigIC.ICPolarity TIM_ICPOLARITY_RISING; sConfigIC.ICSelection TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler TIM_ICPSC_DIV1; sConfigIC.ICFilter 0; if (HAL_TIM_IC_ConfigChannel(htim10, sConfigIC, TIM_CHANNEL_1) ! HAL_OK) { Error_Handler(); } }超声波启动函数需要产生10us以上的触发脉冲void Ultrasonic_Start(void) { HAL_TIM_Base_Start(htim10); HAL_TIM_IC_Start_IT(htim10, TIM_CHANNEL_1); HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET); delay_us(2); HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET); delay_us(12); HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET); __HAL_TIM_SET_COUNTER(htim10, 0); }最重要的捕获中断回调函数void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { static uint8_t captureState 0; if(htim-Instance TIM10) { if(captureState 0) // 上升沿捕获 { capture[0] HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); captureState 1; } else // 下降沿捕获 { capture[1] HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING); captureState 0; distanceReady 1; HAL_TIM_IC_Stop_IT(htim, TIM_CHANNEL_1); HAL_TIM_Base_Stop(htim); } } }4. OLED显示优化技巧得到距离数据后如何在OLED上清晰稳定地显示也很关键。我分享几个提升显示效果的实用技巧。首先是显示刷新策略。不建议每次测量都刷新整个屏幕这样会导致闪烁。更好的做法是只刷新数值变化的部分添加适当的滤波处理避免数值跳动设置合理的刷新间隔100-200ms为宜OLED显示函数可以这样优化void Display_Distance(float distance) { static float lastDistance 0; char buffer[16]; // 数值变化超过0.5cm或1秒未更新时才刷新 if(fabs(distance - lastDistance) 0.5 || HAL_GetTick() - lastDisplayTime 1000) { sprintf(buffer, %.2f cm, distance); OLED_ShowString(0, 4, Distance: , 16); OLED_ShowString(80, 4, buffer, 16); lastDistance distance; lastDisplayTime HAL_GetTick(); } }对于显示内容布局我建议固定显示标题和单位数值区域预留足够宽度添加测量状态指示如测量中...可以显示历史最小/最大值如果发现OLED显示有残影可以适当降低I2C时钟频率在两次刷新之间添加短暂延迟使用OLED自带的清除函数而不是全屏填充5. 常见问题与调试方法在实际项目中超声波测距可能会遇到各种问题。这里总结几个常见问题及其解决方法。问题1测量结果不稳定数值跳动大解决方法添加软件滤波如移动平均或中值滤波增加测量间隔避免声波反射干扰检查电源是否稳定必要时增加滤波电容确保被测物体表面平整避免漫反射问题2测量距离与实际距离有偏差解决方法校准声速参数考虑温度补偿检查定时器时钟配置是否正确验证输入捕获是否准确可以用示波器对比注意模块的测量盲区约2-3cm问题3偶尔出现极大或极小异常值解决方法添加数据合理性检查丢弃明显异常值设置超时机制超过预期时间未收到回波则放弃本次测量检查硬件连接确保Echo信号稳定调试时可以分步骤验证先用示波器观察Trig和Echo信号单独测试输入捕获功能用已知脉冲验证测试OLED显示确保显示功能正常最后整合全部功能6. 性能优化与进阶应用当基本功能实现后我们可以进一步优化系统性能和扩展应用场景。性能优化方面使用DMA传输OLED数据减少CPU占用采用中断定时器的方式实现多模块测量添加温度传感器补偿声速实现低功耗模式间歇性测量进阶应用场景多超声波模块组网实现区域监测结合电机控制实现自动跟踪添加无线传输模块远程监控距离数据实现历史数据记录和分析功能一个实用的优化技巧是动态调整测量频率void Adjust_Measure_Frequency(float distance) { static uint32_t measureInterval 100; // 默认100ms if(distance 50.0) // 近距离时加快测量 measureInterval 50; else if(distance 200.0) // 远距离时减慢测量 measureInterval 200; osDelay(measureInterval); }对于需要更高精度的场合可以考虑使用更高精度的定时器如32位定时器提高定时器时钟频率添加硬件滤波电路使用时间数字转换器(TDC)等专用芯片