DIY Intelligent Car Chapter: Driving, Patrolling and Obstacle Avoidance of Functional Modules

Posted by joma on Wed, 04 Sep 2019 07:16:24 +0200

Preface

This article mainly aims at the realization of the car's various functions in the process of specific implementation principles, procedures and algorithms, as well as the problems encountered in a unified interpretation and explanation. So far, combined with the structure module of the previous article, all the work of the smart car project has been completed, and there must be a lot of room for optimization, but considering that the ultimate goal of this project is to understand embedded programming more deeply, so this project has to explore a broader world.

drive control

PWM Configuration of Driver Module

In order to control the speed of the car, it is necessary to input PWM wave to the enabling pin of L298N motor drive module. Changing the duty cycle can adjust the speed of the car.

The PWM configuration is as follows:

/**
 * @brief: Initialize the output configuration of PWM, and use TIM3 to output four PWM waves at the same time to control the speed of four motors.
 * @param: arr-Automatic reload value psc-clock pre-dividing coefficient
 * @retval	NONE
 * @Others:GPIO_PinAFConfig Functions must be multiplexed step by step. They cannot be combined with a single multiplexed function, or they have only one output.
**/
void TIM3_PWM_Init(u16 arr,u16 psc)
{		 					 
	GPIO_InitTypeDef GPIO_InitStructure;

	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  	//TIM3 Clock Enablation    
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); 	//Enabling GPIOA Clock	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 	//Enabling GPIOB Clock	
	
	//The GPIO_PinAFConfig function must be multiplexed step by step. It cannot be combined with a multiplexed function, or only one output can be obtained.
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM3); //GPIOA6 multiplexed to Timer 3
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM3); //GPIOA7 Multiplexed to Timer 3
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource0,GPIO_AF_TIM3); //GPIOB0 multiplexed to Timer 3
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource1,GPIO_AF_TIM3); //GPIOB1 multiplexed into timer 3
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;       
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //Reuse function
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//Speed 100MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //Push-pull multiplexing output
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //Pull up
	GPIO_Init(GPIOA,&GPIO_InitStructure);               //Initialize PF9

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;         
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //Reuse function
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//Speed 100MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //Push-pull multiplexing output
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //Pull up
	GPIO_Init(GPIOB,&GPIO_InitStructure);  	  

	TIM_TimeBaseStructure.TIM_Prescaler=psc;  //Timer frequency division
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //Upward Counting Mode
	TIM_TimeBaseStructure.TIM_Period=arr;   //Automatic reload value
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
	
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);//Initialization timer 3
	
	//Initialize TIM3 PWM mode	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //Select Timer Mode: TIM Pulse Width Modulation Mode 2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //Comparing output enablement
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //Output Polarity: TIM Output is Low Polarity

	TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //Initialize peripheral TIM3OC1 according to the parameters specified by T
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //Initialize peripheral TIM3OC2 according to the parameters specified by T
	TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //Initialize peripheral TIM3OC3 according to the parameters specified by T
	TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //Initialize peripheral TIM3OC4 according to the parameters specified by T

	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //Preload Register Enabling TIM3 on CCR1
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //Preload Register Enabling TIM3 on CCR1
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //Preload Register Enabling TIM3 on CCR1
	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //Preload Register Enabling TIM3 on CCR1
	
	TIM_ARRPreloadConfig(TIM3,ENABLE);
 
	TIM_Cmd(TIM3, ENABLE);  //Enabling TIM3
										  
}  

Driver implementation

Now equivalent to the drive module has been completed, in order to make the car move, the IN1~IN4 pin level needs to be operated.
Step 1:IO Port Initialization

