It took two weeks to finally get the car ready because for another project in the group, I was only deeply involved at the beginning and later stages of the project.It involves the improvement of ultrasound driver, car obstacle avoidance algorithm, and the complete code of Bluetooth control.
First, briefly describe the functions of the car: 1. ultrasonic obstacle avoidance; 2. Bluetooth control.
The Bluetooth control functions include 1. driving the car to move forward, backward, left and right; 2. driving the car out of the rectangle on the ground.
1. Problems and solutions encountered while debugging the car.
Unlike debugging C-language code, you can always see the value of each variable, and this time we are using the STM32C8T6 super mini-board.This board is very evil. The program that works normally on the mini board is identical to pin-to-pin diagram and port reuse, but it does not work on the mini board.
(1) Many inaccurate data returned by ultrasound results in abnormal car motion.
The data measured by the ultrasonic range element is not precise and is obviously affected by the actual environment, so there are many abnormal data in the large amount of returned data, which can lead to abnormal movement of the car.And our solution is clear - filtering out some apparently unusual data.Ultrasound module (HC-SR04) ranges from 0 to 400 cm. We filter out the data returned by the module which is larger than 500 cm, and improve the accuracy of ranging by averaging the data received several times, which makes the car more stable in motion.
(2) Lack of consideration for extreme situations
When designing the motion algorithm for the car, we did not take into account the abnormal data caused by the ultrasound module itself when the car was moving diagonally against the wall.Because this time our design is to place three ultrasound modules in front of the car to detect obstacles in front, left and right of the car.Our solution to this problem is: when the car decides that it should go straight, judge the data returned by the left and right ultrasonic modules simultaneously under the execution statement. If the distance between one side is less than 5 cm, the car turns to the other side.
(3) The relationship between ultrasound obstacle avoidance and Bluetooth control is not fully understood.
In the design of the program, we consider Bluetooth control and ultrasonic obstacle avoidance as two independent programs. The design idea is that Bluetooth control and ultrasonic obstacle avoidance cannot be executed simultaneously.But the fact is that Bluetooth control is initiated by serial interrupt, that is, the function of Bluetooth control is not affected while ultrasonic obstacle avoidance is running.However, Bluetooth control cannot change and keep the car moving for a long time, because after the serial interrupt service function is executed, the program will continue to cycle the car moving state continuously in the ultrasonic obstacle avoidance.In order to be able to jump out of obstacle avoidance during Bluetooth control, we add a while loop to the Bluetooth control loop, which jumps out when the difference between the value in the receiving data register and 57 is zero.So when the jump-out cycle condition is not met, the program will remain stuck in the while cycle and will not enter the obstacle-avoiding program.
(4) Then there is a temporary requirement - let the car run out of a rectangular pattern, the length and width of which can be customized each time through the Bluetooth serial port
The movement of the car is influenced by many external environments, such as the smoothness of the ground and the remaining battery capacity.For the design idea of this function algorithm, we first measure the driving speed of the car, and then calculate the turning angle speed of the car.Calculate how long it takes the car to turn 90 degrees based on its angular speed.The turning time of the car is then adjusted without interruption to make the car turn 90 degrees.
2. Program Source
(1) ultrasonic obstacle avoidance
#ifndef __HCSR_H #define __HCSR_H #include "sys.h" void TIM2_Cap_Init(u16 arr,u16 psc); #endif ----------------------------------------------------------------------- #include "sys.h" void TIM2_Cap_Init(u16 arr,u16 psc) { RCC->APB1ENR|=1<<0; //TIM2 Clock Enabling RCC->APB2ENR|=1<<2; //Enable PORTA clock RCC->APB2ENR|=1<<3; //Enable PORTB clock GPIOA->CRL&=0XFFFF000F; //PA0,PA1 set before clearing GPIOA->CRL|=0X00008880; //PA0,PA1 input GPIOA->ODR|=0<<1; //PA0,PA1 Dropdown GPIOA->ODR|=0<<2; //PA0,PA1 Dropdown GPIOA->ODR|=0<<3; //PA0,PA1 Dropdown GPIOB->CRL&=0X000FFFFF;//Pre-PB7 Cleanup Settings GPIOB->CRL|=0X33300000;//PB7 Push-Pull Output GPIOB->ODR|=1<<7; //PB7 Output High GPIOB->ODR|=1<<6; GPIOB->ODR|=1<<5; TIM2->ARR=arr; //Set counter auto-reset value TIM2->PSC=psc; //prescaler //CH2 TIM2->CCMR1|=1<<8; //CC2S=01 Select input IC1 to map to TI1 TIM2->CCMR1|=1<<12; //IC2F=0001 Configure input filter to sample with Fck_int, valid after 2 events TIM2->CCMR1|=0<<10; //IC2PS=00 Configure input crossover, no crossover TIM2->CCER|=0<<5; //CC2P=0 Rising Edge Capture TIM2->CCER|=1<<4; //CC2E=1 Allows the value of the capture counter to be transferred to the capture register //CH3 TIM2->CCMR2|=1<<0; //CC3S=01 Select input IC1 to map to TI1 TIM2->CCMR2|=1<<4; //IC3F=0001 Configure input filter to sample with Fck_int, valid after 2 events TIM2->CCMR2|=0<<2; //IC3PS=00 Configure input crossover, no crossover TIM2->CCER|=0<<9; //CC3P=0 Rising Edge Capture TIM2->CCER|=1<<8; //CC3E=1 Allows the value of the capture counter to be transferred to the capture register //CH4 TIM2->CCMR2|=1<<8; //CC4S=01 Select input IC1 to map to TI1 TIM2->CCMR2|=1<<12; //IC4F=0001 Configure input filter to sample with Fck_int, valid after 2 events TIM2->CCMR2|=0<<10; //IC4PS=00 Configure input crossover, no crossover TIM2->CCER|=0<<13; //CC4P=0 Rising Edge Capture TIM2->CCER|=1<<12; //CC4E=1 Allows the value of the capture counter to be transferred to the capture register //Interrupt Enabling TIM2->DIER|=1<<2; //Allow capture 2 interrupts TIM2->DIER|=1<<3; //Allow Capture 3 Interrupts TIM2->DIER|=1<<4; //Allow capture of 4 interrupts TIM2->DIER|=1<<0; //Allow update interruption TIM2->CR1|=0x01; //Enable Timer 2 MY_NVIC_Init(2,0,TIM2_IRQn,2);//Preemption 2, Subpriority 0, Group 2 }
(2) Bluetooth control function
#ifndef __SMG_H #define __SMG_H #include "sys.h" #include "delay.h" #define USART_REC_LEN > 200 // Define the maximum number of characters to receive #define EN_USART3_RX > 1 //enable (1)/disable (0) serial port reception extern u8 USART3_RX_BUF[USART_REC_LEN]; //Receive buffer, maximum USART_REC_LEN bytes, last byte is a line break extern u16 USART3_RX_STA; //Receive status flag #endif ------------------------------------------------------------------------ #include "sys.h" #include "lanya.h" #include "delay.h" #include "usart.h" #include "dianji.h" u8 BTFlag; #if EN_USART1_RX void Juxing()//Rectangular Motion Function of Cart { u8 chang,kuan,x,y;//chang, kuan are the values of the length and width of the rectangle the car will take, respectively delay_ms(200); printf("Chang:\r\n"); while(1) { printf("Input Chang:\r\n"); delay_ms(200); if(USART1->SR&(1<<5))//Jump out of loop when serial port receives message { chang=USART1->DR-'0';//Convert character data to integer data break; } } printf("Long:%d\r\n",chang);//Print data received by serial port USART1->SR=0;//Receive flag bits of serial port zeroed in preparation for next receive width data while(1) { printf("Input Kuan:\r\n"); delay_ms(200); if(USART1->SR&(1<<5)) { kuan=USART1->DR-'0';//Jump out of loop when serial port receives data break; } } printf("wide:%d\r\n",kuan);//Print Width Data //Current trolley speed is 0.25 m/sec. The trolley uses 40 ms for every 1 cm walk, so it takes 40 ms for every 1 cm walk to drive the trolley with the for function for(x=0;x<chang*10;x++)//Straight Length { GO(300,300); delay_ms(40); } RIGHT(300,300);//Right Turn delay_ms(785); for(y=0;y<kuan*10;y++)//Straight Walk Width { GO(300,300); delay_ms(40); } RIGHT(300,300);//Right Turn delay_ms(785); for(x=0;x<chang*10;x++)//Straight Length { GO(300,300); delay_ms(40); } RIGHT(300,300);//Right Turn delay_ms(785); for(y=0;y<kuan*10;y++)//Straight Walk Width { GO(300,300); delay_ms(40); } RIGHT(300,300);//Right Turn delay_ms(785); STOP(); } //Send character message through Bluetooth serial port Control car movement by judging character message u8 USART1_RX_BUF[USART_REC_LEN]; u16 USART1_RX_STA=0; void USART1_IRQHandler(void) { char res; //Define a character variable'res' if(USART1->SR&(1<<5)) { res=USART1->DR;// Make res equal to the data received by the serial port printf("\r\n%d",res); //Change the car motion state by judging the value of res and send the motion state to the mobile phone through the serial port if(res==50)// { GO(300,300);//Car Forward printf("\r\nGo Stright"); } else if(res==56) { BACK(300,300);//Car Back printf("\r\nGo Back"); } else if(res== 52) { LEFT(300,300);//Car turns left printf("\r\nTurn Left"); } else if(res==54) { RIGHT(300,300);//Car turns right printf("\r\nTurn Right"); } else if(res==53) { STOP();//Car stop printf("\r\nStop"); } else if(res=='7')//Car walk rectangle { delay_ms(200); Juxing();//Rectangular Motion Function of Cart } } } #endif
(3) Principal functions
#include "sys.h" #include "delay.h" #include "usart.h" #include "hcsr.h" #include "lanya.h" #include "dianji.h" //Timer 2 Channel 1-4 Input Capture Configuration //arr: auto-reload value //psc: clock Prescale frequency //Capture Status //[7]: 0, no successful capture; 1, successful capture once. //[6]: 0, not yet captured high level; 1, captured high level. //[5:0]: Number of overflows after capturing a high level u8 TIM2CH1_CAPTURE_STA=0; //Input Capture Status u16 TIM2CH1_CAPTURE_Date2; //Data 2 u16 TIM2CH1_CAPTURE_Date1; //Data 1 //CH2 u8 TIM2CH2_CAPTURE_STA=0; //Input Capture Status u16 TIM2CH2_CAPTURE_Date2; //Data 2 u16 TIM2CH2_CAPTURE_Date1; //Data 1 //CH3 u8 TIM2CH3_CAPTURE_STA=0; //Input Capture Status u16 TIM2CH3_CAPTURE_Date2; //Data 2 u16 TIM2CH3_CAPTURE_Date1; //Data 1 //CH4 u8 TIM2CH4_CAPTURE_STA=0; //Input Capture Status u16 TIM2CH4_CAPTURE_Date2; //Data 2 u16 TIM2CH4_CAPTURE_Date1; //Data 1 //Timer 2 interrupt service program void TIM2_IRQHandler(void) { u16 tsr; tsr=TIM2->SR; //CH2 Interrupt Processing if((TIM2CH2_CAPTURE_STA&0X80)==0)//Not Captured Successfully { if(tsr&0X01)//overflow { if(TIM2CH2_CAPTURE_STA&0X40)//High level captured { if((TIM2CH2_CAPTURE_STA&0X3F)==0X3F)//High level is too long { TIM2CH2_CAPTURE_STA|=0X80;//Tag captured once successfully TIM2CH2_CAPTURE_Date2=0XFFFF; }else TIM2CH2_CAPTURE_STA++; } } if(tsr&0x04)//Capture 2 A capture event occurs { if(TIM2CH2_CAPTURE_STA&0X40) //Capture a falling edge { TIM2CH2_CAPTURE_STA|=0X80; //Marker successfully captured a high level pulse width TIM2CH2_CAPTURE_Date2=TIM2->CCR2; //Get the current capture value. TIM2->CCER&=~(1<<5); //CC2P=0 set to Rising Edge Capture }else //Not yet started, capturing the rising edge for the first time { TIM2CH2_CAPTURE_Date2=0; TIM2CH2_CAPTURE_STA=0X40; //Tag captured rising edge TIM2CH2_CAPTURE_Date1=TIM2->CCR2; TIM2->CCER|=1<<5; //CC2P=1 set to Down Edge Capture } } } //CH3 Interrupt Processing if((TIM2CH3_CAPTURE_STA&0X80)==0)//Not Captured Successfully { if(tsr&0X01)//overflow { if(TIM2CH3_CAPTURE_STA&0X40)//High level captured { if((TIM2CH3_CAPTURE_STA&0X3F)==0X3F)//High level is too long { TIM2CH3_CAPTURE_STA|=0X80;//Tag captured once successfully TIM2CH3_CAPTURE_Date2=0XFFFF; }else TIM2CH3_CAPTURE_STA++; } } if(tsr&0x08)//Capture 3 A capture event occurs { if(TIM2CH3_CAPTURE_STA&0X40) //Capture a falling edge { TIM2CH3_CAPTURE_STA|=0X80; //Marker successfully captured a high level pulse width TIM2CH3_CAPTURE_Date2=TIM2->CCR3; //Get the current capture value. TIM2->CCER&=~(1<<9); //CC3P=0 set to Rising Edge Capture }else //Not yet started, capturing the rising edge for the first time { TIM2CH3_CAPTURE_Date2=0; TIM2CH3_CAPTURE_STA=0X40; //Tag captured rising edge TIM2CH3_CAPTURE_Date1=TIM2->CCR3; TIM2->CCER|=1<<9; //CC3P=1 set to Down Edge Capture } } } //Interrupt handling in CH4 if((TIM2CH4_CAPTURE_STA&0X80)==0)//Not Captured Successfully { if(tsr&0X01)//overflow { if(TIM2CH4_CAPTURE_STA&0X40)//High level captured { if((TIM2CH4_CAPTURE_STA&0X3F)==0X3F)//High level is too long { TIM2CH4_CAPTURE_STA|=0X80;//Tag captured once successfully TIM2CH4_CAPTURE_Date2=0XFFFF; }else TIM2CH4_CAPTURE_STA++; } } if(tsr&0x10)//Capture 4 A capture event occurs { if(TIM2CH4_CAPTURE_STA&0X40) //Capture a falling edge { TIM2CH4_CAPTURE_STA|=0X80; //Marker successfully captured a high level pulse width TIM2CH4_CAPTURE_Date2=TIM2->CCR4; //Get the current capture value. TIM2->CCER&=~(1<<13); //CC4P=0 set to Rising Edge Capture }else //Not yet started, capturing the rising edge for the first time { TIM2CH4_CAPTURE_Date2=0; TIM2CH4_CAPTURE_STA=0X40; //Tag captured rising edge TIM2CH4_CAPTURE_Date1=TIM2->CCR4; TIM2->CCER|=1<<13; //CC4P=1 set to Down Edge Capture } } } TIM2->SR=0;//Clear interrupt flag bits } u32 CEJU2(void) { while(1) { u32 temp2=0; PBout(7)=1; //Open TRIG delay_us(20); PBout(7)=0; //Close TRIG if(TIM2CH2_CAPTURE_STA&0X80) //Successfully captured a high level { temp2=TIM2CH2_CAPTURE_STA&0X3F; temp2*=65536; //Total Overflow Time temp2+=TIM2CH2_CAPTURE_Date2; temp2-=TIM2CH2_CAPTURE_Date1; //Get total high level time temp2*=0.017; //Get Distance printf("TIM2_CH2_LENGTH:%d cm\r\n",temp2); //Print Distance TIM2CH2_CAPTURE_Date1=0; TIM2CH2_CAPTURE_STA=0; //Open next capture } if(temp2<500) return temp2; //Return distance value else return 0; } } u32 CEJU3(void) { while(1) { u32 temp2=0; PBout(6)=1; delay_us(20); PBout(6)=0; if(TIM2CH3_CAPTURE_STA&0X80) //Successfully captured a high level { temp2=TIM2CH3_CAPTURE_STA&0X3F; temp2*=65536; //Total Overflow Time temp2+=TIM2CH3_CAPTURE_Date2; temp2-=TIM2CH3_CAPTURE_Date1; //Get total high level time temp2*=0.017; printf("TIM2_CH3_LENGTH:%d cm\r\n",temp2); TIM2CH3_CAPTURE_Date1=0; TIM2CH3_CAPTURE_STA=0; //Open next capture } if(temp2<500) return temp2; else return 0; } } u32 CEJU4(void) { while(1) { u32 temp2=0; PBout(5)=1; delay_us(20); PBout(5)=0; if(TIM2CH4_CAPTURE_STA&0X80) //Successfully captured a high level { temp2=TIM2CH4_CAPTURE_STA&0X3F; temp2*=65536; //Total Overflow Time temp2+=TIM2CH4_CAPTURE_Date2; temp2-=TIM2CH4_CAPTURE_Date1; //Get total high level time temp2*=0.017; printf("TIM2_CH4_LENGTH:%d cm\r\n",temp2); TIM2CH4_CAPTURE_Date1=0; TIM2CH4_CAPTURE_STA=0; //Open next capture } if(temp2<500) return temp2; else return 0; } } int main(void) { u32 i=3,j; u32 temp2=0,temp3=0,temp4=0; extern u8 opq; Stm32_Clock_Init(9); //Initialize the system clock delay_init(72); //Initialization Delay uart_init(72,9600); //Serial port initialization TIM2_Cap_Init(0XFFFF,72-1); //Ultrasound initialization, counted at 2 Mhz frequency TIM_PWM1_Init(1000-1,36-1); //Motor initialization, no frequency division.PWM Frequency=72000/(899+1)=80Khz printf("Choose moudle:"); CEJU2(); //Ultrasound Pre-Initialization CEJU3(); CEJU4(); delay_ms(1000); while(1) { while(USART1->DR-57) //Jump out of the loop when receiving data of type 9 { ; } temp2=0; //For each new cycle, zero temp2, temp3, temp4 temp3=0; temp4=0; while(i) //Detect the value of three positive ultrasound returns { j=CEJU4(); temp4+=j; if(j!=0) i--; } temp4=temp4/3; //Take the average of the values returned by the third ultrasound to reduce the error i=3; j=0; if(temp4<40) //If the distance ahead is less than 40cm, make a judgment { while(i) //Detect the value of three times left ultrasound return { j=CEJU2(); temp2+=j; if(j!=0) i--; } temp2=temp2/3; i=3; j=0; if(temp2<40) //If the left distance is less than 40cm, make a judgment { while(i) //Detect the value of three right ultrasound returns { j=CEJU3(); temp3+=j; if(j!=0) i--; } temp3=temp3/3; i=3; j=0; if(temp3<40) //If the right distance is less than 40cm, go back and turn left { BACK(300,300); printf("back\r\n"); delay_ms(500); LEFT(300,300); delay_ms(500); } else //Turn right if the right distance is greater than 40cm { RIGHT(300,300); printf("right\r\n"); delay_ms(500); } } else //Turn left if the right distance is greater than 40cm { LEFT(300,300); printf("left\r\n"); delay_ms(500); } } else //If the front distance is greater than 40cm, judge { while(i) //Detect the value of three times left ultrasound return { j=CEJU2(); temp2+=j; if(j!=0) i--; } temp2=temp2/3; i=3; j=0; if(temp2<=5) //Turn right if left distance is less than 5cm { RIGHT(200,200); delay_ms(500); } while(i) //Detect the value of three right ultrasound returns { j=CEJU3(); temp3+=j; if(j!=0) i--; } temp3=temp3/3; i=3; j=0; if(temp3<=5) //Turn left if the right distance is less than 5cm { LEFT(200,200); delay_ms(500); } GO(300,300); //Straight delay_ms(500); printf("go\r\n"); } TIM2->CNT=0; //Zero CNT value for each loop } }