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蓝牙控制小车(三)

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