系列文章目录

一、小车1.0——基本蓝牙小车(仅蓝牙遥控小车运动方向,本篇)
二、小车2.0——蓝牙小车PLUS(可以蓝牙控制方向+蓝牙直接调节车速)
三、小车3.0——避障小车(超声波+舵机云台)
四、小车4.0——无线手柄方向感知操控小车(mpu6050+双蓝牙透传)
五、双轮自平衡小车(HAL库版)——点此学习吧



前言

提示:学习这篇文章《超声波避障小车》之前还需要掌握小车驱动以及超声波测距的相关内容

学习完HC-SR04超声波模块的使用以及怎样驱动小车之后,就可以通过简单的代码实现基础的蓝牙避障小车了。
HC-SR04超声波模块的使用详见----->《超声波模块的使用》
小车驱动程序的讲解详见 ----------->小车驱动(这个链接是蓝牙小车的,驱动部分不用看usart中内容)


提示:以下是本篇文章正文内容,下面案例可供参考

一、前期准备

需要准备
STM32F103C8T6 —————————— 1个
12V电池————–—————————— 1个
L298N电机驱动 ——————————— 2个
小车底座 —————————————— 1个
稳压模块或者可调降压模块 —————— 1个
超声波模块HC-SR04————————— 1个
舵机SG90 -————————————— 1个

二、CubeMX中的设置

2.1. 时钟树的配置

(1)点击RCC开启HSE和LSE,并选择RC或晶体作为时钟源
在这里插入图片描述
(2)配置时钟树
在这里插入图片描述

最终最右侧显示为
在这里插入图片描述

2.2. TIM的设置

2.2.1. TIM2设置

(1)点击TIM2,在Mode选项中设置Clock Source为Internal Clock。设置Channel1为PWM Generation CH1,Channel2为PWM Generation CH2,其余默认即可。
在这里插入图片描述
(2)在Mode下面的Configuration选项中Parameter Settings的参数设置
在这里插入图片描述
(3)TIM2 的GPIO参数设置
CH1,CH2都设置为推挽输出,高速
在这里插入图片描述
在这里插入图片描述

2.2.2. TIM3设置

(1)点击TIM3,在Mode选项中设置Clock Source为Internal Clock。
在这里插入图片描述
(2)在Mode下面的Configuration选项中Parameter Settings的参数设置
在这里插入图片描述
(3)TIM3中NVIC设置
在这里插入图片描述

2.2.3. TIM4设置

(1)点击TIM4,在Mode选项中设置Clock Source为Internal Clock,并将TIM4_CH4设置为输入捕获模式
在这里插入图片描述

(2)在Mode下面的Configuration选项中Parameter Settings的参数设置
在这里插入图片描述
(3)TIM4中NVIC设置
在这里插入图片描述
(4)TIM4 的GPIO参数设置
在这里插入图片描述

2.3. INx分配的GPIO参数配置

注意:这里的GPIO设置只用添加电机模块的INx以及超声波的发送端Trig,其余的已经在TIM的设置中配置过了

2.3.1. INx设置:

8个INx与PA11的参数设置一致,但是User Label不一样,对应如下:
IN1——PA8
IN2——PA12
IN3——PA13
IN4——PA14
IN11——PA15
IN22——PB0
IN33——PB1
IN44——PB2
在这里插入图片描述
最终结果为:
在这里插入图片描述

2.3.2. Trig设置:

在这里插入图片描述

自此就全部设置完了
最终:

在这里插入图片描述

三、代码

注意:这里我仅添加了超声波输入捕获测距程序和舵机PWM调速,小车驱动的程序见前言的链接

总体项目代码结构

在这里插入图片描述

3.1. 超声波输入捕获代码

chaoshengbo.h

#ifndef CHAOSHENGBO_CHAOSHENGBO_H_
#define CHAOSHENGBO_CHAOSHENGBO_H_

#include "stm32f1xx_hal.h" //HAL库文件声明
#include <main.h>
#include "../delay/delay.h"


#define TRIG_ON  HAL_GPIO_WritePin(GPIOB, TRIG_Pin, GPIO_PIN_SET)   //定义TRIG输出高电平
#define TRIG_OFF  HAL_GPIO_WritePin(GPIOB, TRIG_Pin, GPIO_PIN_RESET)//定义TRIG输出低电平
extern TIM_HandleTypeDef htim4;//声明TIM4的HAL库结构体
extern TIM_HandleTypeDef htim3;//声明TIM3的HAL库结构体

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);//定时器中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);//定时器输入捕获中断回调函数

#endif /* CHAOSHENGBO_CHAOSHENGBO_H_ */

chaoshengbo.c

#include "chaoshengbo.h"


/********************************************************************************************
 *设置变量
 *distances:超声波所测距离
 *t        :回响信号脉冲持续时间
 *high_time[0]:回响信号脉冲上升沿发生时间
 *high_time[1]:回响信号脉冲下降沿发生时间
 *c_values :标志值,用于定时器输入捕获回调函数

 ********************************************************************************************/
float distances;
uint32_t t=0;
uint32_t high_time[2]={0};
uint8_t c_values=0;

