一、硬件准备
- 主控:STM32F103C8T6 最小系统板
- 模块:HC-SR04(5 V 供电,信号口 5 V 但兼容 3.3 V,可直接接 F103)
- 供电:USB-TTL 的 5 V 给模块,3.3 V 给 MCU
二、测距原理回顾(已懂可跳过)
- Trig 引脚拉低→拉高→拉低,高电平 ≥10 µs,模块自动发出 8 个 40 kHz 方波
- Echo 随后变高,直到收到回波再变低;高电平宽度 t 即“往返时间”
- 距离 d = t * 0.034 cm/µs / 2 = t / 58.8 (cm)温度修正:v = 331.4 + 0.6×T(℃) m/s,可再乘 100/2 得到 cm 对应常数
三、CubeMX 图形配置(HAL 库,5 min 搞定)
- 新建工程→选 MCU STM32F103C8Tx
- RCC:外部晶振 8 M → 72 M(PLL)
- SYS:Debug 选 Serial Wire
- 引脚配置PA7 → GPIO_Output (Trig,默认命名为 Trig_Pin)PA6 → TIM3_CH1 (输入捕获)
- TIM3 参数Prescaler = 72-1 // 72 MHz/72 = 1 MHz → 1 µs 计数Counter Period = 0xFFFF // 最大 65 ms,足够 4 m 量程Trigger = 上升沿,分频 1,滤波 0
- NVIC:使能 TIM3 global interrupt(抢占优先级 2)
- USART1 异步 115200(方便打印结果)
- Project Manager→Toolchain
main.c
/* USER CODE BEGIN PV */
volatile uint32_t cap_val1 = 0; // 第一次捕获
volatile uint32_t cap_val2 = 0; // 第二次捕获
volatile uint8_t capture_done = 0;
#define SPEED_SOUND_CM_US (0.034f) // 340 m/s → 0.034 cm/µs
/* USER CODE END PV */
启动测距函数
/* USER CODE BEGIN 2 */
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1); // 开启输入捕获中断
/* USER CODE END 2 */
中断回调 + 主循环
/* USER CODE BEGIN 4 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3) {
if (capture_done == 0) { // 第一次捕获→上升沿
cap_val1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
capture_done = 1;
}
else if (capture_done == 1) { // 第二次捕获→下降沿
cap_val2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
capture_done = 2; // 通知主循环可以计算
}
}
}
int main(void)
{
/* 初始化省略 */
while (1)
{
// 1. 触发
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET);
delay_us(12); // 随便 >10 µs
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET);
// 2. 等待测量完成
capture_done = 0;
uint32_t timeout = 0;
while (capture_done != 2 && ++timeout < 50000) delay_us(10);
if (capture_done == 2) {
uint32_t diff = (cap_val2 >= cap_val1) ? (cap_val2 - cap_val1)
: (0xFFFF - cap_val1 + cap_val2);
float distance = diff * SPEED_SOUND_CM_US / 2.0f; // cm
printf("Distance=%.2f cm\r\n", distance);
} else {
printf("Timeout!\r\n");
}
HAL_Delay(60); // 抗余震,最小 60 ms
}
}
五、标准库 / 寄存器 方案(非 HAL,极简)
若项目不用 Cube,可直接操作寄存器,思路同 :
- GPIOA 时钟使能,PA7 推挽输出,PA6 浮空输入
- TIM3 1 MHz 计数,循环查询 PA6 电平,记录 t1、t2
- d = (t2-t1)*0.017 cm代码 30 行左右,适合对体积/启动时间极度敏感的场景。
六、常见问题与调试技巧
- 读数恒为 0→ Echo 引脚没进中断,用示波器看是否有高电平;检查 AFIO 重映射
- 读数跳动大→ 多次平均(丢最大最小再均值),或加简单 IIR 低通
- 最大量程不到 4 m→ 保证 5 V 供电;MCU 5 V 兼容差时可接 1 k+二极管钳位
- 温度影响→ 加 DS18B20/NTC,实时修正音速常数
- 多模块并联→ 各模块 Trig 统一控制,Echo 分别进不同捕获通道;

开源社区 


