STM32定时器输入捕获功能应用——超声波模块
2025-08-22 来源:cnblogs
一、工作原理
输入捕获是STM32单片机定时器的一项重要的功能,应用很广泛,常用于测量脉冲宽度,周期等。
超声波模块测距的原理是:单片机给超声波模块(我用到的超声波模块型号是HC-SR04,下面简称HC-SR04)发送一个大于10us的高电平,触发HC-SR04发出8个40kHz的方波,并自动检测是否有信号返回,如果有信号返回,就会通过Echo对单片机输出一个高电平,高电平的持续时间就是超声波从发射到返回的时间。
换而言之,单片机的工作就是给HC-SR04的Trig端发送一个一个大于10us的高电平,触发HC-SR04工作,然后利用输入捕获功能计算出HC-SR04的Echo端输入的高电平持续时间就可以测出超声波发出到返回的时间,声音在空气中的传播速度是340m/s,因此利用公式:测试距离=(高电平时间 * 声速)/2,就可以算出超声波模块与前方的物体之间的距离是多少。原理图如下:

用一个简图来说明输入捕获测量高电平延续时间的实现原理:


二、利用CubeMX生成驱动代码
HC-SR04上有4个引脚:VCC(5V)、GND、Trig(控制端)、Echo(接收端),所以需要配置一个GPIO作为控制HC-SR04的引脚,Echo这个引脚在配置定时器的时候就会自动配置好,不需要单独配置。另外还需要配置一个串口作为打印口方便调试。
1、时钟源配置:

2、定时器


3、控制引脚的配置

4、开启串口

