欢迎光临
我们一直在努力

超声波测距模块——PCA定时器驱动、温度补偿与避障实现

超声波测距在近几届蓝桥杯中几乎每年都考。这篇文章基于项目中的两版超声波驱动代码,把PCA定时器的使用、40KHz发射时序、距离计算和温度补偿全部讲透。

测距原理

超声波模块(HC-SR04)的工作方式很简单:

  • 发射端发出一串40KHz的超声波脉冲
  • 超声波碰到障碍物反射回来
  • 接收端检测到回波
  • 根据往返时间计算距离
  • 距离 = 声速 × 飞行时间 / 2
    = 340 m/s × T / 2

    硬件连接:

    • TX(发射):P1.0
    • RX(接收):P1.1

    PCA定时器

    STC15的PCA(可编程计数器阵列)本质上是一个16位定时器,非常适合超声波的微秒级计时。

    CMOD = 0x00; // PCA时钟源:Fosc/12(12T模式)
    CH = CL = 0; // 清零计数器
    CR = 1; // 启动PCA计时
    CF = 0; // PCA溢出标志

    在12MHz晶振、12T模式下,每个PCA计数值 = 1us。16位最大计数65535,对应最大计时约65ms,可测距离约11m——远超超声波的实际量程(2cm~4m)。


    超声波驱动完整代码

    项目 Driver/U_Wave.c 中的实现:

    #include <STC15F2K60S2.H>
    #include <intrins.h>

    sbit RX = P1^1; // 接收
    sbit TX = P1^0; // 发射

    /* 12us延时函数 */
    void Delay12us(void) { // @12.000MHz
    unsigned char i;
    _nop_();
    i = 3;
    while(–i);
    }

    /* 发射8个40KHz超声波脉冲 */
    void U_Wave_Init() {
    unsigned char i;
    EA = 0; // 关中断!保护发射时序
    for(i = 0; i < 8; i++) {
    TX = 1;
    Delay12us(); // 高电平12us
    TX = 0;
    Delay12us(); // 低电平12us
    }
    EA = 1;
    }

    /* 完整测距函数 */
    unsigned char U_Wave_Data() {
    unsigned long int U_Wave_Time;

    CMOD = 0x00;
    CH = CL = 0; // 清零PCA

    U_Wave_Init(); // 发射超声波

    CR = 1; // 启动PCA
    while((RX == 1) && (CF == 0)); // 等待回波或溢出
    CR = 0; // 停止PCA

    if(CF == 0) { // 未溢出 = 正常测距
    U_Wave_Time = (CH << 8) | CL;
    return U_Wave_Time * 0.017; // 距离(cm) = 时间(us) × 0.017
    } else { // 溢出 = 超出量程
    CF = 0;
    return 0;
    }
    }

    几个关键细节

    1) 为什么发射时关中断?

    40KHz超声波的周期是25us,半周期12.5us。代码用12us延时,已经非常接近。如果发射过程中Timer1中断触发,中断服务函数里会执行数码管扫描(大约需要几十微秒),这就破坏了发射脉冲的宽度,导致发出的不是40KHz的信号,测距会出错。

    2) 为什么是8个脉冲?

    8个脉冲只是经验值。理论上越多越好(接收端更容易识别),但太多会增加发射时间,而且需要精确的时序控制。8个脉冲(约192us)是硬件手册推荐的配置。

    3) 距离计算公式推导

    声速 V = 340 m/s = 0.034 cm/us
    距离 d = V × t / 2 = 0.034 × t / 2 = 0.017 × t

    其中 t 是PCA计数值(单位:us,12T模式下)
    所以 d = PCA值 × 0.017 cm

    4) 返回值类型问题

    注意 U_Wave_Data() 返回 unsigned char,最大值255。这意味着距离超过255cm的测量结果会溢出截断。第16届省赛改进了这一点:

    // 第16届省赛版:超量程返回255(区别于正常值0)
    unsigned char Sonic_Read(void) {
    unsigned int time;
    CMOD = 0x00;
    CH = CL = 0x00;
    CR = 1;
    Sonic_Init();
    while((RX == 1) && (CF == 0));
    CR = 0;
    if(CF == 0) {
    time = CH << 8 | CL;
    return (time * 0.017);
    } else {
    CF = 0;
    return 255; // 返回255表示超量程
    }
    }

    虽然返回类型仍然是 unsigned char,但用255作为超量程标志,与正常的0距离区分开了。实际使用时,如果收到255就知道这次测距失败了。


    温度补偿

    声速不是恒定的,它随温度变化:

    V(T) = 331.4 + 0.6 × T(°C)

    0°C: 331.4 m/s
    20°C: 343.4 m/s
    40°C: 355.4 m/s

    温度从0°C到40°C,声速差了约7%,对测距精度的影响非常大。

    第16届省赛的补偿公式:

    // 用DS18B20读到的温度修正测距结果
    distance = (Sonic_Read() / 340.0) * (330 + 0.6 * temperature);

    推导过程:

    超声波模块内部用固定声速340m/s计算:
    raw_distance = time × 340 / 2 = time × 0.017

    实际距离:
    real_distance = time × V(T) / 2 = time × (331.4 + 0.6T) / 2

    所以:
    real_distance = raw_distance × V(T) / 340
    = (raw_distance / 340) × (331.4 + 0.6T)
    ≈ (raw_distance / 340) × (330 + 0.6T) ← 简化版

    DAC同步输出

    第16届省赛还把测距结果映射到DAC输出(1~5V):

    void DAC_Transition(unsigned int distance) {
    if(distance <= 20)
    DA_Output(1); // 20cm以下 → 1V
    else if(distance >= 100)
    DA_Output(5); // 100cm以上 → 5V
    else
    DA_Output(((distance – 20) / 20.0) + 1); // 20~100cm线性映射
    }

    20cm→1V, 40cm→2V, 60cm→3V, 80cm→4V, 100cm→5V,线性关系。


    避障检测应用

    国赛代码中的避障逻辑:

    void Get_Distance() {
    unsigned char temp;
    temp = U_Wave_Data();
    Distance_Now = temp;

    if(Distance_Now < 30) { // 距离小于30cm
    Barrier_Clean_Flag = 0; // 有障碍物
    if(Running_Mode == 2)
    Running_Mode = 1; // 运行 → 暂停
    } else {
    Barrier_Clean_Flag = 1; // 障碍物已清除
    }
    }

    第16届省赛的运动状态检测更有意思——根据两次测距的差值判断前方物体是静止、徘徊还是跑动:

    // 根据距离变化判断运动状态
    if(D_Distance < 5)
    Temp_Motion_State = 0; // 静止
    else if(D_Distance < 10)
    Temp_Motion_State = 1; // 徘徊
    else
    Temp_Motion_State = 2; // 跑动

    // 状态变化锁定(防止频繁切换)
    // 检测到状态变化后锁定3秒,3秒内不允许再次变化

    常见问题

    问题原因解决
    始终返回0 TX和RX接反了 交换P1.0和P1.1
    读数不稳定 声波多径反射 多次测量取均值/中值
    测量偏大 声速不准 加温度补偿
    近距离(<2cm)测不到 发射和接收重叠 加入最小距离判断
    while((RX==1)&&(CF==0)) 死循环 接收引脚一直为高 检查模块连接
    赞(0)
    未经允许不得转载:171主机测评 » 超声波测距模块——PCA定时器驱动、温度补偿与避障实现
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址