STM32 Ultrasonic Obstacle Avoidance Car Project

STM32 Ultrasonic Obstacle Avoidance Car Project
Word Count: 10000 Content Quality: ⭐⭐⭐⭐⭐

Whether for beginners or electronic enthusiasts who have dabbled in microcontrollers, car projects are a common experience. Today, I will introduce a project based on the STM32 ultrasonic obstacle avoidance car. This was also one of my course projects, and I am sharing it as open source. The full text is over 5000 words, packed with useful information, so keep reading!STM32 Ultrasonic Obstacle Avoidance Car Project I guarantee you will gain a lot.STM32 Ultrasonic Obstacle Avoidance Car Project

Without further ado, let’s watch the video first.STM32 Ultrasonic Obstacle Avoidance Car Project

Processor Circuit Design

The microcontroller is the CPU of the system and is crucial for the system’s stability and normal operation. Below are two options for selecting the microcontroller:
(1) The traditional 8-bit microcontroller, which is integrated into an independent chip controller through large-scale integration circuits. Internal components include CPU, RAM, ROM, I/O interfaces, interrupt systems, timers, serial communication, and DACs. The STC89C52 microcontroller is the most common 51 microcontroller, but it has limited resources, low precision, and much slower processing speed compared to STM32 microcontrollers.
(2) Using the most common STM32 microcontroller available on the market today. The STM32 series microcontrollers can be divided into different application fields based on the ARM Cortex-M3 core architecture. It can be classified into the STM32F1 series and STM32F4 series. The STM32F1 series microcontroller has a maximum clock frequency of 72 MHz, making it the best-performing product in its category. The basic processing speed of the microcontroller is 36 MHz, and the performance of the 16-bit microcontroller is also very good. The built-in flash memory of the microcontroller is quite large, ranging from 32 KB to 512 KB, allowing for selection based on needs. The power consumption of a single device is very low, at only 360 mA, with the lowest power consumption of 32-bit microcontroller products being just 0.5 mA per MHz. Notably, the internal single-chip bus is a Harvard architecture that can execute instructions at speeds of up to 1.25 DMIPS/MHz. This chip is increasingly being used as the main controller.
Based on the resources and processing speed of the microcontroller, we chose the STM32F103C8T6 as the main control chip for this system. Program downloading can be easily completed with just a JLINK. The controller module circuit is shown below:

STM32 Ultrasonic Obstacle Avoidance Car Project

Power Supply Module Design

This design uses a lithium battery for power supply. The power supply voltage for the module is generally 5V, and the ultrasonic module requires a large current to operate normally, so sufficient output current must be ensured while stepping down the voltage. This design uses an adjustable output version, with a wide input voltage range for the module, and output voltage adjustable between 1.25V and 35V, with high voltage conversion efficiency and low output ripple. The step-down circuit is shown below:

STM32 Ultrasonic Obstacle Avoidance Car Project

Motor Driver Module Design

To achieve steering, the microcontroller can be used, but the load capacity of the microcontroller’s I/O is weak, so we chose the high-power amplifier component TB6612FNG. The TB6612FNG uses a MOSFET H-bridge structure for dual-channel high current output, capable of controlling two motors. Compared to ordinary motor drivers, the peripheral circuit is very simple, requiring only one chip and one tantalum capacitor for PWM output filtering, resulting in a compact system. The PWM signal input frequency range is broad, easily meeting the needs of this design.

STM32 Ultrasonic Obstacle Avoidance Car Project

Motor Driver Pin Table

 1 Control Chip: TB6612
 2 Number of Control Chips: 2
 3    TB6612 Pin Allocation for Chip 1:
 4     VM         PWMA--------->TIM1_CH1(PA8)
 5     VCC        AIN2--------->GPIOB_12
 6     GND        AIN1--------->GPIOB_13
 7     AO1        STBY--------->GPIOB_14
 8     AO2        BIN1--------->GPIOB_15
 9     BO2        BIN2--------->GPIOA_12