配置完成后生成代码。
三、修改代码
1、写一个us级别的延时:
/********************************************
*函数名称:void delay_us(__IO uint32_t delay)
*函数形参:__IO uint32_t delay--延时时间
*函数返回值:无
*函数功能:微秒级别延时
*********************************************/
void delay_us(__IO uint32_t delay)
{
int last, curr, val;
int temp;
while (delay != 0)
{
temp = delay > 900 ? 900 : delay;
last = SysTick->VAL;
curr = last - CPU_FREQUENCY_MHZ * temp;
if (curr >= 0)
{
do
{
val = SysTick->VAL;
}
while ((val < last) && (val >= curr));
}
else
{
curr += CPU_FREQUENCY_MHZ * 1000;
do
{
val = SysTick->VAL;
}
while ((val <= last) || (val > curr));
}
delay -= temp;
}
}
2、重定向
/********************************************
*函数名称:int fputc(int ch, FILE* stream)
*函数功能:重定向
*********************************************/
int fputc(int ch, FILE* stream)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
return ch;
}
3、触发信号
/********************************************
*函数名称:void Trigger_Signal(__IO uint32_t us)
*函数形参:__IO uint32_t us--触发信号保持时间
*函数返回值:无
*函数功能:触发信号
*********************************************/
void Trigger_Signal(__IO uint32_t us)
{
HAL_GPIO_WritePin(Trigger_GPIO_Port,Trigger_Pin,GPIO_PIN_SET);
delay_us(us);
HAL_GPIO_WritePin(Trigger_GPIO_Port,Trigger_Pin,GPIO_PIN_RESET);
printf('发送触发信号rn');
}
4、 输入捕获中断回调函数:
/********************************************
*函数名称:void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
*函数形参:TIM_HandleTypeDef *htim--定时器句柄
*函数返回值:无
*函数功能:捕获到高电平后,计数器清零,配置为低电平捕获,
当捕获到低电平时,读出CCR的值
*备注:TIM2_CH2_CAPTURE_STA这个是uint8_t数据类型的全局变量,
它的每一位数据可以自定义为某些状态,在这里,第7位为1表示捕获完成,为0表示未完成,
第6位为1表示捕获到上升沿,为0表示未捕获到高电平,0~5bit保留;
TIM2_CH2_ELAPSED_CNT也是一个全局变量,表示从捕获到高电平起,计数器溢出的次数,
TIM2_CH2_CAPTURE_VAL也是一个全局变量,当捕获到下降沿后把CCR2的值读取到这个变量里
*
*********************************************/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if((TIM2_CH2_CAPTURE_STA&0x80) != 0x80)//还未完成捕获
{
if((TIM2_CH2_CAPTURE_STA&0x40) == 0) //此前尚未捕获到上升沿,那么这次捕获到的就是上升沿
{
TIM2_CH2_CAPTURE_VAL = 0; //清零,防止干扰
TIM2_CH2_ELAPSED_CNT = 0; //清零,捕获到上升沿后重新计算周期溢出次数
TIM2_CH2_CAPTURE_STA |= 0x40; //捕获到一个上升沿
__HAL_TIM_DISABLE(&htim2); //停止TIM2
__HAL_TIM_SET_COUNTER(&htim2,0); //把TIM2的计数器清零
TIM_RESET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_2); //清除原来的设置
TIM_SET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_2,TIM_ICPOLARITY_FALLING); //将TIM2的通道2输入捕获设置为下降沿捕获
__HAL_TIM_ENABLE(&htim2); //使能TIM2
}
else
{
TIM2_CH2_CAPTURE_STA |= 0x80; //捕获到一个下降沿,代表捕获完成
TIM2_CH2_CAPTURE_VAL = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2); //把此时CCR2的值读到变量TIM2_CH2_CAPTURE_VAL
TIM_RESET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_2); //清除原来的设置
TIM_SET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_2,TIM_ICPOLARITY_RISING); //设置为上升沿捕获
}
}
}
5、计数周期溢出中断回调函数:
/********************************************
*函数名称:void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
*函数形参:TIM_HandleTypeDef *htim--定时器句柄
*函数返回值:无
*函数功能:定时器溢出次数计算
*********************************************/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
TIM2_CH2_ELAPSED_CNT++; //每次溢出,该变量增加1
}
6、计算高电平的持续时间:
/********************************************
*函数名称:uint32_t CalculatePulseWide(void)
*函数形参:无
*函数返回值:无
*函数功能:计算出高电平的宽度
*********************************************/
uint32_t CalculatePulseWide(void)
{
uint32_t PulseWide = 0;
if((TIM2_CH2_CAPTURE_STA&0x80) == 0x80)
{
PulseWide = 0xffff*TIM2_CH2_ELAPSED_CNT+TIM2_CH2_CAPTURE_VAL;
TIM2_CH2_CAPTURE_STA = 0; //计算完将该变量清零,其实即使不清零应该也没关系,每次捕获到上升沿也会清零
TIM2_CH2_ELAPSED_CNT = 0; //计算完将该变量清零,其实即使不清零应该也没关系,每次捕获到上升沿也会清零
}
return PulseWide;
}
7、计算距离:
/********************************************
*函数名称:void GetDistance(void)
*函数形参:无
*函数返回值:无
*函数功能:获取距离
*********************************************/
void GetDistance(void)
{
float temp = 0;
float distance = 0;
Trigger_Signal(20); //发送触发信号,因为要大于10us,这里就设置为20us
while(!temp) //等待计算出高电平的时间,如果temp为0,说明还未计算出来,继续等待
{
temp = CalculatePulseWide();
}
printf('temp:%frn',temp);
distance = (float)(temp*0.034)/2; //计算出距离
printf('distant:%.2f CMrn',distance);
HAL_Delay(100); //手册中说明两次测量的时间间距最好大于60ms,避免引起干扰,这里取100ms
}
8、主函数
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf('********** HC-RS04 *********rn');
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2); //开启输入捕获中断
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
GetDistance(); //计算出距离
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
完成代码修改后,烧录到单片机上,进行了测量,发现测得的距离跟实际距离之间的误差不大,当实际距离等于1米的时候,误差大概在1~5cm左右波动。
- 意法半导体中国本地造STM32微控制器启动规模量产
- 意法半导体全新STM32C5系列,重新定义入门级微控制器性能与价值,赋能万千智能设备
- 使用 Keil Studio for Visual Studio Code开发 STM32 设备
- 基于机智云与STM32的智能拐杖安全监测系统在养老物联网中的应用
- 内置全栈安全,一站式满足CRA法案与IEC 62443标准——米尔STM32MP257核心板
- 如何用 STM32 FLASH 实现等效 100 万次擦写的 EEPROM 功能?
- 实战解析:通过一个小项目掌握STM32所有外设
- STM32学了两年半,却还是不会做项目
- 意法半导体推出最新STM32MP21微处理器,兼具高性价比、低功耗、高灵活性
- 基于STM32的矿井作业环境监测系统设计与实现
- 六大全新产品系列推出,MCX A微控制器家族迎来创新
- 意法半导体全新STM32C5系列,重新定义入门级微控制器性能与价值,赋能万千智能设备
- 模组复用与整机重测在SRRC、CCC、CTA/NAL认证中的实践操作指南
- 有源晶振与无源晶振的六大区别详解
- 从控制到系统:TI利用边缘AI重塑嵌入式MCU的边界
- 英飞凌持续巩固全球微控制器市场领导地位
- 使用 Keil Studio for Visual Studio Code开发 STM32 设备
- 蓝牙信道探测技术原理与开发套件实践
- Microchip 推出生产就绪型全栈边缘 AI 解决方案,赋能MCU和MPU实现 智能实时决策
- LoRa、LoRaWAN、NB-IoT与4G DTU技术对比及工业无线方案选型分析