/**
 * @brief: IO Port for Initialization Control Motor Forward and Reverse and Brake
 * @param: NONE
 * @retval:	NONE
 * @Others: When choosing IO, be careful not to conflict with the hardware interface of STM32, for example, IO occupied the first time I chose.
						LCD An interface of the screen, resulting in abnormal initialization of LCD
**/
void CAR_Init(void)
{    	 
  GPIO_InitTypeDef  GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//Enabling GPIOF Clock
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//Enabling GPIOG Clock
  //IO initialization settings
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_3 | GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_15 | GPIO_Pin_13 | GPIO_Pin_14;//LED 0 and LED 1 correspond to IO port
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//Common Output Mode
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//push-pull
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//Pull up
  GPIO_Init(GPIOF, &GPIO_InitStructure);//Initialize GPIO
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//Common Output Mode
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//push-pull
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//Pull up
  GPIO_Init(GPIOG, &GPIO_InitStructure);//Initialize GPIO
	
	GPIO_SetBits(GPIOF,GPIO_Pin_1 | GPIO_Pin_3 | GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_15 | GPIO_Pin_13 | GPIO_Pin_14);
	GPIO_SetBits(GPIOG,GPIO_Pin_1);
}

Step 2: Driving function: including forward, backward, steering, parking

/**
 * @brief: Gear speed conversion function, set 1-4 gears, the gear linear conversion to the corresponding PWM wave output
 * @param: Gear value
 * @retval: pulse
 * @Others: NONE
**/
u16 gear_trans(u16 gear)
{
	u16 Pulse=0;
	switch(gear)
	{
		case 0x00:
			Pulse=500;
			break;
		case 0x01:
			Pulse=350;
			break;
		case 0x02:
			Pulse=300;
			break;
		case 0x03:
			Pulse=200;
			break;
		case 0x04:
			Pulse=100;
			break;
		default:
			break;
	}
	return Pulse;
}