/********************************************************************************************
 * 定时器中断回调函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
 *
 * 说明:
 * 因为SR04每次发送超声波的时间间隔要大于60ms,
 * 故通过Cube MX 已经将TIM3设置为100ms的定时器,每隔100ms才执行一次这个定时器中断回调函数
 * 即每隔100ms发送一个20us的高电平脉冲,同时开启定时器4输入捕获并设置为上升沿捕获
 *
 ********************************************************************************************/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{


	if(htim==&htim3)                      //判断是否为TIM3溢出中断
	{
		TRIG_OFF;                         //先将超声波模块SR04的发送端TRIG拉低
		TRIG_ON;                          //再将超声波模块SR04的发送端TRIG拉高,并且持续20ms后再拉低
		delay_us(20);
		TRIG_OFF;
		__HAL_TIM_SET_CAPTUREPOLARITY(&htim4,TIM_CHANNEL_4,TIM_ICPOLARITY_RISING);//设置为上升沿捕获
		HAL_TIM_IC_Start_IT(&htim4,TIM_CHANNEL_4);//开启定时器输入捕获

	}

}

/********************************************************************************************
 * 定时器输入捕获回调函数 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
 *
 *说明:
 *ECHO接收回响信号脉冲,这个回调函数要被执行2次
 *这个回调函数在类似于一个分叉路口,上升沿捕获走路口0,下降沿捕获走路口1
 *
 ********************************************************************************************/
	void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
	{
		if(htim==&htim4)
		{
			switch(c_values)
			{//标志值c_values初始设定值为0,上升沿输入捕获,先执行回调函数执行case(0)中内容

			case(0): high_time[0]=HAL_TIM_ReadCapturedValue(&htim4,TIM_CHANNEL_4);//获取上升沿的捕获值,即接收的高电平开始时间
			         __HAL_TIM_SET_CAPTUREPOLARITY(&htim4,TIM_CHANNEL_4,TIM_ICPOLARITY_FALLING);//设置为下降沿捕获
			         c_values++;//标志值c_values值变为1,下次回调函数执行case(1)中内容
			         break;


			case(1): high_time[1]=HAL_TIM_ReadCapturedValue(&htim4,TIM_CHANNEL_4);//获取下降沿的捕获值,即接收的高电平结束时间
			         HAL_TIM_IC_Stop_IT(&htim4,TIM_CHANNEL_4); //关闭TIM4输入捕获
			         c_values=0;                 //标志值清零,用于下次输入捕获回调函数
			         __HAL_TIM_SET_COUNTER(&htim4,0);//TIM4计数值清零
				     t=high_time[1]-high_time[0];//下降沿捕获值-上升沿捕获值=回响信号高电平脉冲持续时间t
				     distances= t*0.017; //速度0.034cm/us,计算出的距离要除以2,distances的单位是cm
			         break;
			default: break;
			}
		}
	}

3.2. 舵机PWM代码

我直接将这个舵机PWM调速.c写到了control.c中
由舵机控制原理知道:(sg90)要求0.5ms~2.5ms对应 0至180°(数学映射思想)

	void SG90_Init()//舵机转90°
	{
		__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2,1500);  //sg90是逆时针转动
	}
	void SG90_Turn_L()//舵机转135°
	{
		__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2,2000);
	}
	void SG90_Turn_R()//舵机转45°
	{
		__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2,1000);
	}


3.3. us级延时函数代码

delay.h

#ifndef DELAY_DELAY_H_
#define DELAY_DELAY_H_

#include "stm32f1xx_hal.h" HAL库文件声明
void delay_us(uint32_t us); //us级延时函数

#endif /* DELAY_DELAY_H_ */

delay.c

#include "delay.h"

void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数
{
    uint32_t delay = (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数
    while (delay--); //循环delay次,达到1微秒延时
}

3.4. 小车驱动

略,小车驱动的程序见前言的链接,我写的《蓝牙小车》中有十分详细的介绍,
唯一不同的是因为TIM2设置的预分频以及计时周期的不同,__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,5000);的设定值有所改变。

3.5. main.c

/* USER CODE BEGIN Includes */
#include "../../icode/control/control.h"
#include "../../icode/chaoshengbo/chaoshengbo.h"
/* USER CODE END Includes */



/* USER CODE BEGIN 1 */
extern  float distances;
uint32_t S=20;//设定距离标准值
uint32_t LS,RS=0;//测定的左右两边距离值
/* USER CODE END 1 */



/* USER CODE BEGIN 2 */

HAL_TIM_Base_Start_IT(&htim3);//开启定时器3中断
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //直流电机PWM
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); //SG90PWM
SG90_Init();//SG90舵机初始化,也就是转90°,朝向小车前方
CAR_STOP();
HAL_Delay(500);

/* USER CODE END 2 */
	


while (1)
{
  /* USER CODE END WHILE */
  /* USER CODE BEGIN 3 */
	if(distances>S)
	{
	    CAR_GO();
	}
	else
	{
	    CAR_STOP();
	    SG90_Turn_L();
	    HAL_Delay(1500);
	    LS=distances;
	    SG90_Turn_R();
	    HAL_Delay(1500);
	    RS=distances;
	    SG90_Init();
	
	    if(LS>RS&&LS>S)
	    {
	  	  CAR_LGO();
	  	  HAL_Delay(500);
	    }
	    else if(RS>LS&&RS>S)
	    {
	  	  CAR_RGO();
	  	  HAL_Delay(500);
	    }
	    else
	    {
	  	  CAR_BACK();
	  	  HAL_Delay(500);
	    }
	}
  }
  /* USER CODE END 3 */


总结

总体说来,这个避障小车的避障功能不是很完善,还存在一些视野盲区。因为通过舵机的转动,超声波模块只能监测到小车车头90°的范围,只能算是个半成品吧,这个项目目前是将所学的PWM控制,以及超声波整合在一起作为一个小整体,也算是小车之路的必经处吧。。。

欢迎大家积极交流,本文未经允许谢绝转载!!!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