10     BO1        PWMB--------->TIM1_CH2(PA9)
11     GND        GND
12    TB6612 Pin Allocation for Chip 2:
13     VM         PWMA--------->TIM1_CH3(PA10)
14     VCC        AIN2--------->GPIOB_5
15     GND        AIN1--------->GPIOB_6
16     AO1        STBY--------->GPIOB_7
17     AO2        BIN1--------->GPIOB_8
18     BO2        BIN2--------->GPIOA_9
19     BO1        PWMB--------->TIM1_CH4(PA11)
20     GND        GND
21 Truth Table
22     AIN1   0     1     0     1
23     AIN2   0     0     1     1
24     BIN1   0     1     0     1
25     BIN2   0     0     1     1
26           Stop  Forward  Reverse  Brake

Timer Configuration for Motor

 1 // Initialize TIMX, set TIMx's ARR, PSC
 2 // arr: auto-reload initial value, psc is the prescale value, both control the timer clock cycle
 3 // Select Timer TIM1
 4 static void TB6612_ADVANCE_TIM1_Mode_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty)
 5 {
 6    //----------------- Timer Base Structure Initialization -------------------------/
 7    TIM_TimeBaseInitTypeDef TIM_TimeStructure;
 8    /* Enable Timer 1 clock, i.e., internal clock CK_INT=72M */
 9    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
10    TIM_DeInit(TIMx);
11    /* Internal clock as counter clock, 72MHZ */
12    TIM_InternalClockConfig(TIMx);
13    /* Auto-reload register value, after TIM_Period+1 frequencies generate an update or interrupt */
14    TIM_TimeStructure.TIM_Period=arr;
15    /* Clock prescale coefficient is 71, driving counter clock CK_CNT=CK_INT/(71+1)=1MHZ */
16    TIM_TimeStructure.TIM_Prescaler=psc-1;
17    /* Set clock division, TIM_CKD_DIV1=0, PWM wave has no delay */
18    TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;
19    /* Up counting mode */
20    TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;
21    /* Repeat counter */
22    TIM_TimeStructure.TIM_RepetitionCounter=0;
23    /* Initialize timer */
24    TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);
25    /* Enable ARR preload register (shadow register) */
26    TIM_ARRPreloadConfig(TIMx,ENABLE);
27    //----------------- Output Compare Structure Initialization -----------------------/
28    TIM_OCInitTypeDef   TIM_OCInitStructure;
29    /* PWM mode setting, set to PWM mode 1 */
30    TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
31    /* PWM output enables the corresponding IO port to output signals */
32    TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
33    /* Set duty cycle size, CCR1[15:0]: value of capture/compare channel 1, if CC1 channel is configured for output: CCR1 contains the value loaded into the current capture/compare 1 register (preload value). */
34    TIM_OCInitStructure.TIM_Pulse=duty;
35    /* Output channel level polarity setting */
36    TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
37    /* Initialize output compare parameters */
38    TIM_OC1Init(TIMx,&TIM_OCInitStructure);// Initialize TIM1 Channel 1
39    TIM_OC2Init(TIMx,&TIM_OCInitStructure);// Initialize TIM1 Channel 2
40    TIM_OC3Init(TIMx,&TIM_OCInitStructure);// Initialize TIM1 Channel 3
41    TIM_OC4Init(TIMx,&TIM_OCInitStructure);// Initialize TIM1 Channel 4
42    /* Auto-reload */
43    TIM_OC1PreloadConfig(TIMx,TIM_OCPreload_Enable);
44    TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);
45    TIM_OC3PreloadConfig(TIMx,TIM_OCPreload_Enable);
46    TIM_OC4PreloadConfig(TIMx,TIM_OCPreload_Enable);
47    /* Enable counter */
48    TIM_Cmd(TIMx,ENABLE);
49    /* Main output enable, if the corresponding enable bit is set (CCxE, CCxNE bits of TIMx_CCER register), OC and OCN outputs are enabled. */
50    TIM_CtrlPWMOutputs(TIMx,ENABLE);
51 }
52 // Advanced timer output channel initialization function
53 static void TB6612_ADVANCE_TIM_Gpio_Config()
54 {
55    GPIO_InitTypeDef  GPIO_InitStruct;
56    /*---------- Channel 1 Configuration --------------*/
57    /* Timer 1 output compare channel */
58    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
59    /* Configure as alternate push-pull output */
60    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
61    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
62    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
63    GPIO_Init(GPIOA,&GPIO_InitStruct);
64    /*----------- Channel 2 Configuration -------------*/
65    /* Timer 1 output compare channel */
66    /* Configure as alternate push-pull output */
67    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
68    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;
69    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
70    GPIO_Init(GPIOA,&GPIO_InitStruct);
71     /*----------- Channel 3 Configuration -------------*/
72    /* Timer 1 output compare channel */
73    /* Configure as alternate push-pull output */
74    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
75    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
76    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
77    GPIO_Init(GPIOA,&GPIO_InitStruct);
78     /*----------- Channel 4 Configuration -------------*/
79    /* Timer 1 output compare channel */
80    /* Configure as alternate push-pull output */
81    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
82    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
83    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
84    GPIO_Init(GPIOA,&GPIO_InitStruct);
85 }

