STM32循跡小車/Android藍牙控制小車(三)

STM32循跡小車/Android藍牙控制小車(三)

循跡藍牙小車的第三篇終於來了,這篇開篇先來介紹一下整個開發過程中得構思思路。本來這應該放在第一篇,但是實際思路會因爲開發過程中遇到的問題而改變,到今天爲止小車的三個目標功能都已經實現。所以在介紹app控制小車之前先來理清一下思路。

整個設計軟件方面分爲兩大塊:

一、STM32作爲主控制器,有三種工作模式,三種模式通過開發板上的物理按鍵或者App虛擬按鍵來進行切換:

1、紅外遙控——通過接受解碼紅外頭接收到的遙控信息來控制小車
2、循線跟蹤——通過循環掃描光電循跡模塊檢測黑線並且跟蹤黑線行駛
3、手機藍牙控制——HC-05藍牙模塊接收App通過藍牙發送的信息控制小車行駛

二、Android端APP:
App功能分爲兩大塊:藍牙連接、控制小車,兩大功能模塊通過兩個Activity來實現。

 藍牙連接包括:
 1、判斷當前設備是否支持藍牙
 2、判斷當前設備藍牙功能是否打開
 3、讀取已經配對的藍牙信息
 4、掃描周圍藍牙信息
 5、與指定藍牙發起配對
 6、與指定藍牙發起連接
 7、斷開連接、取消配對

控制小車:
控制小車其實就是和小車通訊,只需要通過已經連接藍牙的Socket通道,向小車端發送約定的信息,
小車端STM32接收到信息,解碼之後根據約定的含義控制小車。

先從App開始:

App開發軟件平臺AndroidStudio2.2.2
硬件平臺:Android手機,目標API 28,最低API 24。檢測平臺OPPO A7X

APP工程文件構架:

下圖詳解說明了每個文件的具體作用:
在這裏插入圖片描述

藍牙連接過程詳細說明請訪問Android 官服檔,有詳細說明:
Android 官服文檔 ——藍牙概覽

APP UI界面:
在這裏插入圖片描述

STM32藍牙接收端源碼:

	因爲TC-05藍牙模塊數據通過串口輸出,所以使用的時候不需要涉及到任何藍牙協議。直接把模塊當成串口來使用就行,代碼相對簡單:

串口初始化程序:

void LanYa_Init(void){
	
	GPIO_InitTypeDef GPIO_InitStrue;
	USART_InitTypeDef USART_InitStrue;
	NVIC_InitTypeDef NVIC_InitStrue;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//① 使能GPIOA時鐘
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//① 使能USART2時鐘
	
	GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStrue.GPIO_Pin=GPIO_Pin_2;
	GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;
  GPIO_Init(GPIOA,&GPIO_InitStrue);//②設置發送端IO
	
	GPIO_InitStrue.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStrue.GPIO_Pin=GPIO_Pin_3;
	GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;
  GPIO_Init(GPIOA,&GPIO_InitStrue);//②設置接收端IO
	
	USART_InitStrue.USART_BaudRate=9600;
	USART_InitStrue.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_InitStrue.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;
	USART_InitStrue.USART_Parity=USART_Parity_No;
	USART_InitStrue.USART_StopBits=USART_StopBits_1;
	USART_InitStrue.USART_WordLength=USART_WordLength_8b;	
	USART_Init(USART2,&USART_InitStrue);//③設置串口參數,波特率9600,發送接收雙工模式,無奇偶校驗,數據位8位,停止位1位,無流控制
	USART_Cmd(USART2,ENABLE);//使能串口1
	USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);//開啓接收中斷
	
	NVIC_InitStrue.NVIC_IRQChannel=USART2_IRQn;
	NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStrue.NVIC_IRQChannelSubPriority=1;//中斷優先級設置
	NVIC_Init(&NVIC_InitStrue);
}

//串口2中斷服務程序
void USART2_IRQHandler(void)
{

	if(USART_GetITStatus(USART2,USART_IT_RXNE))
 {
    LanYaRes = USART_ReceiveData(USART2); 	//藍牙接收到的數據
		LCD_ShowNum(30+8*16,70,1,5,16);
		flag = 0;
  }
}

int GetLanYaRes(void){
	if(flag == 0){
		flag = 1;
		return LanYaRes;
	}else return -1;
}

數據接收處理代碼:

		LCD_Clear(WHITE);
		LanYa_Init();		//藍牙接收初始化
