简介:这是一套面向嵌入式初学者与电子爱好者的超声波测距完整方案,采用51单片机搭配收发一体超声波探头实现距离检测,电路原理与倒车雷达基本一致,适合用来理解测距模块的收发时序、回波捕获与距离换算逻辑。压缩包共21个文件,约54KB,包含C源程序、A51启动汇编、ASM汇编代码、HEX烧写文件,以及uvproj、uvopt等Keil工程配置和obj、lst、m51等编译中间产物,方便直接打开工程编译、烧录与调试。目前已有1236人学习下载。资源将硬件方案与内部源程序一并开放,读者可据此掌握定时器计数、超声波触发与回波接收的完整实现,并对照原理图完成实物验证,是入门嵌入式测距与倒车雷达电路的实用参考。
1. 收发一体超声波探头做测距:为什么你的回波总在盲区里消失
很多人第一次拆开超声波测距模块,看到探头只有两个引脚,就以为收发一体探头不过是把发射和接收两个分体探头塞进同一个金属壳里。实际焊到板子上跑起来才发现,回波信号要么被发射余震淹没,要么在几厘米处就再也测不到。收发一体超声波探头的核心矛盾在于:同一个压电陶瓷片既要承受几十伏的高压激励脉冲,又要在微秒级之后去捕捉毫伏级的微弱回波。这个标题里的“内部源程序”,本质上就是围绕这个矛盾展开的时序控制、盲区处理和温度补偿逻辑。它适合已经能点亮单片机、想从零写一套测距固件的嵌入式工程师,也适合被现成模块的固定盲区卡住、需要自己调参数的老手。读完你能拿到一套可复现的收发一体测距代码框架,知道每个延时和阈值背后的物理依据,而不是抄一段玄学延时了事。
2. 收发一体探头的电气特性与驱动电路选型
2.1 为什么不能拿分体探头的驱动直接套用
分体式超声波模块的发射探头和接收探头是分开的,发射端只管把电信号转换成机械振动,接收端只管把回波振动转换成电信号。收发一体探头把这两件事压在同一片压电陶瓷上,带来的第一个问题就是阻抗匹配和余震拖尾。发射时,驱动电路要给探头施加一个高压窄脉冲,通常是几十伏到上百伏的方波或尖峰。这个脉冲结束后,压电陶瓷不会立刻静止,它会按照自身的谐振频率继续振荡一段时间,这就是余震。余震的幅度远大于回波,如果接收电路在余震期间就打开,比较器会被余震反复触发,测出来的距离永远是零或者一个固定的小值。
常见做法是在发射结束后插入一段“消隐时间”,把接收通道关掉或者把比较器输出屏蔽掉。消隐时间的长短取决于探头的谐振频率和品质因数。以常见的 40kHz 收发一体探头为例,余震拖尾通常在 1ms 到 2ms 之间。如果消隐时间设得太短,余震还没衰减到回波电平以下,就会误触发;设得太长,近距离的回波会被一起屏蔽掉,盲区变大。我一般会先用示波器抓一下探头两端的余震波形,看它衰减到 1mV 以下需要多久,然后把这个时间乘以 0.8 作为消隐时间的初始值。
驱动电路方面,收发一体探头需要高压激励。单片机 IO 口直接输出 3.3V 方波是推不动探头的,必须经过升压或者变压器耦合。常见的方案有两种:一种是使用专用的超声波驱动芯片,内部集成升压和驱动逻辑;另一种是用变压器把低压方波升到几十伏,再通过隔直电容加到探头上。变压器方案的成本低,但要注意变压器的漏感和探头电容可能形成谐振,导致实际加到探头上的波形不是干净的方波,而是带振铃的尖峰。这个振铃如果处理不好,会延长余震时间,进一步恶化盲区。
2.2 接收通道的放大与比较器阈值设定
回波信号经过空气衰减后,到达接收端的幅度可能只有几十微伏到几毫伏。收发一体探头在接收阶段,压电陶瓷输出的信号非常微弱,而且源阻抗很高。如果直接接到比较器,比较器的输入偏置电流和输入电容会严重影响信号。所以接收通道通常需要一级高输入阻抗的缓冲,再经过多级放大,最后送进比较器或者 ADC。
放大器的带宽要覆盖探头的工作频率。40kHz 探头,放大器的 -3dB 带宽至少要覆盖 20kHz 到 80kHz。增益不是越大越好,因为放大器的输入噪声也会被一起放大。如果增益过高,比较器可能被噪声触发,测出来的距离跳变严重。我一般会把总增益设在 60dB 到 80dB 之间,然后通过比较器的阈值来抑制噪声。
比较器阈值设定是收发一体测距里最容易被忽视的环节。阈值固定不变的话,近距离回波强,远距离回波弱,固定阈值会导致远距离测不到或者近距离误触发。常见做法是使用时间增益补偿,也就是随着时间推移逐渐降低比较器阈值,因为回波到达时间越晚,说明距离越远,信号越弱。时间增益补偿可以用单片机 DAC 输出一个斜坡电压作为比较器参考,也可以用数字电位器配合 RC 网络实现。如果不想增加硬件成本,也可以在软件里做多次测量,每次用不同的阈值,但这样会拉长测量周期。
2.3 最小驱动与接收电路搭建步骤
下面给出一套基于变压器的收发一体驱动电路搭建步骤,适合在万能板或者自制 PCB 上复现。
第一步,准备变压器。选用 1:10 到 1:20 的脉冲变压器,初级接单片机定时器输出的 40kHz 方波,次级接探头。初级串联一个限流电阻,阻值根据变压器初级阻抗选取,通常在 10Ω 到 100Ω 之间。次级并联一个反向保护二极管,防止余震电压击穿驱动管。
第二步,连接探头。探头一端接变压器次级,另一端接地。注意探头的极性,虽然收发一体探头通常是对称的,但有些型号有极性标记,接反会导致灵敏度下降。
第三步,搭建接收缓冲。用一颗 JFET 输入运放做电压跟随器,输入接探头热端,输出接后续放大级。运放供电要干净,最好用 LDO 单独供电,避免数字噪声串入。
第四步,搭建放大级。用两级同相放大器,每级增益 20dB 到 30dB。级间加 RC 高通滤波,截止频率设在 20kHz 左右,滤掉低频振动噪声。
第五步,连接比较器。比较器反相端接时间增益补偿电压,同相端接放大后的回波信号。比较器输出接单片机外部中断引脚,下降沿触发。
// 以 STM32 为例,配置定时器输出 40kHz 方波驱动变压器 // 系统时钟 72MHz,定时器预分频 0,自动重装载值 1800 // 72MHz / (0 + 1) / (1800 + 1) = 40kHz,占空比 50% TIM_TimeBaseInitTypeDef TIM_InitStruct; TIM_OCInitTypeDef TIM_OCInitStruct; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_InitStruct.TIM_Prescaler = 0; TIM_InitStruct.TIM_Period = 1799; // 自动重装载值 TIM_InitStruct.TIM_ClockDivision = 0; TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_InitStruct); TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStruct.TIM_Pulse = 900; // 占空比 50% TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM2, &TIM_OCInitStruct); TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_Cmd(TIM2, ENABLE);这段代码配置 TIM2 的通道 1 输出 40kHz 方波。预分频设为 0,自动重装载值设为 1799,比较值设为 900,得到 50% 占空比。实际驱动变压器时,方波需要经过三极管或者 MOS 管扩流,因为单片机 IO 口的驱动能力只有几十毫安,不足以驱动变压器初级。扩流电路可以用推挽结构,上管 PNP,下管 NPN,基极通过电阻接单片机 IO。
参数说明:预分频和自动重装载值决定了输出频率,公式是 频率 = 系统时钟 / (预分频 + 1) / (自动重装载值 + 1)。比较值决定占空比,公式是 占空比 = 比较值 / (自动重装载值 + 1)。如果系统时钟不是 72MHz,需要重新计算这两个值。占空比不建议超过 50%,因为变压器在方波驱动下,占空比过大会导致磁芯饱和,初级电流急剧上升,可能烧毁驱动管。
3. 内部源程序的时序控制与回波捕获逻辑
3.1 发射、消隐、接收三阶段的精确切换
收发一体测距的固件核心是一个状态机,按时间顺序切换发射、消隐、接收三个状态。发射阶段,定时器输出 8 到 12 个 40kHz 脉冲,脉冲数量不能太少,否则探头振动能量不够,回波太弱;也不能太多,否则余震拖尾更长。我一般用 10 个脉冲,对应 250 微秒的发射时间。
发射结束后,立即进入消隐阶段。消隐阶段要做两件事:一是关闭定时器输出,让探头停止被驱动;二是屏蔽比较器中断,防止余震触发。消隐时间根据探头型号不同,通常在 1ms 到 2ms 之间。可以用定时器产生一个固定延时,也可以用系统滴答定时器计数。
消隐结束后,进入接收阶段。接收阶段打开比较器中断,等待回波。回波到达时,比较器输出下降沿,触发单片机外部中断。在中断里读取定时器计数值,这个计数值就是从发射开始到回波到达的时间差。距离等于声速乘以时间差除以二,因为声波是往返传播的。
// 测距状态机核心逻辑 typedef enum { STATE_IDLE, STATE_TRIGGER, STATE_BLANKING, STATE_RECEIVING } UltrasonicState; volatile UltrasonicState g_state = STATE_IDLE; volatile uint32_t g_echo_time = 0; volatile uint32_t g_start_time = 0; void ultrasonic_trigger(void) { if (g_state != STATE_IDLE) return; g_state = STATE_TRIGGER; g_start_time = TIM_GetCounter(TIM3); // 记录发射起始时刻 TIM_Cmd(TIM2, ENABLE); // 打开 40kHz 驱动 delay_us(250); // 发射 10 个脉冲 TIM_Cmd(TIM2, DISABLE); // 关闭驱动 g_state = STATE_BLANKING; delay_us(1500); // 消隐 1.5ms,等待余震衰减 g_state = STATE_RECEIVING; EXTI_GenerateSWInterrupt(EXTI_Line0); // 使能比较器中断 } void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) != RESET) { if (g_state == STATE_RECEIVING) { g_echo_time = TIM_GetCounter(TIM3) - g_start_time; g_state = STATE_IDLE; } EXTI_ClearITPendingBit(EXTI_Line0); } }这段代码展示了状态机的基本框架。ultrasonic_trigger函数负责发起一次测量,先记录起始时刻,然后打开定时器驱动探头,发射 250 微秒后关闭驱动,进入消隐状态。消隐 1.5ms 后进入接收状态,使能外部中断。外部中断服务函数里,如果当前处于接收状态,就读取定时器计数值,计算回波时间,然后回到空闲状态。
参数说明:delay_us(250)对应 10 个 40kHz 脉冲,每个脉冲周期 25 微秒,10 个周期就是 250 微秒。delay_us(1500)是消隐时间,这个值需要根据实际探头调整。如果消隐时间太短,余震会误触发中断,读到的回波时间会非常小,对应距离接近零。如果消隐时间太长,近距离的回波会被屏蔽,盲区变大。可以用示波器观察比较器输出,在发射结束后,余震引起的脉冲会持续一段时间,等这些脉冲消失后再打开中断。
3.2 回波时间到距离的换算与温度补偿
回波时间拿到之后,换算成距离需要知道声速。声速在空气中不是固定值,它随温度变化。0°C 时声速约 331m/s,25°C 时约 346m/s。如果按固定 340m/s 计算,温度变化 10°C,距离误差大约 1.8%。对于精度要求不高的场景,这个误差可以接受;但如果要做毫米级测距,必须做温度补偿。
温度补偿的常见做法是加一颗数字温度传感器,比如 DS18B20 或者 LM75,测量环境温度,然后按公式计算声速。声速与温度的关系可以近似为:v = 331.4 + 0.607 * T,其中 T 是摄氏度。这个公式在 0°C 到 40°C 范围内误差很小,足够超声波测距使用。
// 温度补偿后的距离计算 float get_distance_cm(uint32_t echo_time_us) { float temperature = read_temperature(); // 读取环境温度 float sound_speed = 331.4 + 0.607 * temperature; // 计算声速,单位 m/s float distance_m = sound_speed * echo_time_us * 1e-6 / 2.0; return distance_m * 100.0; // 转换为厘米 }这段代码先读取温度,然后按公式计算声速,再根据回波时间计算距离。注意回波时间是往返时间,所以除以 2。单位换算成厘米后返回。
参数说明:read_temperature函数需要根据实际使用的温度传感器实现。如果不用温度补偿,可以把sound_speed固定为 340。echo_time_us是定时器计数值换算成微秒后的结果,如果定时器频率不是 1MHz,需要先换算。距离计算里浮点运算在单片机上可能比较慢,如果测量周期要求高,可以用定点数或者查表法替代。
3.3 多次测量与滤波策略
单次测量容易受噪声干扰,回波时间可能跳变。常见做法是连续测量 5 到 10 次,然后取中值或者去掉最大最小值后取平均。中值滤波对脉冲噪声抑制效果好,计算量也不大。如果测量周期允许,可以测量 10 次,排序后取中间 3 个的平均值。
// 中值滤波测距 #define SAMPLE_COUNT 10 float measure_distance_filtered(void) { uint32_t samples[SAMPLE_COUNT]; for (int i = 0; i < SAMPLE_COUNT; i++) { ultrasonic_trigger(); while (g_state != STATE_IDLE); // 等待测量完成 samples[i] = g_echo_time; delay_ms(10); // 两次测量之间留间隔,避免余震干扰 } // 冒泡排序 for (int i = 0; i < SAMPLE_COUNT - 1; i++) { for (int j = 0; j < SAMPLE_COUNT - 1 - i; j++) { if (samples[j] > samples[j + 1]) { uint32_t temp = samples[j]; samples[j] = samples[j + 1]; samples[j + 1] = temp; } } } // 取中间 3 个的平均值 uint32_t sum = 0; for (int i = SAMPLE_COUNT / 2 - 1; i <= SAMPLE_COUNT / 2 + 1; i++) { sum += samples[i]; } return get_distance_cm(sum / 3); }这段代码连续测量 10 次,每次测量后等待状态机回到空闲。测量间隔 10ms,避免上一次的余震影响下一次测量。排序后取中间 3 个样本的平均值,这样既能抑制脉冲噪声,又能保留一定的响应速度。
参数说明:SAMPLE_COUNT可以根据实际需求调整,样本越多,滤波效果越好,但测量周期越长。10 次测量加上间隔,总耗时大约 100ms 到 200ms,适合对实时性要求不高的场景。如果要求快速响应,可以减少样本数,或者改用滑动平均滤波。两次测量之间的间隔不能太短,否则上一次的余震还没完全衰减,会影响下一次的消隐判断。
4. 收发一体测距的避坑与排查记录
4.1 回波永远测不到,中断从不触发
现象:发射电路正常,示波器能看到探头两端有高压脉冲,但比较器输出始终没有下降沿,单片机中断从不触发。
原因:接收通道增益不够,或者比较器阈值设得太高。收发一体探头在接收阶段输出的信号非常微弱,如果放大级增益只有 40dB,回波信号可能只有几毫伏,而比较器阈值设在了 100mV,自然触发不了。另一个可能的原因是接收通道的输入阻抗太低,把探头输出的微弱信号短路掉了。
解决:先用示波器直接看放大级输出,确认回波信号是否存在。如果回波信号存在但幅度小,增加放大级增益,或者降低比较器阈值。如果回波信号不存在,检查接收通道的输入阻抗,确保运放输入偏置电流足够小。比较器阈值可以先用可调电阻手动调,找到能稳定触发的位置,再换成固定电阻或者 DAC 控制。
4.2 近距离测不到,盲区比手册标称值大很多
现象:手册标称盲区 2cm,实际测量时 10cm 以内都测不到,回波时间总是零。
原因:消隐时间设得太长。消隐时间是为了屏蔽余震,但如果设得比余震拖尾长很多,近距离的回波会在消隐期间到达,被一起屏蔽掉。另一个可能的原因是发射脉冲数量太多,余震能量大,拖尾时间长。
解决:用示波器观察比较器输出,在发射结束后,余震引起的脉冲会持续一段时间。测量这段脉冲的宽度,把消隐时间设为这个宽度的 0.8 倍左右。如果余震拖尾太长,减少发射脉冲数量,从 10 个降到 6 个或者 8 个。还可以在探头两端并联一个阻尼电阻,加速余震衰减,但阻尼电阻会降低发射效率,需要权衡。
4.3 远距离测量跳变,数据不稳定
现象:测量 1 米以上的距离时,读数在几十厘米范围内跳变,有时甚至跳到零。
原因:远距离回波信号弱,比较器阈值固定不变的话,噪声可能触发比较器。另一个可能的原因是时间增益补偿没有做好,远距离回波到达时,比较器阈值没有相应降低。
解决:检查时间增益补偿电路,确保比较器阈值随时间推移逐渐降低。如果用的是单片机 DAC 输出斜坡电压,确认斜坡的起始值和终止值是否合适。起始值应该略高于近距离回波峰值,终止值应该略低于远距离回波峰值。还可以在软件里做多次测量,剔除明显异常的值。如果跳变仍然严重,考虑增加放大级增益,或者换用灵敏度更高的探头。
4.4 温度变化后精度明显下降
现象:常温下标定好的距离,到了冬天或者夏天,误差变大,有时超过 5%。
原因:声速随温度变化,如果固件里声速固定为 340m/s,温度偏离 25°C 时就会产生误差。温度每变化 1°C,声速变化约 0.607m/s,距离误差约 0.18%。温度变化 20°C,误差接近 3.6%。
解决:加装温度传感器,在距离计算时做温度补偿。温度传感器要远离发热元件,比如稳压芯片和单片机,否则测到的是板子温度而不是环境温度。如果不想增加硬件成本,可以在固件里预留温度校准接口,出厂时用标准距离标定,把校准系数存在 Flash 里。
4.5 多个探头同时工作互相干扰
现象:一个设备上装了多个收发一体探头,单独工作时都正常,同时工作时测距结果混乱。
原因:多个探头工作频率相同,一个探头发射的声波被另一个探头接收,导致交叉干扰。即使工作频率略有不同,如果探头带宽较宽,也会互相影响。
解决:分时复用,让多个探头轮流测量,同一时刻只有一个探头处于发射状态。分时复用的时间间隔要大于最大测量距离对应的回波时间,比如最大测距 4 米,回波时间约 24ms,分时间隔至少 30ms。如果必须同时工作,可以选用不同谐振频率的探头,比如 40kHz 和 25kHz 搭配使用,但接收通道的滤波器也要相应调整。
5. 把测距固件做成可复用的驱动模块
5.1 硬件抽象层的接口设计
一套测距固件如果只针对某款单片机写死,换一颗芯片就要重写,维护成本很高。我一般会把硬件相关的操作抽象成几个接口:发射驱动接口、消隐延时接口、回波捕获接口、温度读取接口。上层状态机只调用这些接口,不直接操作寄存器。
// 硬件抽象层接口定义 typedef struct { void (*trigger_start)(void); // 启动发射 void (*trigger_stop)(void); // 停止发射 void (*blanking_delay)(uint32_t us); // 消隐延时 uint32_t (*get_tick)(void); // 获取当前 tick float (*read_temperature)(void); // 读取温度 } UltrasonicHAL; // 上层状态机使用 HAL 接口 void ultrasonic_measure(UltrasonicHAL *hal) { hal->trigger_start(); hal->blanking_delay(250); hal->trigger_stop(); hal->blanking_delay(1500); uint32_t start = hal->get_tick(); // 等待回波中断,中断里记录时间 // ... }这样设计的好处是,换单片机时只需要重新实现UltrasonicHAL里的函数指针,上层状态机和滤波逻辑不用动。trigger_start和trigger_stop负责控制定时器输出,blanking_delay可以用系统滴答定时器或者硬件定时器实现,get_tick返回微秒级计数值,read_temperature返回摄氏度浮点数。
参数说明:blanking_delay的精度直接影响测距精度,如果延时误差 10 微秒,距离误差约 1.7 毫米。所以消隐延时最好用硬件定时器实现,不要用软件循环。get_tick的计数频率建议不低于 1MHz,否则距离分辨率不够。如果单片机主频较低,可以用定时器输入捕获功能直接测量回波脉宽,减轻 CPU 负担。
5.2 用输入捕获替代外部中断
外部中断方式在回波捕获时有一个缺点:中断响应时间不确定,会导致测距抖动。如果单片机支持定时器输入捕获,可以把比较器输出接到定时器捕获引脚,硬件自动记录回波到达时刻,精度比外部中断高很多。
// 定时器输入捕获配置 TIM_ICInitTypeDef TIM_ICInitStruct; TIM_ICInitStruct.TIM_Channel = TIM_Channel_1; TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling; TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStruct.TIM_ICFilter = 0x0F; // 滤波,抑制噪声 TIM_ICInit(TIM3, &TIM_ICInitStruct); // 捕获中断里读取捕获值 void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) { uint32_t capture = TIM_GetCapture1(TIM3); g_echo_time = capture - g_start_time; TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); } }输入捕获的滤波参数TIM_ICFilter可以抑制比较器输出上的毛刺。滤波值越大,抑制效果越好,但也会引入额外延时。对于 40kHz 回波,滤波值设在 0x0F 左右比较合适,既能滤掉高频噪声,又不会明显影响回波边沿。
参数说明:TIM_ICPolarity_Falling表示下降沿捕获,因为比较器输出在回波到达时通常是从高变低。如果实际波形相反,改成上升沿捕获。TIM_ICPrescaler设为不分频,保证捕获精度。g_start_time是发射起始时刻的定时器计数值,需要在发射开始时记录。
5.3 低功耗场景下的间歇测量策略
如果测距模块用电池供电,连续测量会很快耗尽电量。我一般会把测量周期拉长,比如每秒测一次,其余时间让单片机进入睡眠模式。睡眠时关闭发射驱动和接收通道的电源,只保留定时器唤醒。
// 低功耗间歇测量 void ultrasonic_low_power_loop(void) { while (1) { ultrasonic_measure(&hal); // 测量一次 float distance = get_distance_cm(g_echo_time); if (distance < ALARM_THRESHOLD) { trigger_alarm(); } enter_stop_mode(); // 进入 STOP 模式 // 定时器唤醒后继续循环 } }STOP 模式下,单片机内核停止运行,但定时器可以继续计数。定时器溢出唤醒后,继续下一次测量。发射驱动和接收通道的电源可以用 MOS 管控制,测量时才通电,其余时间断电。这样平均功耗可以降到毫安级甚至微安级。
参数说明:ALARM_THRESHOLD是报警距离阈值,根据应用场景设定。enter_stop_mode的具体实现取决于单片机型号,STM32 系列可以用PWR_EnterSTOPMode函数。唤醒后需要重新配置系统时钟,因为 STOP 模式会关闭主时钟。如果测量周期很长,比如几分钟一次,可以考虑用 RTC 唤醒,进一步降低功耗。
5.4 用标准距离块验证测距精度
固件写完之后,怎么验证它真的准?我一般会做三组测试:近距离、中距离、远距离。近距离用 5cm 到 20cm 的标准块,中距离用 50cm 到 100cm,远距离用 200cm 到 400cm。每个距离点测 20 次,记录平均值和标准差。如果平均值偏差超过 1%,检查声速补偿和零点校准;如果标准差超过 1cm,检查滤波参数和比较器阈值。
零点校准很重要。收发一体探头和分体探头不同,发射和接收之间的电气延迟、比较器延迟、中断响应延迟都会叠加到回波时间上,导致零点偏移。校准方法是在固定距离上测量,比如 50cm,然后调整零点偏移量,让测量值等于实际值。零点偏移量存在 Flash 里,每次上电加载。
| 测试距离 | 允许偏差 | 标准差上限 | 常见问题 |
|---|---|---|---|
| 5cm | ±0.5cm | 0.3cm | 消隐时间过长导致测不到 |
| 20cm | ±0.5cm | 0.3cm | 余震误触发导致读数偏小 |
| 50cm | ±1cm | 0.5cm | 温度补偿未开启 |
| 100cm | ±2cm | 1cm | 比较器阈值过高 |
| 200cm | ±5cm | 2cm | 放大增益不足 |
| 400cm | ±10cm | 5cm | 回波太弱,噪声触发 |
这张表是我在实际项目中总结的验收标准,不同探头和电路会有差异,但可以作为调试起点。如果某个距离点的偏差明显超出表格范围,按对应的问题排查。
做这套固件最大的教训是:不要相信手册上的标称盲区和量程,一定要自己用示波器看波形。余震拖尾、比较器延迟、温度漂移,这些在手册里通常只给一个典型值,实际电路里可能差很多。我习惯在 PCB 上留出比较器阈值的可调电阻位置,调试阶段用手调,量产时再换成固定电阻。还有一点,消隐时间不要设成固定值,不同批次的探头余震特性可能有差异,最好在产测时自动校准消隐时间,把值写进 Flash。希望帮到你。
本文还有配套的精品资源,点击获取