加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入

第18章-綜合以上功能 基于stm32的智能小車(遠(yuǎn)程控制、避障、循跡) 超詳細(xì)

06/24 09:59
2222
服務(wù)支持:
技術(shù)交流群

完成交易后在“購買成功”頁面掃碼入群,即可與技術(shù)大咖們分享疑惑和經(jīng)驗(yàn)、收獲成長和認(rèn)同、領(lǐng)取優(yōu)惠和紅包等。

虛擬商品不可退

當(dāng)前內(nèi)容為數(shù)字版權(quán)作品,購買后不支持退換且無法轉(zhuǎn)移使用。

加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論
放大
實(shí)物圖
  • 方案介紹
    • 第18章-綜合以上功能
  • 相關(guān)文件
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

這個(gè)是全網(wǎng)最詳細(xì)的STM32項(xiàng)目教學(xué)視頻。
第一篇在這里:
視頻在這里


STM32智能小車V3-STM32入門教程-openmv與STM32循跡小車-stm32f103c8t6-電賽 嵌入式學(xué)習(xí) PID控制算法 編碼器電機(jī) 跟隨

第18章-綜合以上功能

18-按鍵和app按鈕切換功能

根據(jù)上面介紹,我們的模式可以有:

**OLED顯示模式: 速度、里程、電壓、超聲波數(shù)據(jù)、MPU6050俯仰角、橫滾角、航向角 數(shù)據(jù)顯示在OLED上和通過串口發(fā)送藍(lán)牙APP **

PID循跡模式:紅外對管PID循跡

手機(jī)遙控普通運(yùn)動(dòng)模式:遙控前、后、左、右加速運(yùn)動(dòng)

超聲波避障模式

PID跟隨模式:超聲波PID定距離跟隨

手機(jī)遙控角度閉環(huán)模式:MPU6050角度PID控制

可以設(shè)置標(biāo)志位通過按鍵改變標(biāo)志位,以實(shí)現(xiàn)功能切換。

定義一個(gè)全局變量,

uint8_t g_ucMode = 0; 
//小車運(yùn)動(dòng)模式標(biāo)志位 0:顯示功能、1:PID循跡模式、2:手機(jī)遙控普通運(yùn)動(dòng)模式、3.超聲波避障模式、4:PID跟隨模式、5:遙控角度閉環(huán)

在這里插入圖片描述

uint8_t g_ucMode = 0; //小車運(yùn)動(dòng)模式標(biāo)志位

在gpio.h聲明一下

在這里插入圖片描述

extern uint8_t g_ucMode ; //小車運(yùn)動(dòng)模式標(biāo)志位

按鍵中斷回調(diào)函數(shù)里面補(bǔ)充按下按鍵后的處理

先不進(jìn)行消抖,如果后面KEY1 KEY2效果不好再消抖
在這里插入圖片描述

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == KEY1_Pin) //判斷一下那個(gè)引腳觸發(fā)中斷
	{
		//這里編寫觸發(fā)中斷后要執(zhí)行的程序
		if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5 
		else
		{
			g_ucMode+=1;
		}
		HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
	}
	if(GPIO_Pin == KEY2_Pin) //判斷一下那個(gè)引腳觸發(fā)中斷
	{
		//這里編寫觸發(fā)中斷后要執(zhí)行的程序
		g_ucMode=0;
		HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
	}
}

然后主函數(shù)顯示當(dāng)前處于的模式

然后判斷當(dāng)前模式 執(zhí)行不同代碼

方法:一個(gè)功能一個(gè)功能的添加代碼,添加好一個(gè)調(diào)試測試一下,然后再添加下一個(gè)

