超聲測距模塊 HC-SR04P。工作電壓3-5V,有效距離2-400cm,解析度1mm。註意型號末尾的“P”:有另一個外觀、介面、工作方式一樣的型號 HC-SR04,但工作電壓為5V。我這個模塊 PCB正面印刷的是HC-SR04,反面印刷 HC-SR04P: 有4個引腳,VCC/GND供電,TRIG ...
超聲測距模塊 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: