基于STM32的智能循迹避障小车实验——超声波部分详解

接上一篇基于STM32的智能循迹避障小车实验(舵机旋转部分)

最后这部分我们实现超声波部分和最后代码的整合

实验所用器件及其介绍

本部分实验采用的是超声波模块HC-SR04,它长这样:

 

买这个的时候最好再买一个支架,可以直接架在舵机上,探查周围的距离。

超声波模块有4个引脚,分别为Vcc、 Trig(控制端)、 Echo(接收端)、 GND;其中VCC、GND接上电源和地, Trig(控制端)控制发出的超声波信号,Echo(接收端)接收反射回来的超声波信号。

控制端的控制原理:通过Trig引脚发一个 10US 以上的高电平,就可以在Echo接收口等待高电平输出;一有输出就可以开定时器计时,当此口变为低电平时就可以读定时器的值,此时就为此次测距的时间,方可算出距离.如此不断的周期测,就可以达到你移动测量的值了。(这个时间最好大于60us,以防检测和发送的声波冲突)。

 

了解了超声波模块的基本工作原理后就可以完成程序设计了。

程序实现

和之前操作一样,我们在之前代码的基础上直接打开CobeMX。

代码实现顺序:先设置触发信号,这里我们使用一个普通的GPIO口输出一个高电平,等待Echo发出脉冲,然后启动定时器,等待Echo变为低电平即为一次定时时间,根据数学公式我们可以将这个时间转化为距离。

CubeMX设置

这里我们还是设置一个普通的GPIO口为输出模式,再使用通用定时器作为测量时间的工具。

这里使用PG11作为GPIO输出口和Trig引脚相连,然后打开TIM4通道1设置为Input Capture direct mode模式,然后再在NVIC中配置主优先级为1次优先级为0,具体配置如图:

 

配置完成后生成代码,

代码实现

和之前实现定时器的中断相似的步骤,但是我们这里要实现另外的中断回调函数:

uint8_t TIM4CH1_CAP_STA = 0;                        // 输入捕获状态

uint16_t TIM4CH1_CAP_VAL;                           // 输入捕获值

// 中断服务函数里面会自动调用这个回调函数,这个是定时器更新中断处理的函数(更新时间回调函数,这里更新溢出的时间)

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

    if(htim->Instance == TIM4)       // 判断定时器4是否发生中断

    {

        if((TIM4CH1_CAP_STA & 0X80) == 0)           // 还未成功捕获

        {

            if(TIM4CH1_CAP_STA & 0X40)   // 已经捕获到高电平

            {         

                if((TIM4CH1_CAP_STA & 0X3F) == 0X3F)// 高电平时间太长了,做溢出处理

                { 

                    TIM4CH1_CAP_STA |= 0X80;   // 标记为完成一次捕获

                    TIM4CH1_CAP_VAL += 1;       // 计数器值

                }

                else

                {

                    TIM4CH1_CAP_STA++;    // 若没有溢出,就只让TIM5CH1_CAP_STA自加

                }               

            }  

        }

    }

}



// 定时器输入捕获中断处理回调函数,该函数在 HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim) 中会被调用(通道输入捕获)

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)

{

    if((TIM4CH1_CAP_STA & 0X80) == 0)               // 还未成功捕获

    {

         if(TIM4CH1_CAP_STA & 0X40)         // 捕获到一个下降沿

        {          

                     TIM4CH1_CAP_STA |= 0X80;   // 标记成功捕获到一次高电平脉宽

                     TIM4CH1_CAP_VAL = HAL_TIM_ReadCapturedValue(&htim4, TIM_CHANNEL_1); // 获取当前的计数器值

                     TIM_RESET_CAPTUREPOLARITY(&htim4, TIM_CHANNEL_1);                 // 清除原来的设置        

                     TIM_SET_CAPTUREPOLARITY(&htim4,TIM_CHANNEL_1, TIM_ICPOLARITY_RISING);// 设置上升沿捕获

              }

              else

        {

                     TIM4CH1_CAP_STA = 0;     // 清空自定义的状态寄存器

                     TIM4CH1_CAP_VAL = 0;        // 清空捕获值

                     TIM4CH1_CAP_STA |= 0X40;        // 标记捕获到上升沿

                     __HAL_TIM_DISABLE(&htim4);    // 关闭定时器

                     __HAL_TIM_SET_COUNTER(&htim4, 0);   // 计数器值清零

                     TIM_RESET_CAPTUREPOLARITY(&htim4,TIM_CHANNEL_1);  // 一定要先清除原来的设置   !!              

                 TIM_SET_CAPTUREPOLARITY(&htim4,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);   // 设置下降沿捕获

                     __HAL_TIM_ENABLE(&htim4);         // 使能定时器     

              }     

       }

}

完成中断回调函数之后,就可以在主函数中启动中断了,最后我这里使用了串口输出相关的距离、时间,也可以使用其他的方式输出,因为最后整合代码时不需要串口输出,所以我对串口配置没有说明。

主函数部分

int main(void)

{

  /* USER CODE BEGIN 1 */

//    uint8_t i = 0;

      

       uint8_t length_res[10];  // 定义一个数组存放前方的距离

//    uint8_t Car_Command= 4;

  /* 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();

  MX_TIM3_Init();

  MX_TIM4_Init();

  /* USER CODE BEGIN 2 */

      

       printf("这是智能小车方向控制测试程序");

       HAL_TIM_Base_Start_IT(&htim3);  // 这个中断是之前的舵机中断

       HAL_TIM_PWM_Start_IT(&htim3, TIM_CHANNEL_1);

       HAL_TIM_IC_Start_IT(&htim4,TIM_CHANNEL_1);  // 一定要开启TIM4通道1的捕获中断,这个是超声波中断

  __HAL_TIM_ENABLE_IT(&htim4,TIM_IT_UPDATE);  // 一定要开启TIM4的更新中断

  printf("This is TIM_CAP test...\n");   

  /* USER CODE END 2 */



  /* Infinite loop */

  /* USER CODE BEGIN WHILE */

  while (1)

  {

              HAL_GPIO_WritePin(GPIOG, GPIO_PIN_12, GPIO_PIN_SET);

              HAL_Delay(1);

              HAL_GPIO_WritePin(GPIOG, GPIO_PIN_12, GPIO_PIN_RESET);

             

              HAL_Delay(500);

   if(TIM4CH1_CAP_STA & 0X80)    // 完成一次高电平捕获

   {

     temp = TIM4CH1_CAP_STA & 0X3F;

      temp *= 1000;            // 溢出总时间

     temp = (temp+TIM4CH1_CAP_VAL)/58.0; // temp+TIM4CH1_CAP_VAL是总的时间,除以58是距离

      printf("High level duration:%lld cm\r\n",temp);  // 输出每次测出的距离,单位是cm

      TIM4CH1_CAP_STA = 0;             // 准备下一次捕获

       }

             

    /* USER CODE END WHILE */

             

    /* USER CODE BEGIN 3 */

  }

                    

  /* USER CODE END 3 */

}

这样就完成了超声波部分的测试,最后我们将代码整合,就可以完成小车自动识别障碍物然后前进

代码整合

在整合之前,我们需要修改之前的代码,现在我们要实现的目的是:超声波模块测量前面的距离,如果距离大于30cm,就让小车直走,距离小于30cm,停车,测量左侧和右侧45°的距离,然后比较这两个数,向距离大的一侧前进。

// 小车停止代码

void Car_Stop(void)

{

       HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, GPIO_PIN_RESET);

       HAL_GPIO_WritePin(IN3_GPIO_Port, IN3_Pin, GPIO_PIN_RESET);



}

Uint8_t a;

// 上节中我们有个关于舵机旋转方向的表格,在这里我们定义90°为正前方

// 右转45°

void SG90_Right_45(void)

{

       a = 1;

}

// 舵机转向前方

void SG90_Front(void)

{

       a = 2;

}

// 左转45°

void SG90_Left_45(void)

{

       a = 3;

}

// 这里我们之前的回调函数也得重新实现,以保证我们可以调整想要的角度

/* TIM3 init function */

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)

{

       // 舵机的回调函数,使舵机转向

       if(htim->Instance == TIM3)  // 判断是否是定时器3引起的中断

       {

              if(a == 1)  // 判断是具体是哪个转向

              {

                     __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,10);

              }

              if(a == 2)

              {

                     __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,15);

              }

              if(a == 3)

              {

                     __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,20);

              }

       }

}

