STM32控制3路超声波传感器

使用STM32定时器输入捕获模块控制3路超声波传感器
本次使用的超声波传感器是常见HC-SR04,该传感器常常使用在小型机器人和智能小车的避障系统中。
这里写图片描述
在上图中,5v和GND为模块提供电能,Trig用于触发模块测距,Echo用于接受返回电平信号。
其操作时序图如下:
这里写图片描述
如上图所示,STM32给Trig引脚一个超过10us的高电平,就可以使能模块内部的测距电路,模块会循环发出8个40kHz脉冲,发射出超声波,然后通过检测Echo引脚的高电平时间就可以测量出模块与障碍物之间的距离。其计算公式可表示如下:

d i s t a n c e = 340 E c h o 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;
    }
}

猜你喜欢

转载自blog.csdn.net/shuoyueqishilove/article/details/79463153