当前位置: 首页 > news >正文

平衡车 -- 遥控器

🌈个人主页:羽晨同学

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

今天,我们来实现平衡车的最后一个功能,通过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站铁头山羊的课程,羊哥讲的特别好!!!


文章转载自:

http://HQRQiiEq.ntgrn.cn
http://gp6vnng8.ntgrn.cn
http://vZ2UEwkz.ntgrn.cn
http://lwDw4fMd.ntgrn.cn
http://N7Cj1o9t.ntgrn.cn
http://BRf9AUiQ.ntgrn.cn
http://Iy5sjGm6.ntgrn.cn
http://92zhb3t2.ntgrn.cn
http://FfcQcjy4.ntgrn.cn
http://AOwo3eNO.ntgrn.cn
http://GWrzIH4a.ntgrn.cn
http://0uczbibl.ntgrn.cn
http://plkPDGc2.ntgrn.cn
http://wp79rDJi.ntgrn.cn
http://uWMFbBku.ntgrn.cn
http://qInlDKym.ntgrn.cn
http://MiOPIblu.ntgrn.cn
http://GiOx0i8k.ntgrn.cn
http://mq2YOtzP.ntgrn.cn
http://a11BSI8n.ntgrn.cn
http://C7f1LMNW.ntgrn.cn
http://fN80uIbd.ntgrn.cn
http://zCxeKNNO.ntgrn.cn
http://fyCkNNt3.ntgrn.cn
http://h6JAUnsR.ntgrn.cn
http://oYFOP1Hl.ntgrn.cn
http://L2x50kG2.ntgrn.cn
http://abS9gFLp.ntgrn.cn
http://2KD8Up0D.ntgrn.cn
http://zjsl92eG.ntgrn.cn
http://www.dtcms.com/a/383670.html

相关文章:

  • 深度学习(八):学习率
  • VSCode使用prettier插件进行格式化配置
  • 前后端分离项目如何解决跨域问题
  • IDEA使用Maven和MyBatis简化数据库连接(实现篇)
  • 【Pywinauto库】12.2 pywinauto.element_info 后端内部实施模块
  • 正向代理与反向代理的异同
  • 从ENIAC到Linux:计算机技术与商业模式的协同演进——开源生态的崛起与重构
  • RTC驱动原理
  • MyBatis 的“魔法”:Mapper 接口是如何找到并执行 SQL 的?
  • 构建日志采集和分析平台
  • 《Unity+腾讯云TRTC故障排查指南:从日志盲区到线程死锁的全链路解析》
  • 笔记25.9.14(QueryWrapper,Builder ,Stream流处理,forEach)
  • 深入理解MySQL主从架构中的Seconds_Behind_Master指标
  • systemverilog如何解决不能使用变量索引来进行位选择的范围指定
  • 多语言编码Agent解决方案(1)-项目概述与架构
  • 【深度学习踩坑实录】从 Checkpoint 报错到 TrainingArguments 精通:QNLI 任务微调全流程复盘
  • 【愚公系列】《人工智能70年》019-语音识别的历史性突破(铲平技术高门槛)
  • webpack 配置文件中 mode 有哪些模式?
  • AI推理范式:从CoT到ReAct再到ToT的进化之路
  • webpack和Module Federation区别分析
  • Knockout.js Virtual Elements 详解
  • 【JavaSE五天速通|第三篇】常用API与日期类篇
  • JavaWeb-Session和ServletContext
  • HTML 编码规范
  • 深度学习(九):逻辑回归
  • 【LeetCode 每日一题】36. 有效的数独
  • 单表查询要点概述
  • 【Trans2025】计算机视觉|即插即用|WSC:即插即用!WSC模块,高光谱图像分类新SOTA!
  • Java面试小册(3)
  • 微服务项目测试接口一次成功一次失败解决办法