STM32定时器输入捕获功能应用——超声波模块

摘要:
换而言之,单片机的工作就是给HC-SR04的Trig端发送一个一个大于10us的高电平,触发HC-SR04工作,然后利用输入捕获功能计算出HC-SR04的Echo端输入的高电平持续时间就可以测出超声波发出到返回的时间,声音在空气中的传播速度是340m/s,因此利用公式:测试距离=/2,就可以算出超声波模块与前方的物体之间的距离是多少。

一、工作原理

输入捕获是STM32单片机定时器的一项重要的功能,应用很广泛,常用于测量脉冲宽度,周期等。

超声波模块测距的原理是:单片机给超声波模块(我用到的超声波模块型号是HC-SR04,下面简称HC-SR04)发送一个大于10us的高电平,触发HC-SR04发出8个40kHz的方波,并自动检测是否有信号返回,如果有信号返回,就会通过Echo对单片机输出一个高电平,高电平的持续时间就是超声波从发射到返回的时间。

换而言之,单片机的工作就是给HC-SR04的Trig端发送一个一个大于10us的高电平,触发HC-SR04工作,然后利用输入捕获功能计算出HC-SR04的Echo端输入的高电平持续时间就可以测出超声波发出到返回的时间,声音在空气中的传播速度是340m/s,因此利用公式:测试距离=(高电平时间 * 声速)/2,就可以算出超声波模块与前方的物体之间的距离是多少。原理图如下:

STM32定时器输入捕获功能应用——超声波模块第1张

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

STM32定时器输入捕获功能应用——超声波模块第2张

STM32定时器输入捕获功能应用——超声波模块第3张

二、利用CubeMX生成驱动代码

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

1、时钟源配置:

STM32定时器输入捕获功能应用——超声波模块第4张

2、定时器

STM32定时器输入捕获功能应用——超声波模块第5张

STM32定时器输入捕获功能应用——超声波模块第6张

3、控制引脚的配置

STM32定时器输入捕获功能应用——超声波模块第7张

4、开启串口

STM32定时器输入捕获功能应用——超声波模块第8张

配置完成后生成代码。

三、修改代码

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("发送触发信号
");
}

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:%f
",temp);  
	distance = (float)(temp*0.034)/2;  //计算出距离
	printf("distant:%.2f CM
",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 *********
");
	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定时器输入捕获功能应用——超声波模块》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Web开发(F12调试)C# winform 弹出输入框下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

VUE清除组件内部定时器

  定时器如果不手动清除,只会在离开当前页面或者F5刷新后才会清除。由于vue项目是SPA应用,离开当前组件后并不会清除定时器,所以需要我们手动去清除定时器。但当我们将清除定时器clearInterval或clearTimeout写入组件的生命周期destroyed内部时并不能清除定时器,直接写window.clearXXX也并不是很好使。网上查了好久,有...

延时器 清除延时器

 // 定时器,延时器         // 定时器 : 根据时间间隔,循环往复执行程序,除非停止会一直执行程序 --有间隔的循环         // 延时器 : 根据时间间隔,延迟执行程序的时间,只会执行一次         --定时炸弹         // 语法: 时间单位 : 毫秒         // setTimeout(function(...

STM32---喜提点灯

一:编译第一个程序 intmain()  //主函数 { } voidSystemInit()  //在执行主函数前,会被调用。不进行实现。在启动文件中被调用 { } ; Reset handler Reset_Handler PROC EXPORT Reset_Handler...

AT89S52之(定时器2)Timer2

在博客园里已经发布了。。。这里在发一下。。哈哈!~ 定时计数器2是一个16位定时/计数器,通过T2CON寄存器的C/T2位可以选择定时还是计数功能。定时器2 有3种操作模式:捕获、自动重新装载(递增或递减计数)和波特率发生器,这3种模式由T2CON 中的位进行选择,具体的选择方式见表1。定时器2有两个8为寄存器:TH2和TL2。在定时模式状态下,TL2寄存...

STM32 定时器级联

根据参考手册给出的主/ 从定时器的例子 其实就是主定时器产生一个触发信号,让从定时器去接收这个触发信号,通过这个触发信号来让从定时器工作。  下面我们来看看我设置的从定时器 只需要配置 TIMx->SMCR 的TS[2:0] (Trigger selection)   选择是哪一个触发信号来触发从定时器工作 和位 SMS[2:0](Slave...

STM32 ------ HardFault_Hander 中断函数

1、数组越界(对数组赋值) 正常情况,数组越界会进入 HardFault_Hander 中断函数的无线循环。 避免数组越界的一个方法是:每次使用数组前,检查要放入数据的数据长度是否大于数组长度,大于则进入while(1),方便定位,如果可以打印信息更好。  2、定义的局部数组过大,超过 IAR 设置的栈大小(未使用FreeRTOS) 单步运行时,刚执行完定...