目录

平衡车-遥控器

平衡车 – 遥控器

https://i-blog.csdnimg.cn/blog_migrate/65e2c919493111fe0c134761ea131fab.png

🌈个人主页:

💫个人格言:“成为自己未来的主人~”

https://i-blog.csdnimg.cn/blog_migrate/8a9e7fcf23d9bad5b435af5495db19bd.gif

今天,我们来实现平衡车的最后一个功能,通过APP的蓝牙来遥控平衡小车

https://i-blog.csdnimg.cn/direct/1f7c66f5e26347e2ad905f10f3cba232.png

我们通过蓝牙芯片可以获取手机上的操作,然后给到单片机的串口3.以便于后面的单片机进行处理。

接收

我们讲一下如何通过串口3来接收APP的数据。

可以看到,我们指令格式后面有一个换行符,所以我们要每次接收到一行数据。

https://i-blog.csdnimg.cn/direct/39351556cd8c431fbf343bafc5526eeb.png

RXNE是接收数据中断,当串口3接收到数据的时候,就会触发对应的中断。

我们将接收到的数据存放到intBuf数组当中,然后由transBuf进行转存,最后由进程函数进行获取。

那么接下来,我们创建两个文件来进行这个操作,分别为app_rc.c和app_rc.h

然后,我们在头文件中创建一个进程函数和初始化函数。

#ifndef APP_RC_H
#define APP_RC_H
#include "stm32f10x.h"
void App_Rc_Init(void);
void App_Rc_Proc(void);
#endif

https://i-blog.csdnimg.cn/direct/39acf8bea3be428580bf79d05820e298.png

在初始化函数当中,我们需要对串口3进行初始化,串口3对应的引脚为PB10和PB11,所以,我们对这两个引脚进行初始化。

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	//初始化PB10 - AFPP - USART3_Tx
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	//初始化PB11 - IPU - USART3_Rx
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
	GPIO_Init(GPIOB,&GPIO_InitStruct);

接下来,我们对串口3进行配置

	//为USART3开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
	//配置串口3的参数
	USART_InitTypeDef USART_InitStruct = {0};
	USART_InitStruct.USART_BaudRate = 9600;
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART3,&USART_InitStruct);
	
	//开启串口3的RXNE中断
	USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);
	NVIC_InitTypeDef NVIC_InitStruct = {0};
	NVIC_InitStruct.NVIC_IRQChannel = USART3_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_InitStruct);
	//开启串口3的总中断
	USART_Cmd(USART3,ENABLE);

接下来,我们声明我们想要的三个数组,数组的大小应该大于指令的最长长度。

#define CMD_MAX_LEN 64
static char intBuf[CMD_MAX_LEN];//专门用于中断程序的缓存区
static char transBuf[CMD_MAX_LEN];//在中断程序和进程函数间转运数据的缓存区
static char procBuf[CMD_MAX_LEN];//专门用于进程函数的缓存区
static uint8_t lineReceivedFlag = 0;//一行字符串接收完成的标志位

接下来,我们对中断函数中进行处理,使其能够接收到数据。

https://i-blog.csdnimg.cn/direct/fbc3119b12f04cdea05ab1497659bcb1.png

static uint16_t inBufCursor = 0;//中断程序缓存区的游标

开始的时候,我们将其赋值为0,表示我们从数组的开头进行数据的接收。

//
// @简介:串口3的中断响应函数
//
void USART3_IRQHandler(void)
{
	if(USART_GetFlagStatus(USART3,USART_FLAG_RXNE)==SET)
	{
		uint8_t data = USART_ReceiveData(USART3);
		if(data=='\n')
		{
			intBuf[inBufCursor++] = data;
		}
		else
		{
			intBuf[inBufCursor] = '\0';
			inBufCursor = 0;
			strcpy(transBuf,intBuf);
			lineReceivedFlag = 1;
		}
	}
}

接下来,当lineReceivedFlag置1的时候,那么需要在进程函数当中对中转缓存区进行处理

//
// @简介:远程控制的进程函数
//
void App_Rc_Proc(void)
{
	if(lineReceivedFlag)
	{
		strcpy(procBuf,transBuf);
		lineReceivedFlag = 0;
		
	}
}

解析

接下来,我们就是在进程函数中对数组接收到的数据进行解析。

我们解析的步骤分为两步,分别是判断指令名称和解析指令参数。

//解析
		if(strncasecmp(procBuf,"move ",5)==0)
		{
			int turnSpeed,moveSpeed;
			if(scannf(procBuf,"move %d %d",&turnSpeed,&moveSpeed)==2)
			{
				//解析成功
				//执行
				
			}    
        }

这样子,我们就把对应的数值保存到了turnSpeed和moveSpeed当中。

执行

接下来,我们对我们解析到的数据进行执行。

https://i-blog.csdnimg.cn/direct/f000ff4b46c446d9a7a9819c5e58565b.png

我们可以通过改变平衡车的theta来改变平衡车的速度,θ大于0,平衡车会往前加速,θ小于0,平衡车会往后加速。

首先,我们创建两个函数来改变对应的速度环的设定值。

void App_Control_SetMoveSpeed(float MoveSpeed);
void App_Control_SetTurnSpeed(float TurnSpeed);

接下来,我们对这两个函数进行实现

//
// @简介:改变平衡车移动的速度
//
void App_Control_SetMoveSpeed(float MoveSpeed)
{
	PID_ChangeSP(&pid_velocity,MoveSpeed);
}
			if(scannf(procBuf,"move %d %d",&turnSpeed,&moveSpeed)==2)
			{
				//解析成功
				//执行
				App_Control_SetMoveSpeed(-moveSpeed * 0.01f * 0.7f);
				
			}

接下来是转弯部分。

https://i-blog.csdnimg.cn/direct/af1846002dc34ed1bf60742f87a11553.png

static PID_TypeDef pid_turn;
	PID_Init(&pid_turn,1.0f,0.0f,0.0f);
	PID_LimitConfig(&pid_turn,15.0f,-15.0f);
	// 转向环
	float gz = App_MPU6050_GetGz() * 0.017453f;
	float omega_diff = PID_Compute(&pid_turn,gz);
	
	// #8. 设置轮胎的转速
	App_Motor_SetOmega_L(omega_ref + omega_diff);
	App_Motor_SetOmega_R(omega_ref - omega_diff);
//
// @简介:改变平衡车转弯的速度
//
void App_Control_SetTurnSpeed(float TurnSpeed)
{
	PID_ChangeSP(&pid_turn,TurnSpeed);

}

OK,平衡车项目就全部结束了,大家可以听一下B站铁头山羊的课程,羊哥讲的特别好!!!

https://i-blog.csdnimg.cn/blog_migrate/11bb1b081a7b1f9ca3e7c66b7aa7764d.gif