/**
 * @brief: Forward Function, Control Car Forward
 * @param: Gear value
 * @retval: NONE
 * @Others: NONE
**/
void drive(u16 gear)
{
		u16 Pulse;
		Pulse=gear_trans(gear);
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_1); 
		GPIO_SetBits(GPIOF,GPIO_Pin_3);   
	
		GPIO_SetBits(GPIOF,GPIO_Pin_5);	  
		GPIO_ResetBits(GPIOF,GPIO_Pin_7); 
	
		GPIO_SetBits(GPIOF,GPIO_Pin_15);	   
		GPIO_ResetBits(GPIOG,GPIO_Pin_1); 
	
		GPIO_SetBits(GPIOF,GPIO_Pin_14);	  
		GPIO_ResetBits(GPIOF,GPIO_Pin_13); 
	
		TIM_SetCompare1(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare2(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare3(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare4(TIM3,Pulse);	//Modify the comparison value and duty cycle

}
/**
 * @brief: Backward function
 * @param: Gear value
 * @retval: NONE
 * @Others: NONE
**/
void reverse(u16 gear)	
{
		u16 Pulse;
		Pulse=gear_trans(gear);
	
		GPIO_SetBits(GPIOF,GPIO_Pin_1); 
		GPIO_ResetBits(GPIOF,GPIO_Pin_3); 
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_5);	  
		GPIO_SetBits(GPIOF,GPIO_Pin_7); 
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_15);	 
		GPIO_SetBits(GPIOG,GPIO_Pin_1); 
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_14);	
		GPIO_SetBits(GPIOF,GPIO_Pin_13); 
	
		TIM_SetCompare1(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare2(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare3(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare4(TIM3,Pulse);	//Modify the comparison value and duty cycle

}
/**
 * @brief: Parking function
 * @param: NONE
 * @retval: NONE
 * @Others: NONE
**/
void stop(void)
{
		GPIO_ResetBits(GPIOF,GPIO_Pin_1); 
		GPIO_ResetBits(GPIOF,GPIO_Pin_3);   
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_5);	 
		GPIO_ResetBits(GPIOF,GPIO_Pin_7); 
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_15);	  
		GPIO_ResetBits(GPIOG,GPIO_Pin_1); 
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_14);	 
		GPIO_ResetBits(GPIOF,GPIO_Pin_13); 
}
/**
 * @brief: Left turn function, using one side wheel to lock turn
 * @param: Turning gear
 * @retval: NONE
 * @Others: NONE
**/
void left_move(u16 gear_change)
{
		u16 Pulse;
		Pulse=gear_trans(gear_change);
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_1); 
		GPIO_SetBits(GPIOF,GPIO_Pin_3);   
	
		GPIO_SetBits(GPIOF,GPIO_Pin_5);	  
		GPIO_ResetBits(GPIOF,GPIO_Pin_7); 
	
		GPIO_SetBits(GPIOF,GPIO_Pin_15);	   
		GPIO_ResetBits(GPIOG,GPIO_Pin_1);
	
		GPIO_SetBits(GPIOF,GPIO_Pin_14);	 
		GPIO_ResetBits(GPIOF,GPIO_Pin_13); 
	
		TIM_SetCompare1(TIM3,500);	//Modify the comparison value and lock the left wheel
		TIM_SetCompare2(TIM3,500);	//Modify the comparison value and lock the left wheel
		TIM_SetCompare3(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare4(TIM3,Pulse);	//Modify the comparison value and duty cycle
}
/**
 * @brief: Right turn function, using one side wheel to lock turn
 * @param: Turning gear
 * @retval: NONE
 * @Others: NONE
**/
void right_move(u16 gear_change)
{
		u16 Pulse;
		Pulse=gear_trans(gear_change);
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_1); 
		GPIO_SetBits(GPIOF,GPIO_Pin_3);  
	
		GPIO_SetBits(GPIOF,GPIO_Pin_5);	  
		GPIO_ResetBits(GPIOF,GPIO_Pin_7); 
	
		GPIO_SetBits(GPIOF,GPIO_Pin_15);	
		GPIO_ResetBits(GPIOG,GPIO_Pin_1);
	
		GPIO_SetBits(GPIOF,GPIO_Pin_14);	 
		GPIO_ResetBits(GPIOF,GPIO_Pin_13); 
	
		TIM_SetCompare1(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare2(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare3(TIM3,500);	//Modify the comparison value to lock the right wheel
		TIM_SetCompare4(TIM3,500);	//Modify the comparison value to lock the right wheel
}
/**
 * @brief: Left-turn function, using one forward and one backward turn
 * @param: Turning gear
 * @retval: NONE
 * @Others: NONE
**/
void left_move_2(u16 gear_change)
{
		u16 Pulse;
		Pulse=gear_trans(gear_change);
	
		GPIO_SetBits(GPIOF,GPIO_Pin_1);  
		GPIO_ResetBits(GPIOF,GPIO_Pin_3);  
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_5);	  
		GPIO_SetBits(GPIOF,GPIO_Pin_7); 
	
		GPIO_SetBits(GPIOF,GPIO_Pin_15);	  
		GPIO_ResetBits(GPIOG,GPIO_Pin_1); 
	
		GPIO_SetBits(GPIOF,GPIO_Pin_14);	 
		GPIO_ResetBits(GPIOF,GPIO_Pin_13); 
	
		TIM_SetCompare1(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare2(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare3(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare4(TIM3,Pulse);	//Modify the comparison value and duty cycle
}
/**
 * @brief: Right turn function, using one side forward and one side backward turn
 * @param: Turning gear
 * @retval: NONE
 * @Others: NONE
**/
void right_move_2(u16 gear_change)
{
		u16 Pulse;
		Pulse=gear_trans(gear_change);
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_1);  
		GPIO_SetBits(GPIOF,GPIO_Pin_3);  
	
		GPIO_SetBits(GPIOF,GPIO_Pin_5);	  
		GPIO_ResetBits(GPIOF,GPIO_Pin_7); 
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_15);	 
		GPIO_SetBits(GPIOG,GPIO_Pin_1); 
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_14);	   
		GPIO_SetBits(GPIOF,GPIO_Pin_13); 
	
		TIM_SetCompare1(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare2(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare3(TIM3,Pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare4(TIM3,Pulse);	//Modify the comparison value and duty cycle
}
/**
 * @brief: Forward function, mainly for remote control mode
 * @param: pulse value
 * @retval: NONE
 * @Others: NONE
**/
void drive_pulse(int pulse)
{	
		GPIO_ResetBits(GPIOF,GPIO_Pin_1); 
		GPIO_SetBits(GPIOF,GPIO_Pin_3);  
	
		GPIO_SetBits(GPIOF,GPIO_Pin_5);	   
		GPIO_ResetBits(GPIOF,GPIO_Pin_7);
	
		GPIO_SetBits(GPIOF,GPIO_Pin_15);	  
		GPIO_ResetBits(GPIOG,GPIO_Pin_1);
	
		GPIO_SetBits(GPIOF,GPIO_Pin_14);	  
		GPIO_ResetBits(GPIOF,GPIO_Pin_13);
	
		TIM_SetCompare1(TIM3,pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare2(TIM3,pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare3(TIM3,pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare4(TIM3,pulse);	//Modify the comparison value and duty cycle
}

/**
 * @brief: Back-off function, mainly for remote control mode
 * @param: pulse value
 * @retval: NONE
 * @Others: NONE
**/
void reverse_pulse(int pulse)
{
		GPIO_SetBits(GPIOF,GPIO_Pin_1); 
		GPIO_ResetBits(GPIOF,GPIO_Pin_3);  
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_5);	  
		GPIO_SetBits(GPIOF,GPIO_Pin_7);
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_15);	
		GPIO_SetBits(GPIOG,GPIO_Pin_1); 
	
		GPIO_ResetBits(GPIOF,GPIO_Pin_14);	 
		GPIO_SetBits(GPIOF,GPIO_Pin_13); 
	
		TIM_SetCompare1(TIM3,pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare2(TIM3,pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare3(TIM3,pulse);	//Modify the comparison value and duty cycle
		TIM_SetCompare4(TIM3,pulse);	//Modify the comparison value and duty cycle
}

/**
 * @brief: Turning function, mainly for remote control mode
 * @param: pulse1-On the left, pulse 2 - on the right
 * @retval: NONE
 * @Others: NONE
**/
void turn_pulse(int pulse1,int pulse2)
{
	 GPIO_ResetBits(GPIOF,GPIO_Pin_1); 
		GPIO_SetBits(GPIOF,GPIO_Pin_3);   
	
		GPIO_SetBits(GPIOF,GPIO_Pin_5);	   
		GPIO_ResetBits(GPIOF,GPIO_Pin_7);
	
		GPIO_SetBits(GPIOF,GPIO_Pin_15);	
		GPIO_ResetBits(GPIOG,GPIO_Pin_1); 
	
		GPIO_SetBits(GPIOF,GPIO_Pin_14);	  
		GPIO_ResetBits(GPIOF,GPIO_Pin_13); 
	
		TIM_SetCompare1(TIM3,pulse1);	//Modify the comparison value and duty cycle
		TIM_SetCompare2(TIM3,pulse1);	//Modify the comparison value and duty cycle
		TIM_SetCompare3(TIM3,pulse2);	//Modify the comparison value and duty cycle
		TIM_SetCompare4(TIM3,pulse2);	//Modify the comparison value and duty cycle
}

Now you can get your car moving. Comfortable.

Patrol function

Here I use four patrol sensors, one on the left and right sides and two on the middle part. The number and location of the sensors are different, so the corresponding algorithms to realize patrol are different. The specific algorithm problems can be consulted with relevant information.

Initialization of Patrol Sensor

Configuration of IO ports that need to receive patrol sensor signals

/**
 * @brief: Initialization of IO Port Connected by Trace Sensor
 * @param: NONE
 * @retval: NONE
 * @others: Initially, the switch indicator on the track module was always on because the wrong IO port was selected. PC0 and PC2 were used.
					  Change to PF2 and PF4, why?
**/
void Trace_Init(void)
{
	GPIO_InitTypeDef   GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); 	//Enabling GPIOF Clock	
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_6; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;        //Reuse function
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//Speed 100MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  //Push-pull multiplexing output
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;       //Pull up
	GPIO_Init(GPIOF,&GPIO_InitStructure);           
	
}

Receiving and transmitting signals

To collect and record the line information collected by the patrol line sensor

/**
 * @brief: Scanning function of tracking module, referring to STM32 button experiment
 * @param: modeļ¼Œ==1 Represents support for continuous pressing, == 0 means support for continuous pressing
 * @retval: 4 Number of each tracing module, return 1 to indicate No. 1 sensor pressure line
 * @others: NONE
**/

u8 TRACE_Scan(u8 mode)
{	 
	static u8 trace_up=1;//Press the button to loosen the sign
	if(mode)trace_up=1;  //Support Click		  
	if(trace_up&&(TRACE1==1||TRACE2==1||TRACE3==1||TRACE4==1))
	{
		delay_ms(10);//debounce 
		trace_up=0;
		if(TRACE1==1)return 1;
		else if(TRACE2==1)return 2;
		else if(TRACE3==1)return 3;
		else if(TRACE4==1)return 4;
	}
	else if(TRACE1==0&&TRACE2==0&&TRACE3==0&&TRACE4==0)trace_up=1;
		return 0;// Press without key
}

Patrol Line Realization

Only rectangular patrol lines are compiled here. If other shape lines need to be patrolled, relevant information can be consulted.

/**
 * @brief: Track execution module (rectangular patrol line), according to the detected sensor signal to make corresponding actions
 * @param: NONE
 * @retval: NONE
 * @others: Because this project is mainly used to practice programming ability, so only rectangular patrol lines are written here.
						There is no in-depth study of other linetype situations and various algorithms.
**/
void TRACE_Implement(void)
{
		extern u16 GEAR;
		u8 Edge_Flag=0;//Rectangular 90 degree angle detection, after detection of this flag position 1
		u8 trace_scan; 
		trace_scan=TRACE_Scan(0);//Number of the sensor that gets the pressure line		
	  if(trace_scan)
		{						   
			switch(trace_scan)
			{				 
				case TRACE1_SCAN:	//Leftmost sensor detected
					left_move_2(GEAR+2);//Turn left at a higher speed
					Edge_Flag	=	1;//90 degree mark position 1
					delay_ms(150);//Turn 90 degrees to the end
					left_move(GEAR);//Little Left Turn
					break;
				case TRACE2_SCAN: //Right-most sensor detected
					right_move_2(GEAR+2);
					Edge_Flag	=	1;
					delay_ms(150); 
					right_move(GEAR);
					LED0=!LED0;
					break;
				case TRACE3_SCAN:	//Detection of left-middle sensor
					if(Edge_Flag==1)//It shows that it has just passed a 90 degree bend.
					{
						right_move(GEAR);//Turn right when the middle left sensor is pressing the line
						Edge_Flag=0;
					}
					else
					{
						left_move(GEAR);//Without 90 degree bend, turn left when the left sensor is pressing the line in the middle
					}
					LED1=!LED1;
					break;
				case TRACE4_SCAN:	//Detection of the middle right sensor
					if(Edge_Flag==1)
					{
						left_move(GEAR);
						Edge_Flag=0;
					}
					else
					{
						right_move(GEAR);
					}
					LED0=!LED0;
					LED1=!LED1;
					break;
			}
		}else delay_ms(10); 
}

Barrier avoidance function

As mentioned earlier, we have built an obstacle avoidance platform using steering gear and ultrasonic sensors. Now we need to initialize it and complete ranging and obstacle avoidance.

PWM configuration of steering gear

The angle of the steering gear is controlled by PWM wave. By inputting different duty cycles of PWM wave to the steering gear, the steering gear can reach different angle positions.

/**
 * @brief: PWM Output Configured with Driving Obstacle Avoidance Yuntai Steering Machine
 * @param: arr-Automatic reload value psc-clock pre-dividing coefficient
 * @retval: NONE
 * @others: NONE
**/
void TIM4_PWM_Init(u16 arr,u16 psc)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;

	/* Turn on the clock */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);

	GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4); //GPIOB6 Multiplexed to Timer 4
	
	/*  Configure GPIO mode and IO port */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;// PB6
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;     
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	//TIM4 timer initialization
	TIM_TimeBaseInitStructure.TIM_Period = arr; //PWM frequency = 72000/(199+1)=36Khz// Set the value of the automatic reload register cycle
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc;//Set as TIM4 clock frequency pre-dividing value
	TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//TIM Upward Counting Mode
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStructure);

	//PWM initialization initializes peripheral TIM4 according to the parameters specified in TIM_OCInitStruct
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//PWM Output Enablation
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;

	TIM_OC1Init(TIM4,&TIM_OCInitStructure);
	//Note that TIM_OC1Init is initialized here instead of TIM_OCInit, otherwise an error will occur. Because firmware libraries have different versions
	TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);//Preload Register Enabling TIM4 on CCR1
	
	TIM_Cmd(TIM4,ENABLE);//Enabling TIM4 peripherals
}

