捕获中断实现超声波测距
前言
因为要实现一下卡尔曼滤波,所以这次先写一下超声波,顺便重温一下输入捕获中断。
一、超声波模块原理
但凡在网上搜过超声波这个模块的,对这个图都不陌生,简单的说就是想得到超声波的数据,包含以下几个步骤。
1.发送大于10us的触发信号。
2.检测超声波发出信号时产生的高电平。
3.检测超声波接收到信号时产生的低电平
而我们就是通过后两步,即去检测处因为超声波产生的高电平的时间,从而来得到距离信息的。而输入捕获的作用就是用来去捕获高电平或者低电平的持续时间的
我们的编程就是根据上图来实现的。
具体流程为:
- 设置输入捕获为上升沿检测,记录发生上升沿的时候定时器的值
- 设置输入捕获为下降沿检测,记录发生下降沿的时候定时器的值
- 根据两次记录的时间和定时器的频率,即可知道准确的高电平或低电平的持续时间
二、Cubmax配置
1.配置定时器输入捕获
本实验板子为F407,分频系数选择84-1,则计数精度达到1us。100000为溢出上限
—————————————————————————————————————————————
2.配置触发引脚
此引脚即为用来产生20us的高电平来触发超声波。
—————————————————————————————————————————————
3.开一个60ms的定时器,来在固以一定的频率触发超声波。
————————————————————————————————————————————
4.开相应的中断
三、代码
一,我们老样子,先创建一个有关超声波的参数的结构体和定义一些参数,以及结构体的初始化函数。
#define CPU_FREQUENCY_MHZ 168 // F407主频,用来写20us延时函数
#define Trig(state) HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1, (GPIO_PinState)(state)) //触发引脚
#define RELOADVALUE 100000 //重装载值
typedef struct
{
float distance; //计算出来的距离
uint8_t loop_num; //溢出次数
uint32_t rising_time; //捕获上升沿的时间
uint32_t falling_time; //捕获下降沿的时间
uint8_t capture_state; //当前捕获的状态
uint32_t time; //计算出来的时间
}Supersonic;
初始化函数,在main.c中调用
void Supersonic_Init()
{
supersonic.distance = 0;
supersonic.rising_time = 0;
supersonic.falling_time = 0;
supersonic.capture_state = 0;
supersonic.time = 0;
supersonic.loop_num = 0;
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2); //开启捕获中断
HAL_TIM_Base_Start_IT(&htim3); //开启60ms中断
__HAL_TIM_ENABLE_IT(&htim2,TIM_IT_UPDATE); //开启溢出中断
}
二,编写20us延时函数和触发函数
//延时函数,这个直接抄就行
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;
}
}
//20us的高电平
void Supersonic_Start()
{
Trig(0);
delay_us(20);
Trig(1);
}
三,编写输入捕获中断以及更新中断
//重写输入捕获中断
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == htim2.Instance)
{
if(supersonic.capture_state == 0) //如果当前状态为捕获高电平
{
supersonic.rising_time = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2); //上升沿的时间
__HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_2,TIM_INPUTCHANNELPOLARITY_FALLING); //开始捕获低电平
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2); //重新开启输入捕获中断
supersonic.capture_state = 1; //将状态改为捕获低电平
}
else if(supersonic.capture_state == 1) //如果当前状态为捕获低电平
{
supersonic.falling_time = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2); //获得下降沿的时间
__HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_RISING); //恢复为捕获高电平
HAL_TIM_IC_Stop_IT(&htim2, TIM_CHANNEL_2); //停止输入捕获,等触发信号开始再开启
supersonic.time = supersonic.falling_time + supersonic.loop_num * RELOADVALUE - supersonic.rising_time;
supersonic.distance = (float)supersonic.time*340/(2*10000); //转换为cm为单位
//清空状态
supersonic.loop_num = 0;
supersonic.capture_state = 0;
}
}
}
//在更新中断中,我们要处理溢出中断和定时器60ms中断
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == htim3.Instance) //60ms
{
if(supersonic.capture_state == 0) //此时是初始状态
{
Supersonic_Start();
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2); //记得开启捕获中断
}
}
if(htim->Instance == htim2.Instance) //溢出中断
{
if(supersonic.capture_state == 1) //此时是在捕获低电平
{
supersonic.loop_num++;
}
}
}
—————————————————————————————————————————————
实验结果
我这里将测得的距离值,打印处理做成表格。
可以看到,超声波测量过程中噪声是挺多的,这就需要我们使用一些滤波方法来进行处理。
总结
根据我们的结果,我们将使用卡尔曼滤波来处理超声波数据,来看看结果如何。
STM32 Cubemax(十) ——利用一阶卡尔曼滤波处理超声波数据