超声测距

超声测距模块 HC-SR04P。工作电压3-5V,有效距离2-400cm,分辨率1mm。注意型号末尾的“P”:有另一个外观、接口、工作方式一样的型号 HC-SR04,但工作电压为5V。我这个模块 PCB正面印刷的是HC-SR04,反面印刷 HC-SR04P:

有4个引脚,VCC/GND供电,TRIG触发测距,ECHO接收测量结果。向TRIG给一个10us以上(规格书建议40-50us)的正脉冲触发信号,模块开始工作,发射超声波并接收回声。从发射到接收到回声的时间,以回响电平的形式从ECHO输出;电平持续时间等于声波往返模块到障碍物的时间:

 温下,声音在空气中的传播速度约343m/s = 3.43mm/10us = .8575mm/2.5us。初步想法是,启动一个Timer作为时间基,计数频率 2.5us = 400kHz,以满足10us触发信号延时和1mm测量分辨率的需求。接口方式,触发信号用GPIO output,回响信号用GPIO外部中断。如下图,回响信号接PC7(Arduino兼容Pin D9),其EXTI line编号为7:

使用基本Timer TIM6,时钟源为PCLK1 x 2=72MHz。设PSC=179,得计数频率 CK_CNT = 72MHz / (1+179) = 400kHz。设 ARR = 63999,则 UEV 频率为 400kHz /(1+63999) = 6.25Hz = 160ms。

计时及有关工具函数实现如下。全局变量 _App_timestamp 记录时间戳,在TIM6 UEV 中断回调函数中更新,因此其精度为160ms。获取当前时间戳时,将_App_timestamp 与 TIM6 CNT寄存器值相加,可达到 2.5us 精度:

typedef struct {
    uint32_t milli;
    uint16_t micro;
} _App_Timestamp;

volatile _App_Timestamp _App_timestamp = { 0, 0 };

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if (&htim6 == htim) {
        _App_timestamp.milli += 160; // 160 ms/tick
    }
}

#define _App_GetDuration(from, to)  (           \
        1000 * ((to).milli - (from).milli)    \
        + (to).micro - (from).micro           \
        )

void _App_GetTimestamp(_App_Timestamp *ts) {
    *ts = _App_timestamp;
    uint32_t micro = 5 * htim6.Instance->CNT / 2;
    ts->milli += (micro / 1000);
    ts->micro += (micro % 1000);
}

void _App_DelayMicro(uint32_t micro) {
    _App_Timestamp t1, t2;
    _App_GetTimestamp(&t1);
    for (;;) {
        _App_GetTimestamp(&t2);
        if (_App_GetDuration(t1, t2) >= micro) {
            break;
        }
    }
}

ECHO Pin启用外部中断,在中断回调函数内记录上升沿和下降沿的时间戳:

typedef struct {
    uint8_t state;
    uint32_t duration; // in us
    _App_Timestamp _risingTs;
} _App_Measure;

volatile _App_Measure _App_measure = { 0 };

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (D9_ECHO_Pin == GPIO_Pin) {
        if (GPIO_PIN_SET == HAL_GPIO_ReadPin(D9_ECHO_GPIO_Port, D9_ECHO_Pin)) {
            // Rising edge
            _App_Timestamp now;
            _App_GetTimestamp(&now);
            _App_measure._risingTs = now;
            _App_measure.state = 0;
        } else {
            // Falling edge
            _App_Timestamp now;
            _App_GetTimestamp(&now);
            _App_measure.duration = _App_GetDuration(_App_measure._risingTs,
                    now);
            _App_measure.state = 1;
        }
    }
}

 

主循环如下。首先发送TRIG脉冲,时间设为20us。距离的计算注意将声波往返距离除以 2 才是到障碍物的距离:

void App_Loop() {

    _App_Trig(20);

    HAL_Delay(500);

    if (_App_measure.state) {
        uint32_t dur = _App_measure.duration;
        uint32_t dist = (uint32_t) (.343f * dur / 2);

        if (dist > 25 && dist < 5000) {
            char msg[80];
            sprintf(msg, "%d mm\n", (int) dist);
            HAL_UART_Transmit(&huart2, (uint8_t *) msg, strlen(msg),
            HAL_MAX_DELAY);
        }
    }
}

void _App_Trig(uint32_t micro) {
    HAL_GPIO_WritePin(D8_TRIG_GPIO_Port, D8_TRIG_Pin, GPIO_PIN_SET);
    _App_DelayMicro(micro);
    HAL_GPIO_WritePin(D8_TRIG_GPIO_Port, D8_TRIG_Pin, GPIO_PIN_RESET);
}

测得的距离从串口 USART2 输出,单位为mm:

  

posted @ 2018-01-20 19:58  vinccc  阅读(825)  评论(0编辑  收藏  举报