Initial configuration of ultrasonic sensor

/**
 * @brief: Ultrasound sensor initialization,
 * @param: NONE
 * @retval: NONE
 * @others: NONE
**/

void HCSR04_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;    
    EXTI_InitTypeDef EXTI_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStructure;
		NVIC_InitTypeDef   NVIC_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG , ENABLE);
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//Enabling SYSCFG Clock
	
    GPIO_InitStructure.GPIO_Pin = TRIG_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_Init(SONAR_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = ECHO_PIN;                   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; 
    GPIO_Init(SONAR_PORT, &GPIO_InitStructure); 
	
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOG, GPIO_PinSource3);  // Interrupt line and interrupt initialization configuration

    EXTI_ClearITPendingBit(EXTI_Line3);
	
	  /* External interrupt initialization for receiving detection signals */
    EXTI_InitStructure.EXTI_Line = EXTI_Line3;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);     
		
		/* External interrupt priority setting */
		NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;//External interruption 3
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//Enabling external interruption of channels
		NVIC_Init(&NVIC_InitStructure);

		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
		
		/* TIM2 Interrupt initialization for counting and timing */
    TIM_TimeBaseInitStructure.TIM_Prescaler = 83;//Frequency dividing coefficient 83, frequency 1 MHz, theoretical measurement accuracy 0.34 mm
		TIM_TimeBaseInitStructure.TIM_Period = 50000;//The counting period is 50000, equivalent to 0.05s, and the maximum measuring range is 17m.
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
		
		TIM_ITConfig(  //Enables or disables specified TIM interrupts
		TIM2, //TIM2
		TIM_IT_Update  |  //TIM interrupt source
		TIM_IT_Trigger,   //TIM Trigger Interrupt Source 
		ENABLE  //Enable
		);
		
		TIM_Cmd(TIM2, DISABLE); 

    TIM_ClearFlag(TIM2, TIM_FLAG_Update);
		
		/* TIM2 Interrupt priority setting */
		NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2 interruption
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ Channel Enablation
		NVIC_Init(&NVIC_InitStructure);  

		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
		
		/* TIM5 Interrupt initialization to start the ranging function */
    TIM_TimeBaseInitStructure.TIM_Prescaler = 83;
    TIM_TimeBaseInitStructure.TIM_Period = 100;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseInitStructure);
		
		TIM_ITConfig(  //Enables or disables specified TIM interrupts
		TIM5, //TIM5
		TIM_IT_Update  |  //TIM interrupt source
		TIM_IT_Trigger,   //TIM Trigger Interrupt Source 
		ENABLE  //Enable
		);
		
		TIM_Cmd(TIM5, ENABLE); 

    TIM_ClearFlag(TIM5, TIM_FLAG_Update);
		
		/* TIM5 Interrupt priority setting */
		NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM5 interruption
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; 
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ channel enabled
		NVIC_Init(&NVIC_InitStructure); 
}

