
STM32新手必看HY-SRF05超声波模块从接线到测距的完整实战指南超声波测距技术因其非接触、低成本和高精度的特点在机器人避障、工业检测等领域广泛应用。对于STM32初学者来说HY-SRF05模块是入门嵌入式开发的绝佳选择。本文将带你从零开始逐步完成硬件连接、代码编写到实际测距的全过程。1. 硬件准备与接线1.1 认识HY-SRF05模块HY-SRF05超声波模块具有以下核心特性测距范围2cm-450cm测量精度最高可达3mm工作电压5V DC工作电流15mA工作频率40kHz模块引脚功能说明引脚名称功能描述连接注意事项VCC5V电源输入需稳定5V供电GND接地先接GND再上电TRIG触发控制信号输入需STM32 GPIO控制ECHO回波信号输出返回高电平脉冲OUT报警输出(本教程不使用)可悬空重要提示模块不宜带电连接务必先接GND再上电否则可能损坏模块。1.2 STM32连接方案以STM32F103C8T6最小系统板为例推荐接线方式HY-SRF05 STM32F103 VCC → 5V GND → GND TRIG → PB0 (任意GPIO) ECHO → PB1 (需支持外部中断)实际接线时建议使用杜邦线确保连接牢固。若测量环境有电磁干扰可在VCC和GND之间并联一个0.1μF的电容。2. 开发环境配置2.1 软件准备开始编码前需要准备Keil MDK或STM32CubeIDE开发环境STM32标准外设库或HAL库USB转串口工具(用于调试输出)2.2 工程基础配置新建STM32工程选择对应芯片型号配置系统时钟(推荐使用72MHz主频)启用GPIO和TIM3定时器配置USART1用于调试信息输出关键初始化代码示例// GPIO初始化 void GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; // TRIG引脚配置为推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // ECHO引脚配置为浮空输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, GPIO_InitStructure); } // 定时器3初始化 void TIM3_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period 65535; TIM_TimeBaseStructure.TIM_Prescaler 71; // 1MHz计数频率 TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); }3. 测距原理与代码实现3.1 超声波测距工作原理HY-SRF05的工作时序可分为三个阶段触发阶段给TRIG引脚至少10μs的高电平发射阶段模块自动发送8个40kHz超声波脉冲接收阶段检测ECHO引脚高电平持续时间距离计算公式距离(cm) (高电平时间(μs) × 声速(340m/s)) / 2 高电平时间(μs) / 583.2 基础测距代码实现完整测距流程代码示例// 触发超声波模块 void Trigger_Ultrasonic(void) { GPIO_SetBits(GPIOB, GPIO_Pin_0); // TRIG高电平 delay_us(20); // 保持20μs GPIO_ResetBits(GPIOB, GPIO_Pin_0);// TRIG低电平 } // 计算距离 float Get_Distance(void) { uint32_t time 0; float distance 0; Trigger_Ultrasonic(); // 等待ECHO高电平 while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) RESET); // 启动定时器计数 TIM_SetCounter(TIM3, 0); TIM_Cmd(TIM3, ENABLE); // 等待ECHO低电平 while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) ! RESET); // 停止定时器并获取计数值 TIM_Cmd(TIM3, DISABLE); time TIM_GetCounter(TIM3); // 计算距离(cm) distance time / 58.0; return distance; }3.3 使用外部中断优化为提高测量精度可采用外部中断方式检测ECHO信号// 外部中断配置 void EXTI_Config(void) { EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 连接PB1到EXTI_Line1 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1); EXTI_InitStructure.EXTI_Line EXTI_Line1; EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Rising_Falling; EXTI_InitStructure.EXTI_LineCmd ENABLE; EXTI_Init(EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel EXTI1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0x0F; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0x0F; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); } // 外部中断服务函数 void EXTI1_IRQHandler(void) { static uint32_t start_time 0; if(EXTI_GetITStatus(EXTI_Line1) ! RESET) { if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)) { // 上升沿开始计时 TIM_SetCounter(TIM3, 0); TIM_Cmd(TIM3, ENABLE); } else { // 下降沿停止计时并计算距离 TIM_Cmd(TIM3, DISABLE); float distance TIM_GetCounter(TIM3) / 58.0; printf(Distance: %.2f cm\r\n, distance); } EXTI_ClearITPendingBit(EXTI_Line1); } }4. 常见问题与优化技巧4.1 测量误差分析常见误差来源及解决方法环境温度影响声速随温度变化(331.4 0.6×T℃ m/s)高精度应用需温度补偿多径反射避免在狭窄空间或有反射面的环境测量测量超时添加超时检测防止ECHO一直为高改进的温度补偿代码float Get_Distance_With_Temp(float temperature) { float sound_speed 331.4 0.6 * temperature; // m/s uint32_t time Get_Echo_Time(); // 获取高电平时间(μs) return (time * sound_speed) / 20000.0; // cm }4.2 软件滤波处理为消除偶然误差可采用滑动平均滤波#define FILTER_SIZE 5 float Distance_Filter(float new_distance) { static float buffer[FILTER_SIZE] {0}; static uint8_t index 0; float sum 0; buffer[index] new_distance; index (index 1) % FILTER_SIZE; for(int i0; iFILTER_SIZE; i) { sum buffer[i]; } return sum / FILTER_SIZE; }4.3 实际应用建议测量周期建议每次测量间隔≥60ms被测物体要求面积≥0.5平方米表面平整避免吸音材料(如布料、泡沫)安装位置模块应远离电机等干扰源在机器人项目中可将模块安装在可旋转的舵机上实现多方向测距。一个典型的扫描测距实现void Scan_Area(Servo_TypeDef* servo, uint8_t start_angle, uint8_t end_angle) { for(int anglestart_angle; angleend_angle; angle10) { Servo_SetAngle(servo, angle); delay_ms(200); float dist Get_Filtered_Distance(); printf(Angle:%d, Distance:%.1fcm\n, angle, dist); } }