下面這個(gè)就是我們主函數(shù)的代碼。

	sprintf((char *)OledString," g_ucMode:%d",g_ucMode);//顯示g_ucMode 當(dāng)前模式
	OLED_ShowString(0,6,OledString,12);	//顯示在OLED上
	
	sprintf((char *)Usart3String," g_ucMode:%d",g_ucMode);//藍(lán)牙APP顯示
	HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式發(fā)送通過串口三輸出字符 strlen:計(jì)算字符串大小
	
	if(g_ucMode == 0)
	{
	//0LED顯示功能
		sprintf((char*)OledString, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//顯示速度
		OLED_ShowString(0,0,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
		
		sprintf((char*)OledString, "Mileage:%.2f", Mileage);//顯示里程
		OLED_ShowString(0,1,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
		
		sprintf((char*)OledString, "U:%.2fV", adcGetBatteryVoltage());//顯示電池電壓
		OLED_ShowString(0,2,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
		
		sprintf((char *)OledString,"HC_SR04:%.2fcmrn",HC_SR04_Read());//顯示超聲波數(shù)據(jù)
		OLED_ShowString(0,3,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
		
		sprintf((char *)OledString,"p:%.2f r:%.2f rn",pitch,roll);//顯示6050數(shù)據(jù) 俯仰角 橫滾角
		OLED_ShowString(0,4,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
		
		sprintf((char *)OledString,"y:%.2f  rn",yaw);//顯示6050數(shù)據(jù)  航向角
		OLED_ShowString(0,5,OledString,12);//這個(gè)是oled驅(qū)動(dòng)里面的,是顯示位置的一個(gè)函數(shù),
		
	//藍(lán)牙APP顯示
		sprintf((char*)Usart3String, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//顯示速度
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式發(fā)送通過串口三輸出字符 strlen:計(jì)算字符串大小
		//阻塞方式發(fā)送可以保證數(shù)據(jù)發(fā)送完畢,中斷發(fā)送不一定可以保證數(shù)據(jù)已經(jīng)發(fā)送完畢才啟動(dòng)下一次發(fā)送
		sprintf((char*)Usart3String, "Mileage:%.2f", Mileage);//顯示里程
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式發(fā)送通過串口三輸出字符 strlen:計(jì)算字符串大小
		
		sprintf((char*)Usart3String, "U:%.2fV", adcGetBatteryVoltage());//顯示電池電壓
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式發(fā)送通過串口三輸出字符 strlen:計(jì)算字符串大小
		
		sprintf((char *)Usart3String,"HC_SR04:%.2fcmrn",HC_SR04_Read());//顯示超聲波數(shù)據(jù)
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式發(fā)送通過串口三輸出字符 strlen:計(jì)算字符串大小
		
		sprintf((char *)Usart3String,"p:%.2f r:%.2f rn",pitch,roll);//顯示6050數(shù)據(jù) 俯仰角 橫滾角
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式發(fā)送通過串口三輸出字符 strlen:計(jì)算字符串大小
		
		sprintf((char *)Usart3String,"y:%.2f  rn",yaw);//顯示6050數(shù)據(jù)  航向角
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式發(fā)送通過串口三輸出字符 strlen:計(jì)算字符串大小
	
		//獲得6050數(shù)據(jù)
		while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){}  //這個(gè)可以解決經(jīng)常讀不出數(shù)據(jù)的問題
		
		//顯示模式電機(jī)停轉(zhuǎn)
		motorPidSetSpeed(0,0);
	}
	if(g_ucMode == 1)
	{
	///****    紅外PID循跡功能******************/
	g_ucaHW_Read[0] = READ_HW_OUT_1;//讀取紅外對管狀態(tài)、這樣相比于寫在if里面更高效
	g_ucaHW_Read[1] = READ_HW_OUT_2;
	g_ucaHW_Read[2] = READ_HW_OUT_3;
	g_ucaHW_Read[3] = READ_HW_OUT_4;

	if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )
	{
//		printf("應(yīng)該前進(jìn)rn");//注釋掉更加高效,減少無必要程序執(zhí)行
		g_cThisState = 0;//前進(jìn)
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )//使用else if更加合理高效
	{
//		printf("應(yīng)該右轉(zhuǎn)rn");
		g_cThisState = -1;//應(yīng)該右轉(zhuǎn)
	}
	else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )
	{
//		printf("快速右轉(zhuǎn)rn");
		g_cThisState = -2;//快速右轉(zhuǎn)
	}
	else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0)
	{
//		printf("快速右轉(zhuǎn)rn");
		g_cThisState = -3;//快速右轉(zhuǎn)
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 0 )
	{
//		printf("應(yīng)該左轉(zhuǎn)rn");
		g_cThisState = 1;//應(yīng)該左轉(zhuǎn)	
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 1 )
	{
//		printf("快速左轉(zhuǎn)rn");
		g_cThisState = 2;//快速左轉(zhuǎn)
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 1)
	{
//	    printf("快速左轉(zhuǎn)rn");
		g_cThisState = 3;//快速左轉(zhuǎn)
	}
	g_fHW_PID_Out = PID_realize(&pidHW_Tracking,g_cThisState);//PID計(jì)算輸出目標(biāo)速度 這個(gè)速度,會(huì)和基礎(chǔ)速度加減

	g_fHW_PID_Out1 = 3 + g_fHW_PID_Out;//電機(jī)1速度=基礎(chǔ)速度+循跡PID輸出速度
	g_fHW_PID_Out2 = 3 - g_fHW_PID_Out;//電機(jī)1速度=基礎(chǔ)速度-循跡PID輸出速度
	if(g_fHW_PID_Out1 >5) g_fHW_PID_Out1 =5;//進(jìn)行限幅 限幅速度在0-5之間
	if(g_fHW_PID_Out1 <0) g_fHW_PID_Out1 =0;
	if(g_fHW_PID_Out2 >5) g_fHW_PID_Out2 =5;
	if(g_fHW_PID_Out2 <0) g_fHW_PID_Out2 =0;
	if(g_cThisState != g_cLastState)//如何這次狀態(tài)不等于上次狀態(tài)、就進(jìn)行改變目標(biāo)速度和控制電機(jī)、在定時(shí)器中依舊定時(shí)控制電機(jī)
	{
		motorPidSetSpeed(g_fHW_PID_Out1,g_fHW_PID_Out2);//通過計(jì)算的速度控制電機(jī)
	}
	
	g_cLastState = g_cThisState;//保存上次紅外對管狀態(tài)	

	}
	if(g_ucMode == 2)
	{
		//***************遙控模式***********************//
		//遙控模式的控制在串口三的中斷里面
	}
	if(g_ucMode == 3)
	{
		//******超聲波避障模式*********************//
避障邏輯
		if(HC_SR04_Read() > 25)//前方無障礙物
		{
			motorPidSetSpeed(1,1);//前運(yùn)動(dòng)
			HAL_Delay(100);
		}
		else{	//前方有障礙物
			motorPidSetSpeed(-1,1);//右邊運(yùn)動(dòng) 原地	
			HAL_Delay(500);
			if(HC_SR04_Read() > 25)//右邊無障礙物
			{
				motorPidSetSpeed(1,1);//前運(yùn)動(dòng)
				HAL_Delay(100);
			}
			else{//右邊有障礙物
				motorPidSetSpeed(1,-1);//左邊運(yùn)動(dòng) 原地
				HAL_Delay(1000);
				if(HC_SR04_Read() >25)//左邊無障礙物
				{
					 motorPidSetSpeed(1,1);//前運(yùn)動(dòng)
					HAL_Delay(100);
				}
				else{
					motorPidSetSpeed(-1,-1);//后運(yùn)動(dòng)
					HAL_Delay(1000);
					motorPidSetSpeed(-1,1);//右邊運(yùn)動(dòng)
					HAL_Delay(50);
				}
			}
		}
	}
	if(g_ucMode == 4)
	{
	//**********PID跟隨功能***********//
		g_fHC_SR04_Read=HC_SR04_Read();//讀取前方障礙物距離
		if(g_fHC_SR04_Read < 60){  //如果前60cm 有東西就啟動(dòng)跟隨
			g_fFollow_PID_Out = PID_realize(&pidFollow,g_fHC_SR04_Read);//PID計(jì)算輸出目標(biāo)速度 這個(gè)速度,會(huì)和基礎(chǔ)速度加減
			if(g_fFollow_PID_Out > 6) g_fFollow_PID_Out = 6;//對輸出速度限幅
			if(g_fFollow_PID_Out < -6) g_fFollow_PID_Out = -6;
			motorPidSetSpeed(g_fFollow_PID_Out,g_fFollow_PID_Out);//速度作用與電機(jī)上
		}
		else motorPidSetSpeed(0,0);//如果前面60cm 沒有東西就停止
		HAL_Delay(10);//讀取超聲波傳感器不能過快
	}
	if(g_ucMode == 5)
	{
	//*************MPU6050航向角 PID轉(zhuǎn)向控制*****************//

		sprintf((char *)Usart3String,"pitch:%.2f roll:%.2f yaw:%.2frn",pitch,roll,yaw);//顯示6050數(shù)據(jù) 俯仰角 橫滾角 航向角
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),0xFFFF);//通過串口三輸出字符 strlen:計(jì)算字符串大小	
	   
	   //mpu_dmp_get_data(&pitch,&roll,&yaw);//返回值:0,DMP成功解出歐拉角
		while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){}  //這個(gè)可以解決經(jīng)常讀不出數(shù)據(jù)的問題
		
		
		g_fMPU6050YawMovePidOut = PID_realize(&pidMPU6050YawMovement,yaw);//PID計(jì)算輸出目標(biāo)速度 這個(gè)速度,會(huì)和基礎(chǔ)速度加減

		g_fMPU6050YawMovePidOut1 = 1.5 + g_fMPU6050YawMovePidOut;//基礎(chǔ)速度加減PID輸出速度
		g_fMPU6050YawMovePidOut2 = 1.5 - g_fMPU6050YawMovePidOut;
		if(g_fMPU6050YawMovePidOut1 >3.5) g_fMPU6050YawMovePidOut1 =3.5;//進(jìn)行限幅
		if(g_fMPU6050YawMovePidOut1 <0) g_fMPU6050YawMovePidOut1 =0;
		if(g_fMPU6050YawMovePidOut2 >3.5) g_fMPU6050YawMovePidOut2 =3.5;
		if(g_fMPU6050YawMovePidOut2 <0) g_fMPU6050YawMovePidOut2 =0;
		motorPidSetSpeed(g_fMPU6050YawMovePidOut1,g_fMPU6050YawMovePidOut2);
	
	}