Ultrasonic Module

The HC-SR04 ultrasonic module is used, which has a high level of integration and good stability, measuring distances very accurately and stably. The power supply voltage is DC5V with a current consumption of less than 10mA, and the detection distance ranges from 0.010m to 3.5m, with four pins: VCC (DC5V), Triger (transmitter), Echo (receiver), and GND (ground). The physical image of the HC-SR04 is shown below:

STM32 Ultrasonic Obstacle Avoidance Car Project

The module uses the microcontroller’s I/O to trigger the distance measurement. The microcontroller generates a high-level signal using a regular timer, after which the ultrasonic wave is autonomously sent at a frequency of 40kHz, and then waits for the signal to return. If a signal returns, the microcontroller’s I/O pin immediately outputs a high level, and the duration of the high level can be used to calculate the distance from the car to the obstacle. The final distance is the duration of the high level multiplied by the speed of sound in air, divided by 2, allowing for repeated distance measurements.
The working principle of the module is as follows: (1) The microcontroller triggers the pin, outputting a high-level signal; (2) The module’s transmitter automatically sends a square wave at a specific frequency; (3) If a signal returns, it outputs a high level via I/O, and the duration of the high level is the transmission time of the ultrasonic wave; (4) The distance tested = (high level duration * speed of sound (340M/S)) / 2.
Note: In hardware operations, ensure the module’s ground is connected first, as this can affect the module’s operation. During distance measurement, the placement of the object being measured should not be too chaotic, as this can affect the test results.

STM32 Ultrasonic Obstacle Avoidance Car Project

Important Ultrasonic Code (for reference)

 1/* Get the duration of the received high level (us) */
 2 uint32_t Get_HC_SR04_Time(void)
 3 {
 4    uint32_t t=0;
 5    t=Acoustic_Distance_Count*1000;//us
 6    t+=TIM_GetCounter(TIM2);// Get us
 7    TIM2->CNT =0;
 8    Acoustic_Distance_Count=0;
 9    Systic_Delay_us(100);
10    return t;
11 }
12/* Get distance */
13 void Get_HC_SR04_Distance(void)
14 {
15    static uint16_t count=0;
16    switch(count)
17    {
18        case 1:
19        {
20            GPIO_SetBits(Acoustic_Port,HC_SR04_Trig);// Trig sends a high level greater than 10us
21        }break;
22
23        case 15:
24        {
25            count=0;
26            GPIO_ResetBits(Acoustic_Port,HC_SR04_Trig);
27            while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==0);// When Echo is 0, start the timer
28            Open_Tim2();
29            while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==1);// When Echo is 1, stop the timer
30            Close_Tim2();
31            HC_SR04_Distance=(float)(Get_HC_SR04_Time()/5.78);
32
33        }break;
34        default:break;
35    }
36    count++;
37 }