Ranging function

According to the instructions of HC-SR05, it is necessary to trigger a high level not less than 10us. The following function realizes this function

/*Distance measure--------------------------------------------------*/
/**
 * @brief: Start the ranging function and trigger the ultrasonic sensor
 * @param: NONE
 * @retval: NONE
 * @others: The delay function inside this function must be reentrant because it is placed inside the interrupt function of TIM5
						STM32 The delay function in the routine is not reentrant, so here we customize the reentrant delay function.
**/
void HCSR04_StartMeasure(void)
{
    GPIO_SetBits(SONAR_PORT, TRIG_PIN);
    delay_us_unpre(40);   //  The width of trig signal must be greater than 10us.
													//It appears in the interrupt function of timer 5, so the system delay cannot be used.
	                        //  Customize the normal delay function to solve the problem of delay failure
    GPIO_ResetBits(SONAR_PORT, TRIG_PIN);
		//Printf ("Test start!"); the // printf function is also a non-reentrant function, which causes interruption failure after opening.
}

Processing counter counting value to get ranging distance

/**
 * @brief: Ranging function
 * @param: TIM2 Counting value
 * @retval: Ranging distance, units:cm
 * @others: NONE
**/
float HCSR04_GetDistance(u32 count)
{
		float distance;
    // distance = measurement/2/1000*340 = measurement/59 (cm)  measurement-units:us
    distance = (float)count / 58.8 ; 
    return distance;
}

