您的位置:首页 > 其它

STM32控制3路超声波传感器

2018-03-06 20:17 555 查看
使用STM32定时器输入捕获模块控制3路超声波传感器

本次使用的超声波传感器是常见HC-SR04,该传感器常常使用在小型机器人和智能小车的避障系统中。



在上图中,5v和GND为模块提供电能,Trig用于触发模块测距,Echo用于接受返回电平信号。

其操作时序图如下:



如上图所示,STM32给Trig引脚一个超过10us的高电平,就可以使能模块内部的测距电路,模块会循环发出8个40kHz脉冲,发射出超声波,然后通过检测Echo引脚的高电平时间就可以测量出模块与障碍物之间的距离。其计算公式可表示如下:

distance=340∗Echo高电平时间2distance=340∗Echo高电平时间2

模块驱动程序如下:

void HC_SR04_Init(void)
{
/*初始化GPIO*/
GPIO_InitTypeDef  GPIO_InitStructure;
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
NVIC_InitTypeDef         NVIC_InitStructure;
TIM_ICInitTypeDef        TIM_ICInitStructure;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//使能定时器2

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6; //Trig:PA4,PA5,PA6
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3; //Echo: PA2,PA3对应TIM2的通道3,4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//打开引脚复用
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_TIM2);
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//Enable GPIOB's Clock
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PB3 refers to TIM2's channel 2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_TIM2);
GPIO_Init(GPIOB, &GPIO_InitStructure);//initialize GPIOB

/初始化TIM2*/
TIM_TimeBaseStructure.TIM_Period = 0xffffffff; //传感器最大探测距离4000mm,一般不会溢出
TIM_TimeBaseStructure.TIM_Prescaler =84-1;  //设置TIM2时钟频率为1MHz
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

//初始化TIM2输入捕获

TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge; //上升和下降都触发输入捕获
TIM_ICInitStructure.TIM_ICSelection =  TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x00;
TIM_ICInit(TIM2, &TIM_ICInitStructure);

TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge;
TIM_ICInitStructure.TIM_ICSelection =  TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x00;
TIM_ICInit(TIM2, &TIM_ICInitStructure);

TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge;
TIM_ICInitStructure.TIM_ICSelection =  TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x00;
TIM_ICInit(TIM2, &TIM_ICInitStructure);

//设置输入捕获中断
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4,ENABLE);//使能中断
TIM_Cmd(TIM2,ENABLE);
dist.overflow=0;
}


值得注意的是,这里的输入捕获的边缘极性设定为上升和下降沿都捕获,效率比较高,也有大神开始使用上升沿捕获,然后在中断中将捕获记性设定为下降沿捕获,我试过这种办法,程序容易卡死,所以没有采用。

中断程序可以这样编写:

void TIM2_IRQHandler(void)
{
static uint16_t cnt_f,cnt_l,cnt_r;

if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
{
dist.overflow++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
//       printf("overflow\n");
}

if(TIM_GetITStatus(TIM2, TIM_IT_CC2)!=RESET)
{
cnt_f++;
if(cnt_f%2==1)//检测到了上升电平
{
TIM2->CNT=0;//计数器清零
}
else//检测到了下降沿电平,读取距离值
{
dist.cnt=TIM2->CNT;
TIM2->CNT=0;
dist.f_distance=(dist.overflow*0xffffffff+dist.cnt)*0.17f;
printf("F_dis=%.2fmm\n",dist.f_distance);
dist.overflow=0;
}
if(cnt_f>65535)cnt_f=0;
TIM_ClearITPendingBit(TIM2,  TIM_IT_CC2);
}

if(TIM_GetITStatus(TIM2, TIM_IT_CC3)!=RESET)
{
cnt_l++;
if(cnt_l%2==1)//检测到了上升电平
{
TIM2->CNT=0;
}
else
{
dist.cnt=TIM2->CNT;
TIM2->CNT=0;
dist.l_distance=(dist.overflow*0xffffffff+dist.cnt)*0.17f;
printf("L_dis=%.2fmm\n",dist.l_distance);
dist.overflow=0;
}
if(cnt_l>65535)cnt_l=0;
TIM_ClearITPendingBit(TIM2,  TIM_IT_CC3);
}

if(TIM_GetITStatus(TIM2, TIM_IT_CC4)!=RESET)
{
cnt_r++;
if(cnt_r%2==1)
{
TIM2->CNT=0;
}
else
{
dist.cnt=TIM2->CNT;
TIM2->CNT=0;
dist.r_distance=(dist.overflow*0xffffffff+dist.cnt)*0.17f;
printf("R_dis=%.2fmm\n",dist.r_distance);
dist.overflow=0;
}
if(cnt_r>65535)cnt_r=0;
TIM_ClearITPendingBit(TIM2,  TIM_IT_CC4);
}
}


这样设计的好处是中断程序设计简单,可以保证实时性。

触发模块进行测量,通过给模块的Trig引脚一个超过10us高电平信号就可以触发模块进行距离测量,程序代码如下:

/*
function:启动距离测量
choice: 0:左边超声波模块
1:中间超声波模块
2:右边超声波模块
*/
void Get_Distance(uint8_t choice)
{
switch(choice)
{
case 0:
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
GPIO_SetBits(GPIOA,GPIO_Pin_4);
Delay_us(15);
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
break;
case 1:
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
GPIO_SetBits(GPIOA,GPIO_Pin_5);
Delay_us(15);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
break;
case 2:
GPIO_ResetBits(GPIOA,GPIO_Pin_6);
GPIO_SetBits(GPIOA,GPIO_Pin_6);
Delay_us(15);
GPIO_ResetBits(GPIOA,GPIO_Pin_6);
break;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息