Servo Module

This system uses the SG90 model servo, which is a common angle actuator. This system needs to determine the position of different obstacles and can apply a small amount of force for steering. The servo can be understood as a steering wheel, which is a common term. It is essentially a servo motor. The physical image of the servo is shown below:

STM32 Ultrasonic Obstacle Avoidance Car Project

The servo module interface is simple, with only three pins. The two outer wires are the power supply positive and negative terminals, while the middle wire is the PWM signal wire that connects directly to the control pin of the microcontroller. By controlling the pulse width output from the microcontroller’s pin, the angle of the servo can be controlled. For every 0.1ms increase, the servo rotates by 9 degrees.
0.5ms———0
1.0ms———45
1.5ms———90
2.0ms———135
2.5ms———–180
For a 20ms base pulse, if you want the servo to turn 90 degrees, a high level lasting for 1.5ms should be generated, with a period of 20ms, giving duty=1.5/20=7.5%. Here, the timer’s auto-reload register arr is set to 1000, so when the duty cycle is 75%, in the program, the duty cycle should be set to 75/1000=7.5%. This is the specific algorithm.

Important Servo Code (for reference)

 1/** PWM Pin Initialization */
 2 static void SERVO_Gpio_Init(void)
 3 {
 4    GPIO_InitTypeDef  GPIO_InitStruct;
 5    /*---------- Channel 2 Configuration --------------*/
 6    /* Timer 3 output compare channel */
 7    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
 8    /* Configure as alternate push-pull output */
 9    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
10    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7;
11    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
12    GPIO_Init(GPIOA,&GPIO_InitStruct); 
13 }
14// Timer 3 initialization, set TIMx's ARR, PSC
15// arr: auto-reload initial value, psc is the prescale value, both control the timer clock cycle
16 static void SERVO_TIM_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty)
17 {
18    //----------------- Timer Base Structure Initialization -------------------------/
19    TIM_TimeBaseInitTypeDef TIM_TimeStructure;
20    /* Enable Timer 3 clock, i.e., internal clock CK_INT=72M */
21    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
22    TIM_DeInit(TIMx);
23    /* Internal clock as counter clock, 72MHZ */
24    TIM_InternalClockConfig(TIMx);
25    /* Auto-reload register value, after TIM_Period+1 frequencies generate an update or interrupt */
26    TIM_TimeStructure.TIM_Period=arr;//1000 When the timer counts from 0 to 999, it produces one timing cycle
27    /* Clock prescale coefficient is 71, driving counter clock CK_CNT=CK_INT/(1440-1+1)=0.05MHZ */
28    TIM_TimeStructure.TIM_Prescaler=psc-1;;//1400  // Timer frequency is 5KHZ   
29    /* Set clock division, TIM_CKD_DIV1=0, PWM wave has no delay */
30    TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;
31    /* Up counting mode */
32    TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;
33    /* Repeat counter */
34    TIM_TimeStructure.TIM_RepetitionCounter=0;
35    /* Initialize timer */
36    TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);
37    /* Enable ARR preload register (shadow register) */
38    TIM_ARRPreloadConfig(TIMx,ENABLE);
39    //----------------- Output Compare Structure Initialization Start -----------------------/
40    TIM_OCInitTypeDef   TIM_OCInitStructure;
41    /* PWM mode setting, set to PWM mode 1 */
42    TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
43    /* PWM output enables the corresponding IO port to output signals */
44    TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
45    /* Set duty cycle size, CCR1[15:0]: value of capture/compare channel 1, if CC1 channel is configured for output: CCR1 contains the value loaded into the current capture/compare 1 register (preload value). */
46    TIM_OCInitStructure.TIM_Pulse=duty;   // Duty cycle size
47    /* Output channel level polarity setting */
48    TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
49    /* Initialize output compare parameters */
50    TIM_OC2Init(TIMx,&TIM_OCInitStructure);
51    //----------------- Output Compare Structure Initialization End -----------------------/    
52    /* Auto-reload */
53    TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);
54    /* Enable counter */
55    TIM_Cmd(TIMx,ENABLE);
56    /* Main output enable, if the corresponding enable bit is set (CCxE, CCxNE bits of TIMx_CCER register), OC and OCN outputs are enabled. */
57    TIM_CtrlPWMOutputs(TIMx,ENABLE);    
58 }
59 /* Servo PWM initialization
60   Each increase of 0.1ms corresponds to a 9-degree turn of the servo
610.5ms---------0
621.0ms---------45   
631.5ms---------90
642.0ms---------135
652.5ms-----------180
662.1ms    turn_left=150
670.8ms    turn_right=25
681.3ms    turn_front=75
6920ms base pulse, if you want the servo to turn 90 degrees, a high level lasting for 1.5ms should be generated, with a period of 20ms, giving duty=1.5/20=7.5% , and the timer's auto-reload register arr is set to 1000, so duty=75, the duty cycle is 75/1000=7.5%. 
70 */
71 void SERVO_Init(void)
72 {
73    SERVO_Gpio_Init();
74    SERVO_TIM_Config(TIM3,1000,1440,turn_front);
75 /** We set the value of the auto-reload register arr to 1000, and set the clock prescaler to 1440, then
76 the clock for the counter: CK_CNT = CK_INT / (1440-1+1)=0.05M, so the time for the counter to count once is:
77 1/CK_CNT=20us, when the counter counts to the value of ARR 1000, it generates an interrupt, then the time for one interrupt
78 is: 1/CK_CNT*ARR=20ms.
79 The frequency of the PWM signal f = TIM_CLK/{(ARR+1)*(PSC+1)}  TIM_CLK=72MHZ
80               = 72 000 000/(1000*1440)=5KHZ    
81 */    
82 }
83 /* Servo Angle Control */
84 void SERVO_Angle_Control(uint16_t Compare2)
85 {
86    TIM_SetCompare2(TIM3,Compare2);
87 }