剩下的就是主函数部分的重新实现:

int main(void)

{

  /* USER CODE BEGIN 1 */

//    uint8_t i = 0;

      

       uint8_t length_res[10];  // 定义一个数组存放前方的距离

//    uint8_t Car_Command= 4;

  /* 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();

  MX_TIM3_Init();

  MX_TIM4_Init();

  /* USER CODE BEGIN 2 */

      

       printf("这是智能小车方向控制测试程序");

       HAL_TIM_Base_Start_IT(&htim3);

       HAL_TIM_PWM_Start_IT(&htim3, TIM_CHANNEL_1);

//    Car_Dir_Com(Car_Command);            

//    Car_Go();

       HAL_TIM_IC_Start_IT(&htim4,TIM_CHANNEL_1);  // 一定要开启TIM4通道1的捕获中断

  __HAL_TIM_ENABLE_IT(&htim4,TIM_IT_UPDATE);  // 一定要开启TIM4的更新中断

  printf("This is TIM_CAP test...\n");

             

  /* USER CODE END 2 */



  /* Infinite loop */

  /* USER CODE BEGIN WHILE */

  while (1)

  {

              SG90_Front();   //舵机摆正

              HAL_Delay(100);

              length_res[0] =Senor_Using();  //测前方距离放在数组里

              HAL_Delay(100);

              if(length_res[0]>30.00)       //如果前方距离大于30cm  前进

              {

                     Car_Go();

              }     

                           

              if(length_res[0]<30.00)     //如果前方距离小于30厘米  停车测左右距离

              {

                     Car_Stop(); 

                     SG90_Left_45();      //舵机左转45度测距

                     HAL_Delay(700);       

                     length_res[1] =Senor_Using();    //把测量结果放进数组

                    

                     SG90_Right_45();     //舵机右转45度测距

                     HAL_Delay(700);

                     length_res[4] =Senor_Using();     //把测量结果放进数组                    

                           

                     SG90_Front();           //舵机摆正

                     HAL_Delay(100);

                     if(length_res[1]>length_res[4])    //如果左边的距离大于右边的距离

                     {

                            do                        //舵机摆正

                            {

                            SG90_Front();

                            HAL_Delay(10);

                            length_res[0] =Senor_Using(); //重复测前方的距离同时左转

                            HAL_Delay(10);                                     

                            Car_Left();

                            }

                            while(length_res[0]<30.00);           //一直转到前方距离大于30cm             

                     }

                     if(length_res[1]<length_res[4])    //如果右边的距离大于左边的距离

                     {

                            do

                            {

                            SG90_Front();

                            HAL_Delay(10);

                            length_res[0] =Senor_Using();  //重复测前方的距离同时右转

                            HAL_Delay(10);                                     

                            Car_Right();

                            }

                            while(length_res[0]<30.00);           //一直转到前方距离大于30cm

                     }

                           

              }

// 这部分函数是之前测试超声波的函数,现在不需要

//           HAL_GPIO_WritePin(GPIOG, GPIO_PIN_12, GPIO_PIN_SET);

//           HAL_Delay(1);

//           HAL_GPIO_WritePin(GPIOG, GPIO_PIN_12, GPIO_PIN_RESET);

//          

//           HAL_Delay(500);

//    if(TIM4CH1_CAP_STA & 0X80)    // 完成一次高电平捕获

//    {

//      temp = TIM4CH1_CAP_STA & 0X3F;

//      temp *= 1000;            // 溢出总时间

//      temp = (temp+TIM4CH1_CAP_VAL)/58.0;  // 总的高电平时间

//      printf("High level duration:%lld cm\r\n",temp);

//      TIM4CH1_CAP_STA = 0;          // 准备下一次捕获

//           }

             

    /* USER CODE END WHILE */

             

    /* USER CODE BEGIN 3 */

  }

                    

  /* USER CODE END 3 */

}

这样就完成了整个项目的书写,最后附上整个项目的HAL库源码:

百度网盘 请输入提取码

提取码:1234

物联沃分享整理
物联沃-IOTWORD物联网 » 基于STM32的智能循迹避障小车实验——超声波部分详解

发表评论