可以測試上面的代碼 然后沒有問題后,我們添加一個(gè)通過藍(lán)牙APP按鈕切換模式代碼
在這里插入圖片描述

		if(g_ucUsart3ReceiveData == 'J') //改變模式
		{
			if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5 
			else
			{
				g_ucMode+=1;
			}
		}
		if(g_ucUsart3ReceiveData == 'K') g_ucMode=0;//設(shè)置為顯示模式

然后對應(yīng)APP也要添加 按鈕設(shè)置
在這里插入圖片描述
我們
按鍵沒有消抖效果不好,我們消抖一下
在這里插入圖片描述
我們增加了 HAL延時(shí)和再次判斷電平

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == KEY1_Pin) //判斷一下那個(gè)引腳觸發(fā)中斷
	{
		HAL_Delay(10);//延時(shí)消抖 主要
		if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_SET)//判斷KEY1引腳仍為高電平
		{
			//這里編寫觸發(fā)中斷后要執(zhí)行的程序
			if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5 
			else
			{
				g_ucMode+=1;
			}
			HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
		}
	}
	if(GPIO_Pin == KEY2_Pin) //判斷一下那個(gè)引腳觸發(fā)中斷
	{
		HAL_Delay(10);//延時(shí)消抖
		if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_RESET)//判斷KEY2引腳仍為低電平
		{
			//這里編寫觸發(fā)中斷后要執(zhí)行的程序
			g_ucMode=0;
			HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
		}
	}
}