//		USART_Cmd(USART2,ENABLE);//使能串口2
//		USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);//開啓串口2接收中斷
		pwm_sudu = 10000;	
		flagSudu = 0;
		while(mode1 == 2){ //模式3,App藍牙模式
			mode1 = GetMode();
			if(flagSudu == 0){
				PGout(13) = 0;
				PGout(14) = 0;
				flagSudu = 1;
			}
			Show_Str(30,30,200,16,"循跡小車藍牙調試界面",16,0);	
			POINT_COLOR=RED;
			LanYaRes1 = GetLanYaRes();
			Show_Str(30,70,200,16,"藍牙接收數據:",16,0);

			switch(LanYaRes1){
				case 48:{
						TIM_SetCompare4(TIM3,pwm_sudu);	//前進
						PGout(13) = 1;
						PGout(14) = 0;
						LanYaRes1 = -1;
				}break;
				case 50:{
						TIM_SetCompare4(TIM3,pwm_sudu);	//後退
						PGout(13) = 0;
						PGout(14) = 1;
						LanYaRes1 = -1;					
				}break;
				case 49:{
						TIM_SetCompare4(TIM3,0);				//停止
						PGout(13) = 0;
						PGout(14) = 0;
						LanYaRes1 = -1;					
				}break;
				case 51:{														//加速
						if(pwm_sudu <= 14000)pwm_sudu+=500;
						else pwm_sudu=8000;	
						TIM_SetCompare4(TIM3,pwm_sudu);
						LanYaRes1 = -1;					
				}break;	
				case 52:{														//減速
						if(pwm_sudu >=1000)pwm_sudu-=500;
						else pwm_sudu=0;
						TIM_SetCompare4(TIM3,pwm_sudu);
						LanYaRes1 = -1;					
				}break;
				case 54:{														//左轉
						TIM_SetCompare2(TIM3,MIN_zou);
						LanYaRes1 = -1;					
				}break;					
				case 53:{														//右轉
						TIM_SetCompare2(TIM3,MAX_you);
						LanYaRes1 = -1;					
				}break;	
				case 56:{														//右轉
						TIM_SetCompare2(TIM3,Z_qian);
						LanYaRes1 = -1;					
				}break;
				default:break;
			}
		}

代碼中設計一條LanYaRes1 = -1; 的代碼,爲什麼要在每次數據處理之後將LanYaRes1 = -1置爲-1?
因爲藍牙接收到的信息不是按鍵,如果不在每使用一次數據信息之後將數據清理,那麼同一條數據就會重複直行很多次,就像按鍵處理裏面的連續按鍵一樣。可以看到,在上面一個程序片段中,也有相似的代碼:flag = 0; return -1; 如下,也是同樣的原因。

if(USART_GetITStatus(USART2,USART_IT_RXNE))
 {
    LanYaRes = USART_ReceiveData(USART2); 	//藍牙接收到的數據
		LCD_ShowNum(30+8*16,70,1,5,16);
		flag = 0;
  }
}

int GetLanYaRes(void){
	if(flag == 0){
		flag = 1;
		return LanYaRes;
	}else return -1;
}

App端源碼:

public void onClick(View v) {
        switch (v.getId()){
            case R.id.BluetoothConnectivity:{   //連接藍牙
                startActivityForResult(new Intent(MainActivity.this,Main2Activity.class),1);
//                Toast.makeText(MainActivity.this,"按“搜索”開始掃描附近藍牙設備",Toast.LENGTH_LONG).show();
            }break;
            case R.id.Forward_Button:{          //前進
                if(writeTask != null)
                    writeTask.doInBackground(""+0);
            }break;
            case R.id.Stop_Button:{          //停止
                if(writeTask != null)
                    writeTask.doInBackground(""+1);
            }break;
            case R.id.Back_Button:{          //後退
                if(writeTask != null)
                    writeTask.doInBackground(""+2);
            }break;
            case R.id.Accelerate_Button:{          //加速
                if(writeTask != null)
                    writeTask.doInBackground(""+3);
            }break;
            case R.id.SlowDown_Button:{          //減速
                if(writeTask != null)
                    writeTask.doInBackground(""+4);
            }break;
            case R.id.Right_Button:{          //右轉
                if(writeTask != null)
                    writeTask.doInBackground(""+5);
            }break;
            case R.id.Left_Button:{          //左轉
                if(writeTask != null)
                    writeTask.doInBackground(""+6);
            }break;
            case R.id.Mode_Button:{          //模式切換
                if(writeTask != null)
                    writeTask.doInBackground(""+7);
            }break;
            case R.id.StraightTravel_Button:{          //直行
                if(writeTask != null)
                    writeTask.doInBackground(""+8);
            }break;
            default:writeTask.cancel(); //關閉藍牙通道
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
//        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case 1:{
                if(resultCode == RESULT_OK){
                    if(data.getParcelableExtra("connectBluetooth") != null)
                        currentBluetoothDevice = data.getParcelableExtra("connectBluetooth"); //接收反饋回來的當前連接藍牙設備
                    LanYa_TextView.setText("藍牙“" + currentBluetoothDevice.getName() + "”已連接");
                    if(((GlobalBlueSocket)getApplication()).getGlobalBlueSocket() != null)
                        mBluetoothSocket = ((GlobalBlueSocket)getApplication()).getGlobalBlueSocket();  //通過全局變量獲取藍牙套接字
                    writeTask =  new WriteTask(mBluetoothSocket);
               }
            }break;
            default:break;
        }
    }

關於藍牙連接以及相關的邏輯代碼,因爲涉及功能多邏輯複雜,將在下篇進行介紹。

STM32循跡小車/Android藍牙控制小車(一)
STM32循跡小車/Android藍牙控制小車(二)
STM32循跡小車/Android藍牙控制小車(三)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章