Encoder Module

To adjust the car’s forward speed and obstacle avoidance speed, we use the EC11 rotary encoder, which can be used for adjusting parameters such as brightness, humidity, and volume. The shape of the EC11 encoder is similar to that of a potentiometer, with a knob in the center for adjusting the PWM signal. The physical image of the EC11 encoder is shown below:

STM32 Ultrasonic Obstacle Avoidance Car Project

OLED Display Module

The OLED display module is used to show the car’s speed, the values of the left and right encoders, and the battery voltage. It has a high resolution and low power consumption, normally only 0.06W, with a power supply voltage range of 3.3V-5V, and supports both IIC and SPI communication protocols. The brightness and contrast of the display module can be set through the program. Due to its long lifespan and other advantages, OLED is more suitable for small systems. In this system, due to the limited pins of the microcontroller, it is not suitable to use simple LCD1602 or 12864 for display; after comparison, the OLED performs better. The OLED display part is relatively simple, and you can implement it by referring to the examples from Zhongjingyuan.

This Week’s Benefits

STM32 Ultrasonic Obstacle Avoidance Car Project
STM32 Ultrasonic Obstacle Avoidance Car Project

1

“Open Source Project! Use ZigBee Module for Smart Home Remote Voice Control”

2

“Reviewing 14 Mainstream Embedded Operating Systems, How Many Do You Recognize?”

3

“Review | Common Classic Interview Questions for Embedded Software Engineers”

STM32 Ultrasonic Obstacle Avoidance Car Project
STM32 Ultrasonic Obstacle Avoidance Car Project
Share
STM32 Ultrasonic Obstacle Avoidance Car Project
Like
STM32 Ultrasonic Obstacle Avoidance Car Project
View

Leave a Comment