平衡车学习记录(8) 最后的部分学习的是让小车通过手机端的app连接蓝牙控制前进和转向。第一部分 接受一行字符串这是手机端的app界面这个app由up主开发具体我没研究过。有控制前进和后退左右的摇杆。如图是具体的实现方式通过串口3每次接收一行字符串通过触发中断实现接收同时还有三个数组用于存储数据。接收一个字符触发一次中断放到中断程序的数组里。在接收结束的地方有\n在中断数组里存储\0将中断数组里的数据转存到暂存数组里面并且将lineReceiveFlag标志位置1。进程函数里不断查询标志位当标志位指示数组里有数据了就把数据转存到进程函数的数组里。写一个初始化函数和进程函数#ifndef APP_RC_H #define APP_RC_H #includestm32f10x.h void App_rc_Init(void); void App_rc_Proc(void); #endif我们要对蓝牙芯片进行操作初始化USART3并且打开中断连接到单片机的PB10和PB11引脚// //简介对遥控器模块进行初始化 // void App_rc_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启GPIOB的时钟 GPIO_InitTypeDef GPIO_InitStruct{0}; //初始化PB10 - AFPP - USART3 - Tx GPIO_InitStruct.GPIO_PinGPIO_Pin_10; GPIO_InitStruct.GPIO_ModeGPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_SpeedGPIO_Speed_2MHz; GPIO_Init(GPIOB,GPIO_InitStruct); //初始化PB11 - IPU - USART3 - Rx GPIO_InitStruct.GPIO_PinGPIO_Pin_11; GPIO_InitStruct.GPIO_ModeGPIO_Mode_IPU; GPIO_Init(GPIOB,GPIO_InitStruct); //为串口3打开时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //配置串口3的参数 USART_InitTypeDef USART_InitStruct{0}; USART_InitStruct.USART_BaudRate9600; USART_InitStruct.USART_ModeUSART_Mode_Rx; USART_InitStruct.USART_ParityUSART_Parity_No; USART_InitStruct.USART_StopBitsUSART_StopBits_1; USART_InitStruct.USART_WordLengthUSART_WordLength_8b; USART_Init(USART3,USART_InitStruct); //开启USART3的RXNE中断 USART_ITConfig(USART3,USART_IT_RXNE,ENABLE); NVIC_InitTypeDef NVIC_InitStruct{0}; NVIC_InitStruct.NVIC_IRQChannel USART3_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmdENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority0; NVIC_InitStruct.NVIC_IRQChannelSubPriority0; NVIC_Init(NVIC_InitStruct); //开启USART3的总开关 USART_Cmd(USART3,ENABLE); }接着声明3个数组#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 lineReceiveFlag0;//一行字符串接收完成的标志位 static uint16_t intBufCursor0;//intBuf的游标指向下一个空白位置第一步当接受到的字符不是\n时接收一个字符用游标来移动存储的位置。接着编写中断相应函数的内容和进程函数的内容// //简介遥控器的进程函数 // void App_rc_Proc(void) { if(lineReceiveFlag1) { strcpy(procBuf,transBuf); lineReceiveFlag0; } } // //简介USART3的中断相应函数 // void USART3_IRQHandler(void) { if(USART_GetFlagStatus(USART3,USART_FLAG_RXNE)SET) { uint8_t data USART_ReceiveData(USART3); if(data !\n) { intBuf[intBufCursor]data; } else { intBuf[intBufCursor] \0; intBufCursor0; strcpy(transBuf,intBuf); lineReceiveFlag1; } } }第二部分 指令解析strnacase指令比较n个字符是否相等并且忽略大小写区别和strcmp有区别先判断开头是否是move空格再对后面的指令进行解析对cmdline里面的数据进行解析move空格是固定格式后面的%d %d是需要解析的内容把解析到的内容放到turnSpeed和moveSpeed里面解析成功一个就返回1解析成功两个就返回2。这个函数包含在stdio.h的头文件里面使用前需要先引用头文件。#include stdio.h void App_rc_Proc(void) { if(lineReceiveFlag1) { strcpy(procBuf,transBuf); lineReceiveFlag0; } //解析 if(strncasecmp(procBuf,move ,5)0) { int moveSpeed,turnSpeed; if(sscanf(procBuf,move %d %d,turnSpeed,moveSpeed)2)//第二个d后面不要写\n,因为已经被替换了 { //解析成功 //执行 } } }第三部分 速度和转向控制控制平衡车的车速需要控制平衡车的速度环。编写两个函数一个控制移动速度一个控制转向速度。计算最大的移动速度先转换成m/s然后乘上一周的周长最后再给平衡车调节留一些余量小于0.7m/s。需要把手机APP摇杆上的范围映射到-0.7和0.7上// //简介改变平衡车移动的速度 //参数MoveSpeed - 速度单位m/s // void App_control_SetMoveSpeed(float MoveSpeed) { PID_ChangeSP(pid_velocity,MoveSpeed); }在main.c里面调用初始化函数和进程函数注意需要把app_control.c里对速度环强制设定成0的代码删掉// //#-1.改变速度环的设定值 // PID_ChangeSP(pid_velocity,0.0f);接着设定转向的速度设置以左转为例只需要让小车左轮正转右轮反转即可控制转向的速度实际上是控制gz通过pid控制器调节加在左右电机上的速度就可以改变转向的速度了。KP1是up根据经验试出来的。还需要设置转向环的范围如下图结果大概是60rad/s只取四分之一15rad/s。°/s转换成rad/s,用前面的乘(π/180),约等于0,.017453。PID_Init(pid_turn,1.0f,0.0f,0.0f);//初始化转向环的PID PID_LimitConfig(pid_turn,15.0f,-15.0f);//将转向环的PID控制器输出限制在 - 15.0rad/s之间 //转向环 float gzApp_MPU6050_GetGz()*0.017453f;//°/s-rad/s float Omega_diffPID_Compute(pid_turn,gz); //#8.设置轮胎的转速 App_motor_SetOmega_L(Omega_refOmega_diff); App_motor_SetOmega_R(Omega_ref-Omega_diff);计算好PID的输出后加载左右电机上左电机加右电机减// //简介改变平衡车转向的速度 //参数TurnSpeed - 转向速度单位rad/s,最大转速15rad/s // void App_control_SetTurnSpeed(float TurnSpeed) { PID_ChangeSP(pid_turn,TurnSpeed); } if(strncasecmp(procBuf,move ,5)0) { int moveSpeed,turnSpeed; if(sscanf(procBuf,move %d %d,turnSpeed,moveSpeed)2)//第二个d后面不要写\n,因为已经被替换了 { //解析成功 //执行 App_control_SetMoveSpeed(-moveSpeed*0.01f*0.7f);//乘0.01是让-100映射到范围上 App_control_SetTurnSpeed(-turnSpeed*0.01f*15.0f);//用负的turnSpeed带入是因为-100~100。 } }至此小车项目顺利结束。