超声波测距听起来高大上,其实原理特别接地气。就像你在山谷里喊一嗓子,听到回声就能估算距离一样。模块通过TX引脚发射40kHz的超声波(这个频率人耳听不见),遇到障碍物反射后被RX引脚接收。关键点在于计算"喊话"到"听到回声"的时间差。
在CT107D开发板上,硬件连接简单到哭:
我实测时发现个坑:超声波模块最好离开发板边缘5cm以上,否则电路板本身可能反射超声波导致误判。有次调试半天发现距离永远显示30cm,原来是被开发板外壳反弹了。
要让TX引脚准确发出40kHz信号,需要精确控制高低电平时间。1个周期25μs,所以高低电平各12.5μs。但STC15的指令周期是1μs(12MHz时钟),直接写延时函数会有误差。
这是我的优化方案:
c复制void delay12us() {
_nop_(); // 消耗1μs
_nop_(); // 再1μs
unsigned char i = 33;
while(--i); // 精确补足剩余10μs
}
发送8个周期的完整代码:
c复制void send_wave() {
unsigned char i;
for(i=0; i<8; i++) {
TX = 1;
delay12us();
TX = 0;
delay12us();
}
}
注意:实际比赛时建议提前用示波器校准,我遇到过因晶振个体差异导致实际频率38kHz的情况。
定时器配置是核心难点,建议用模式0(13位计数):
c复制TMOD &= 0x0f; // 清零高4位
TH1 = 0x00; // 初始值0
TL1 = 0x00;
计算距离时要注意三个细节:
V=332+0.607t,但比赛时通常简化用344m/s我的计算公式经过实测优化:
c复制distance = ((time/10)*17)/100 + 3;
这个魔数172的来历:344m/s ÷ 2 × 100(转cm)÷ 1000000(μs转s)≈ 0.0172cm/μs
三位数码管显示有讲究,要解决两个问题:
我的显示函数这样写:
c复制void show_hc(unsigned char pos, unsigned char value) {
XBYTE[0xE000] = 0xFF; // 先消隐
XBYTE[0xC000] = 0x01 << pos;
XBYTE[0xE000] = value;
}
动态刷新时有个坑:测量函数执行时间不能太长,否则数码管会闪烁。建议在主循环中这样安排:
c复制while(1) {
measure_distance();
Delay(10); // 这个函数内部包含显示刷新
}
踩过无数坑后总结的必查清单:
有个骚操作:用蜂鸣器当调试工具。在关键代码处加:
c复制XBYTE[0xA000] = 0x40; // 蜂鸣器响
delay_ms(50);
XBYTE[0xA000] = 0x00; // 关闭
这样通过声音就能知道程序执行到哪一步了。
整个项目应该包含这些模块:
关键点在于变量设计:
c复制unsigned int distance = 0; // 全局变量存储距离
bit measure_flag = 0; // 测量状态标志
中断服务函数里记得重装定时器初值:
c复制void timer1_isr() interrupt 3 {
TF1 = 0; // 必须手动清除标志
TH1 = 0x00;
TL1 = 0x00;
}
要让测量更稳定,可以尝试:
我的稳定性优化代码:
c复制unsigned int stable_measure() {
unsigned int buf[5];
for(int i=0; i<5; i++) {
measure_distance();
buf[i] = distance;
delay_ms(30);
}
// 排序取中值
bubble_sort(buf);
return buf[2];
}
这个方案在蓝桥杯比赛中实测误差能控制在±1cm内。