但是測試不能執(zhí)行中斷,程序異常卡死了

原因是HAL_Delay使用的是sysTick 中斷優(yōu)先級在軟件初始化是默認(rèn)最低的,比外部中斷優(yōu)先級低,所以HAL_Delay不能在外部中斷服務(wù)函數(shù)中調(diào)用。

所以我們可以通過提高sysTick 中斷的優(yōu)先級,提高的比HAL_Delay高。
在這里插入圖片描述
然后我們提高至 如下圖
在這里插入圖片描述
然后編譯燒錄測試按鍵是否更加穩(wěn)定。
下面的章節(jié)我們講解視覺,RTOS系統(tǒng),電磁循跡等功能

聯(lián)系:Q,1930299709

  • 聯(lián)系方式.txt

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險(xiǎn)等級 參考價(jià)格 更多信息
NX3225SA-114.285MHZ-EXS00A-CS06528 1 Nihon Dempa Kogyo Co Ltd Parallel - 3Rd Overtone Quartz Crystal, 114.285MHz Nom,
$33.41 查看
DP83848IVVX/NOPB 1 Texas Instruments Industrial temperature, 10/100-Mbps Ethernet PHY transceiver with SNI &amp; JTAG support 48-LQFP -40 to 85

ECAD模型

下載ECAD模型
$5.13 查看
HFBR-2412TZ 1 Foxconn Receiver, ST Connector, Through Hole Mount, ROHS COMPLIANT, PLASTIC, 8 PIN
$41.26 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