Realization of Obstacle Avoidance

There are some algorithms involved here, which are easy to understand, that is, when an obstacle is detected, what kind of operation should be done after the obstacle is detected, and so on. Of course, obstacle avoidance can be realized in different ways, and it can be studied and optimized by itself.

/**
 * @brief: Obstacle avoidance task can be accomplished by corresponding operation using the obtained ranging distance
 * @param: NONE
 * @retval: NONE
 * @others: Initial real-time performance was not resolved because the function used a lot of delay, and now it is based on ranging distance.
						Real-time problem can be solved by implementing corresponding operations.

**/
void Obstacle_avoid(void)
{
	extern u16 GEAR;
	if(UltrasonicWave_Distance<30)
	{
		reverse(GEAR);
		while(UltrasonicWave_Distance<=40);
		stop();
		TIM_SetCompare1(TIM4, 181);//Left turn of detector 45 degrees
		delay_ms(500);
		if(UltrasonicWave_Distance<30)
		{
				TIM_SetCompare1(TIM4, 191);//Detector turning right 45 degrees
				delay_ms(500);
				if(UltrasonicWave_Distance<30)
				{
						reverse(GEAR);
						while(UltrasonicWave_Distance<=40);
						right_move_2(GEAR+1);
						delay_ms(500);
						TIM_SetCompare1(TIM4, 186);//Detector return zero position
						drive(GEAR);
				}
				else
				{		
						right_move_2(GEAR+1);
						delay_ms(500);
						TIM_SetCompare1(TIM4, 186);//Detector return zero position
						drive(GEAR);
				}
		}
		else
		{
				left_move_2(GEAR+1);
				delay_ms(500);
				drive(GEAR);			
				TIM_SetCompare1(TIM4, 186);//Detector return zero position
				
				
		}
		
		
	}
}

Topics: Programming less