天天看點

STM32控制3路超音波傳感器

使用STM32定時器輸入捕獲子產品控制3路超音波傳感器

本次使用的超音波傳感器是常見HC-SR04,該傳感器常常使用在小型機器人和智能小車的避障系統中。

STM32控制3路超音波傳感器

在上圖中,5v和GND為子產品提供電能,Trig用于觸發子產品測距,Echo用于接受傳回電平信号。

其操作時序圖如下:

STM32控制3路超音波傳感器

如上圖所示,STM32給Trig引腳一個超過10us的高電平,就可以使能子產品内部的測距電路,子產品會循環發出8個40kHz脈沖,發射出超音波,然後通過檢測Echo引腳的高電平時間就可以測量出子產品與障礙物之間的距離。其計算公式可表示如下:

distance=340∗Echo高電平時間2 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 = ; //傳感器最大探測距離4000mm,一般不會溢出
    TIM_TimeBaseStructure.TIM_Prescaler =-;  //設定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 = ;
  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 = ;
  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 = ;
  TIM_ICInit(TIM2, &TIM_ICInitStructure);

    //設定輸入捕獲中斷
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = ;     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=;
}
           

值得注意的是,這裡的輸入捕獲的邊緣極性設定為上升和下降沿都捕獲,效率比較高,也有大神開始使用上升沿捕獲,然後在中斷中将捕獲記性設定為下降沿捕獲,我試過這種辦法,程式容易卡死,是以沒有采用。

中斷程式可以這樣編寫:

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%==)//檢測到了上升電平
         {
              TIM2->CNT=;//計數器清零
         }
         else//檢測到了下降沿電平,讀取距離值
         {
             dist.cnt=TIM2->CNT;
             TIM2->CNT=;
             dist.f_distance=(dist.overflow*+dist.cnt)*f;
             printf("F_dis=%.2fmm\n",dist.f_distance);
             dist.overflow=;
         }
         if(cnt_f>)cnt_f=;
         TIM_ClearITPendingBit(TIM2,  TIM_IT_CC2);
     }

     if(TIM_GetITStatus(TIM2, TIM_IT_CC3)!=RESET)
     {
       cnt_l++;
         if(cnt_l%==)//檢測到了上升電平
         {
              TIM2->CNT=;
         }
         else
         {
             dist.cnt=TIM2->CNT;
             TIM2->CNT=;
             dist.l_distance=(dist.overflow*+dist.cnt)*f;
             printf("L_dis=%.2fmm\n",dist.l_distance);
             dist.overflow=;
         }
         if(cnt_l>)cnt_l=;
         TIM_ClearITPendingBit(TIM2,  TIM_IT_CC3);
     }

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

這樣設計的好處是中斷程式設計簡單,可以保證明時性。

觸發子產品進行測量,通過給子產品的Trig引腳一個超過10us高電平信号就可以觸發子產品進行距離測量,程式代碼如下:

/*
function:啟動距離測量
choice: :左邊超音波子產品
        :中間超音波子產品
        :右邊超音波子產品
*/
void Get_Distance(uint8_t choice)
{
    switch(choice)
    {
      case :
       GPIO_ResetBits(GPIOA,GPIO_Pin_4);
         GPIO_SetBits(GPIOA,GPIO_Pin_4);
         Delay_us();
         GPIO_ResetBits(GPIOA,GPIO_Pin_4);  
       break;
    case :
             GPIO_ResetBits(GPIOA,GPIO_Pin_5);
         GPIO_SetBits(GPIOA,GPIO_Pin_5);
         Delay_us();
         GPIO_ResetBits(GPIOA,GPIO_Pin_5);  
       break;
        case :
             GPIO_ResetBits(GPIOA,GPIO_Pin_6);
         GPIO_SetBits(GPIOA,GPIO_Pin_6);
         Delay_us();
         GPIO_ResetBits(GPIOA,GPIO_Pin_6);  
       break;
    }
}
           

繼續閱讀