一、基本原理
1.1. 引脚功能
VCC | 电源 (需要5V直流电源) |
GND | 地 |
Trig | 信号触发脉冲 (>10us TTL脉冲) |
Echo | 输出TTL电平信号, 电平持续时间与距离程正比 |
1.2. 电气特性
工作电压 | 5V |
工作电流 | 15mA |
工作频率 | 40kHz |
射程范围 | 0.02 ~ 4m (参考值,不同厂家质量可能也不一样) |
测量精度 | 1mm (理论值,也跟代码实现有关系) |
测量角度 | 15° |
1.3. 测距原理
HC-SR04超声波测距模块提供2cm~400cm的测距功能,精度达3mm。
外部通过Trig引脚提供一个持续时间 >10us的TTL脉冲,内部则产生8个40kHz的模拟声波信号输出,并通过Echo引脚输出回响TTL电平,电平在接收到回响信号后无效,通过Echo引脚TTL电平的持续时间,以及声波的传输速度,即可估算出器件与物体间的距离。公式如下:
其中,Tttl为高电平持续时间,Vacoustic为声波理论速度340m/s 。
1.4. 代码思路
- Trig引脚的TTL电平输出:可以使用普通的GPIO口,配合延时函数进行输出。
- Echo引脚的回响电平接收:由于需要计算回响电平持续时间,因此使用EXTI中断的边沿触发模式,配合定时器中断计数。在上升沿时获取定时器时间戳t1,在下降沿时再次获取定时器时间戳t2,(t2-t1)*时间戳粒度 即为回响电平持续时间,即可得到距离。
二、驱动代码
2.1. IO配置代码
#define HCSR04_TRIG_GPIO_PORT GPIOA
#define HCSR04_TRIG_GPIO_MODE GPIO_MODE_OUTPUT_PP
#define HCSR04_TRIG_GPIO_PIN GPIO_PIN_12
#define HCSR04_TRIG_GPIO_PULL GPIO_NOPULL
#define HCSR04_TRIG_GPIO_SPEED GPIO_SPEED_FREQ_HIGH
#define HCSR04_TRIG_HIGH() HAL_GPIO_WritePin(HCSR04_TRIG_GPIO_PORT, HCSR04_TRIG_GPIO_PIN, GPIO_PIN_SET)
#define HCSR04_TRIG_LOW() HAL_GPIO_WritePin(HCSR04_TRIG_GPIO_PORT, HCSR04_TRIG_GPIO_PIN, GPIO_PIN_RESET)#define HCSR04_ECHO_GPIO_PORT GPIOB
#define HCSR04_ECHO_GPIO_MODE GPIO_MODE_IT_RISING_FALLING
#define HCSR04_ECHO_GPIO_PIN GPIO_PIN_1
#define HCSR04_ECHO_GPIO_PULL GPIO_NOPULL
#define HCSR04_ECHO_GPIO_SPEED GPIO_SPEED_FREQ_HIGH
#define HCSR04_ECHO_GPIO_READ() HAL_GPIO_ReadPin(HCSR04_ECHO_GPIO_PORT, HCSR04_ECHO_GPIO_PIN) #include"gpioM.h"void GpioInit(void);void GpioInit(void)
{GPIO_InitTypeDef tGpioInit;/* -1- Enable GPIO Clock (to be able to program the configuration registers) */__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();tGpioInit.Mode = HCSR04_TRIG_GPIO_MODE;tGpioInit.Pull = HCSR04_TRIG_GPIO_PULL;tGpioInit.Speed = HCSR04_TRIG_GPIO_SPEED;tGpioInit.Pin = HCSR04_TRIG_GPIO_PIN;HAL_GPIO_Init(HCSR04_TRIG_GPIO_PORT, &tGpioInit);tGpioInit.Mode = HCSR04_ECHO_GPIO_MODE;tGpioInit.Pull = HCSR04_ECHO_GPIO_PULL;tGpioInit.Speed = HCSR04_ECHO_GPIO_SPEED;tGpioInit.Pin = HCSR04_ECHO_GPIO_PIN;HAL_GPIO_Init(HCSR04_ECHO_GPIO_PORT, &tGpioInit);return ;
}
2.2. EXTI中断配置和处理代码
#include "stm32f1xx_hal_cortex.h"#define HCSR04_EXTI_PRIORITY 1u
#define HCSR04_EXTI_SUBPRIORITY 0u
#define HCSR04_EXTI_IRQ EXTI1_IRQnfloat hcsr40Distance = 0.0f;void ExtiInit(void);
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
void Hcsr04_IRQHandle(void);void ExtiInit(void)
{/* 配置 EXTI 中断源 到key1 引脚、配置中断优先级*/HAL_NVIC_SetPriority(HCSR04_EXTI_IRQ, HCSR04_EXTI_PRIORITY, HCSR04_EXTI_SUBPRIORITY);/* 使能中断 */HAL_NVIC_EnableIRQ(HCSR04_EXTI_IRQ);}void EXTI1_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(HCSR04_ECHO_GPIO_PIN);
}/*外部中断回调函数*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{ if(GPIO_Pin == HCSR04_ECHO_GPIO_PIN){Hcsr04_IRQHandle();}
}void Hcsr04_IRQHandle(void)
{static unsigned char flag = 0;static unsigned int startTime100Us = 0;static unsigned int endTime100Us = 0;if (0 == flag) // 上升沿{startTime100Us = timer100UsCounter;flag = 1;}else // 下降沿{endTime100Us = timer100UsCounter;if (endTime100Us >= startTime100Us){// 距离 = 高电平时长 * 声波速度 / 2hcsr40Distance = 1.0 * (endTime100Us - startTime100Us) * (ACOUSTIC_VELOCITY >> 1);}else{hcsr40Distance = 1.0 * (0xFFFFFFFF - startTime100Us + endTime100Us) * (ACOUSTIC_VELOCITY >> 1);}flag = 0;}
}
2.3. 定时器中断配置和处理代码
#include "stm32f1xx_hal_dma.h"
#include "stm32f1xx_hal_tim.h"
#include "stm32f1xx_hal_rcc.h"
#include "stm32f1xx_hal_cortex.h"#define HCSR04_TIMER_PRIORITY 2u
#define HCSR04_TIMER_SUBPRIORITY 0u
#define HCSR04_TIMER_IRQ TIM2_IRQn#define HCSR04_COUNTER_TIMER TIM2
#define HCSR04_COUNTER_TIMER_PRESCALER (64-1)
#define HCSR04_COUNTER_TIMER_PERIOD_VALUE (100-1)
#define HCSR04_COUNTER_TIMER_CLK_ENABLE() __HAL_RCC_TIM2_CLK_ENABLE()volatile unsigned int timer100UsCounter = 0;void HCSR04_Timer_Init(void)
{// Timer2计数中断HAL_NVIC_SetPriority(HCSR04_TIMER_IRQ, HCSR04_TIMER_PRIORITY, HCSR04_TIMER_SUBPRIORITY);HAL_NVIC_EnableIRQ(HCSR04_TIMER_IRQ);HCSR04_COUNTER_TIMER_CLK_ENABLE();hcsr04TimHandle.Instance = HCSR04_COUNTER_TIMER;hcsr04TimHandle.Init.Prescaler = HCSR04_COUNTER_TIMER_PRESCALER;hcsr04TimHandle.Init.Period = HCSR04_COUNTER_TIMER_PERIOD_VALUE;hcsr04TimHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;hcsr04TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;hcsr04TimHandle.Init.RepetitionCounter = 0;hcsr04TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&hcsr04TimHandle) != HAL_OK){/* Initialization Error */Error_Handler("HCSR40 TIM Init Err0.");}// 使能定时器3更新中断并启动定时器HAL_TIM_Base_Start_IT(&hcsr04TimHandle);// 清除定时器初始化过程中的更新中断标志,避免定时器一启动就中断 (这句一定要在Start_IT后面!!!!)__HAL_TIM_CLEAR_IT(&hcsr04TimHandle, TIM_IT_UPDATE);
}void TIM2_IRQHandler(void)
{// HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_GPIO_PIN);HAL_TIM_IRQHandler(&hcsr04TimHandle);
}// 定时器更新中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{// HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_GPIO_PIN);if(htim->Instance == HCSR04_COUNTER_TIMER){timer100UsCounter++;}
}
2.4. HCSR04功能驱动代码
extern float hcsr40Distance;void Hcsr04_Trig(void);
void Hcsr04_Show(void);
float Hcsr04_GetDistance(void);void Hcsr04_Trig(void)
{HCSR04_TRIG_HIGH();Delay_us(15);HCSR04_TRIG_LOW();
}float Hcsr04_GetDistance(void)
{float totalDistance = 0.0f;unsigned char time = GET_DISTANCE_TIME;// 求均值while (time > 0){Hcsr04_Trig();Delay_ms(65);totalDistance += hcsr40Distance;time--;}totalDistance /= GET_DISTANCE_TIME;return totalDistance;
}void Hcsr04_Show(void)
{char distance[20];float nowDistance = 0.0f;nowDistance = Hcsr04_GetDistance();sprintf(distance,"dis: %.2f mm",nowDistance);SSD1306_WriteStr(distance, strlen(distance));
}