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藍牙控制小車(三)