平衡车 -- 遥控器
🌈个人主页:羽晨同学
💫个人格言:“成为自己未来的主人~”
今天,我们来实现平衡车的最后一个功能,通过APP的蓝牙来遥控平衡小车
我们通过蓝牙芯片可以获取手机上的操作,然后给到单片机的串口3.以便于后面的单片机进行处理。
接收
我们讲一下如何通过串口3来接收APP的数据。
可以看到,我们指令格式后面有一个换行符,所以我们要每次接收到一行数据。
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
在初始化函数当中,我们需要对串口3进行初始化,串口3对应的引脚为PB10和PB11,所以,我们对这两个引脚进行初始化。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};//初始化PB10 - AFPP - USART3_TxGPIO_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_RxGPIO_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;//一行字符串接收完成的标志位
接下来,我们对中断函数中进行处理,使其能够接收到数据。
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当中。
执行
接下来,我们对我们解析到的数据进行执行。
我们可以通过改变平衡车的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);}
接下来是转弯部分。
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站铁头山羊的课程,羊哥讲的特别好!!!