• 方案介绍
    • 第18章-综合以上功能
  • 附件下载
  • 推荐器件
  • 相关推荐
申请入驻 产业图谱

第18章-综合以上功能 基于stm32的智能小车(远程控制、避障、循迹) 超详细

2024/06/24
2737
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

联系方式.txt

共1个文件

这个是全网最详细的STM32项目教学视频。
第一篇在这里:
视频在这里


STM32智能小车V3-STM32入门教程-openmv与STM32循迹小车-stm32f103c8t6-电赛 嵌入式学习 PID控制算法 编码器电机 跟随

第18章-综合以上功能

18-按键和app按钮切换功能

根据上面介绍,我们的模式可以有:

**OLED显示模式: 速度、里程、电压、超声波数据、MPU6050俯仰角、横滚角、航向角 数据显示在OLED上和通过串口发送蓝牙APP **

PID循迹模式:红外对管PID循迹

手机遥控普通运动模式:遥控前、后、左、右加速运动

超声波避障模式

PID跟随模式:超声波PID定距离跟随

手机遥控角度闭环模式:MPU6050角度PID控制

可以设置标志位通过按键改变标志位,以实现功能切换。

定义一个全局变量,

uint8_t g_ucMode = 0; 
//小车运动模式标志位 0:显示功能、1:PID循迹模式、2:手机遥控普通运动模式、3.超声波避障模式、4:PID跟随模式、5:遥控角度闭环

在这里插入图片描述

uint8_t g_ucMode = 0; //小车运动模式标志位

在gpio.h声明一下

在这里插入图片描述

extern uint8_t g_ucMode ; //小车运动模式标志位

按键中断回调函数里面补充按下按键后的处理

先不进行消抖,如果后面KEY1 KEY2效果不好再消抖
在这里插入图片描述

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == KEY1_Pin) //判断一下那个引脚触发中断
	{
		//这里编写触发中断后要执行的程序
		if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5 
		else
		{
			g_ucMode+=1;
		}
		HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
	}
	if(GPIO_Pin == KEY2_Pin) //判断一下那个引脚触发中断
	{
		//这里编写触发中断后要执行的程序
		g_ucMode=0;
		HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
	}
}

然后主函数显示当前处于的模式

然后判断当前模式 执行不同代码

方法:一个功能一个功能的添加代码,添加好一个调试测试一下,然后再添加下一个

下面这个就是我们主函数的代码。

	sprintf((char *)OledString," g_ucMode:%d",g_ucMode);//显示g_ucMode 当前模式
	OLED_ShowString(0,6,OledString,12);	//显示在OLED上
	
	sprintf((char *)Usart3String," g_ucMode:%d",g_ucMode);//蓝牙APP显示
	HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
	
	if(g_ucMode == 0)
	{
	//0LED显示功能
		sprintf((char*)OledString, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//显示速度
		OLED_ShowString(0,0,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
		
		sprintf((char*)OledString, "Mileage:%.2f", Mileage);//显示里程
		OLED_ShowString(0,1,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
		
		sprintf((char*)OledString, "U:%.2fV", adcGetBatteryVoltage());//显示电池电压
		OLED_ShowString(0,2,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
		
		sprintf((char *)OledString,"HC_SR04:%.2fcmrn",HC_SR04_Read());//显示超声波数据
		OLED_ShowString(0,3,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
		
		sprintf((char *)OledString,"p:%.2f r:%.2f rn",pitch,roll);//显示6050数据 俯仰角 横滚角
		OLED_ShowString(0,4,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
		
		sprintf((char *)OledString,"y:%.2f  rn",yaw);//显示6050数据  航向角
		OLED_ShowString(0,5,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
		
	//蓝牙APP显示
		sprintf((char*)Usart3String, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//显示速度
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
		//阻塞方式发送可以保证数据发送完毕,中断发送不一定可以保证数据已经发送完毕才启动下一次发送
		sprintf((char*)Usart3String, "Mileage:%.2f", Mileage);//显示里程
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
		
		sprintf((char*)Usart3String, "U:%.2fV", adcGetBatteryVoltage());//显示电池电压
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
		
		sprintf((char *)Usart3String,"HC_SR04:%.2fcmrn",HC_SR04_Read());//显示超声波数据
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
		
		sprintf((char *)Usart3String,"p:%.2f r:%.2f rn",pitch,roll);//显示6050数据 俯仰角 横滚角
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
		
		sprintf((char *)Usart3String,"y:%.2f  rn",yaw);//显示6050数据  航向角
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
	
		//获得6050数据
		while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){}  //这个可以解决经常读不出数据的问题
		
		//显示模式电机停转
		motorPidSetSpeed(0,0);
	}
	if(g_ucMode == 1)
	{
	///****    红外PID循迹功能******************/
	g_ucaHW_Read[0] = READ_HW_OUT_1;//读取红外对管状态、这样相比于写在if里面更高效
	g_ucaHW_Read[1] = READ_HW_OUT_2;
	g_ucaHW_Read[2] = READ_HW_OUT_3;
	g_ucaHW_Read[3] = READ_HW_OUT_4;

	if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )
	{
//		printf("应该前进rn");//注释掉更加高效,减少无必要程序执行
		g_cThisState = 0;//前进
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )//使用else if更加合理高效
	{
//		printf("应该右转rn");
		g_cThisState = -1;//应该右转
	}
	else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )
	{
//		printf("快速右转rn");
		g_cThisState = -2;//快速右转
	}
	else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0)
	{
//		printf("快速右转rn");
		g_cThisState = -3;//快速右转
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 0 )
	{
//		printf("应该左转rn");
		g_cThisState = 1;//应该左转	
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 1 )
	{
//		printf("快速左转rn");
		g_cThisState = 2;//快速左转
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 1)
	{
//	    printf("快速左转rn");
		g_cThisState = 3;//快速左转
	}
	g_fHW_PID_Out = PID_realize(&pidHW_Tracking,g_cThisState);//PID计算输出目标速度 这个速度,会和基础速度加减

	g_fHW_PID_Out1 = 3 + g_fHW_PID_Out;//电机1速度=基础速度+循迹PID输出速度
	g_fHW_PID_Out2 = 3 - g_fHW_PID_Out;//电机1速度=基础速度-循迹PID输出速度
	if(g_fHW_PID_Out1 >5) g_fHW_PID_Out1 =5;//进行限幅 限幅速度在0-5之间
	if(g_fHW_PID_Out1 <0) g_fHW_PID_Out1 =0;
	if(g_fHW_PID_Out2 >5) g_fHW_PID_Out2 =5;
	if(g_fHW_PID_Out2 <0) g_fHW_PID_Out2 =0;
	if(g_cThisState != g_cLastState)//如何这次状态不等于上次状态、就进行改变目标速度和控制电机、在定时器中依旧定时控制电机
	{
		motorPidSetSpeed(g_fHW_PID_Out1,g_fHW_PID_Out2);//通过计算的速度控制电机
	}
	
	g_cLastState = g_cThisState;//保存上次红外对管状态	

	}
	if(g_ucMode == 2)
	{
		//***************遥控模式***********************//
		//遥控模式的控制在串口三的中断里面
	}
	if(g_ucMode == 3)
	{
		//******超声波避障模式*********************//
避障逻辑
		if(HC_SR04_Read() > 25)//前方无障碍物
		{
			motorPidSetSpeed(1,1);//前运动
			HAL_Delay(100);
		}
		else{	//前方有障碍物
			motorPidSetSpeed(-1,1);//右边运动 原地	
			HAL_Delay(500);
			if(HC_SR04_Read() > 25)//右边无障碍物
			{
				motorPidSetSpeed(1,1);//前运动
				HAL_Delay(100);
			}
			else{//右边有障碍物
				motorPidSetSpeed(1,-1);//左边运动 原地
				HAL_Delay(1000);
				if(HC_SR04_Read() >25)//左边无障碍物
				{
					 motorPidSetSpeed(1,1);//前运动
					HAL_Delay(100);
				}
				else{
					motorPidSetSpeed(-1,-1);//后运动
					HAL_Delay(1000);
					motorPidSetSpeed(-1,1);//右边运动
					HAL_Delay(50);
				}
			}
		}
	}
	if(g_ucMode == 4)
	{
	//**********PID跟随功能***********//
		g_fHC_SR04_Read=HC_SR04_Read();//读取前方障碍物距离
		if(g_fHC_SR04_Read < 60){  //如果前60cm 有东西就启动跟随
			g_fFollow_PID_Out = PID_realize(&pidFollow,g_fHC_SR04_Read);//PID计算输出目标速度 这个速度,会和基础速度加减
			if(g_fFollow_PID_Out > 6) g_fFollow_PID_Out = 6;//对输出速度限幅
			if(g_fFollow_PID_Out < -6) g_fFollow_PID_Out = -6;
			motorPidSetSpeed(g_fFollow_PID_Out,g_fFollow_PID_Out);//速度作用与电机上
		}
		else motorPidSetSpeed(0,0);//如果前面60cm 没有东西就停止
		HAL_Delay(10);//读取超声波传感器不能过快
	}
	if(g_ucMode == 5)
	{
	//*************MPU6050航向角 PID转向控制*****************//

		sprintf((char *)Usart3String,"pitch:%.2f roll:%.2f yaw:%.2frn",pitch,roll,yaw);//显示6050数据 俯仰角 横滚角 航向角
		HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const  char  *)Usart3String),0xFFFF);//通过串口三输出字符 strlen:计算字符串大小	
	   
	   //mpu_dmp_get_data(&pitch,&roll,&yaw);//返回值:0,DMP成功解出欧拉角
		while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){}  //这个可以解决经常读不出数据的问题
		
		
		g_fMPU6050YawMovePidOut = PID_realize(&pidMPU6050YawMovement,yaw);//PID计算输出目标速度 这个速度,会和基础速度加减

		g_fMPU6050YawMovePidOut1 = 1.5 + g_fMPU6050YawMovePidOut;//基础速度加减PID输出速度
		g_fMPU6050YawMovePidOut2 = 1.5 - g_fMPU6050YawMovePidOut;
		if(g_fMPU6050YawMovePidOut1 >3.5) g_fMPU6050YawMovePidOut1 =3.5;//进行限幅
		if(g_fMPU6050YawMovePidOut1 <0) g_fMPU6050YawMovePidOut1 =0;
		if(g_fMPU6050YawMovePidOut2 >3.5) g_fMPU6050YawMovePidOut2 =3.5;
		if(g_fMPU6050YawMovePidOut2 <0) g_fMPU6050YawMovePidOut2 =0;
		motorPidSetSpeed(g_fMPU6050YawMovePidOut1,g_fMPU6050YawMovePidOut2);
	
	}

可以测试上面的代码 然后没有问题后,我们添加一个通过蓝牙APP按钮切换模式代码
在这里插入图片描述

		if(g_ucUsart3ReceiveData == 'J') //改变模式
		{
			if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5 
			else
			{
				g_ucMode+=1;
			}
		}
		if(g_ucUsart3ReceiveData == 'K') g_ucMode=0;//设置为显示模式

然后对应APP也要添加 按钮设置
在这里插入图片描述
我们
按键没有消抖效果不好,我们消抖一下
在这里插入图片描述
我们增加了 HAL延时和再次判断电平

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == KEY1_Pin) //判断一下那个引脚触发中断
	{
		HAL_Delay(10);//延时消抖 主要
		if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_SET)//判断KEY1引脚仍为高电平
		{
			//这里编写触发中断后要执行的程序
			if(g_ucMode == 5) g_ucMode = 1;//g_ucMode模式是0 1 2 3 4 5 
			else
			{
				g_ucMode+=1;
			}
			HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
		}
	}
	if(GPIO_Pin == KEY2_Pin) //判断一下那个引脚触发中断
	{
		HAL_Delay(10);//延时消抖
		if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_RESET)//判断KEY2引脚仍为低电平
		{
			//这里编写触发中断后要执行的程序
			g_ucMode=0;
			HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
		}
	}
}

但是测试不能执行中断,程序异常卡死了

原因是HAL_Delay使用的是sysTick 中断优先级在软件初始化是默认最低的,比外部中断优先级低,所以HAL_Delay不能在外部中断服务函数中调用。

所以我们可以通过提高sysTick 中断的优先级,提高的比HAL_Delay高。
在这里插入图片描述
然后我们提高至 如下图
在这里插入图片描述
然后编译烧录测试按键是否更加稳定。
下面的章节我们讲解视觉,RTOS系统,电磁循迹等功能

联系:Q,1930299709

  • 联系方式.txt
    下载

推荐器件

更多器件
器件型号 数量 器件厂商 器件描述 数据手册 ECAD模型 风险等级 参考价格 更多信息
74LVTH125MTCX 1 Fairchild Semiconductor Corporation Bus Driver, LVT Series, 4-Func, 1-Bit, True Output, BICMOS, PDSO14, 4.40 MM, LEAD FREE, MO-153AB, TSSOP-14
$0.56 查看
ATS08ASM-1 1 CTS Corporation Parallel - Fundamental Quartz Crystal, 8MHz Nom, GREEN, RESISTANCE WELD, METAL PACKAGE-2

ECAD模型

下载ECAD模型
$0.59 查看
TLP187(TPL,E(T 1 Toshiba America Electronic Components 1 CHANNEL DARLINGTON OUTPUT OPTOCOUPLER
$1.25 查看
意法半导体

意法半导体

意法半导体(ST)集团于1987年6月成立,是由意大利的SGS微电子公司和法国Thomson半导体公司合并而成。1998年5月,SGS-THOMSON Microelectronics将公司名称改为意法半导体有限公司。意法半导体是世界最大的半导体公司之一,公司销售收入在半导体工业五大高速增长市场之间分布均衡(五大市场占2007年销售收入的百分比):通信(35%),消费(17%),计算机(16%),汽车(16%),工业(16%)。 据最新的工业统计数据,意法半导体是全球第五大半导体厂商,在很多市场居世界领先水平。例如,意法半导体是世界第一大专用模拟芯片和电源转换芯片制造商,世界第一大工业半导体和机顶盒芯片供应商,而且在分立器件、手机相机模块和车用集成电路领域居世界前列.

意法半导体(ST)集团于1987年6月成立,是由意大利的SGS微电子公司和法国Thomson半导体公司合并而成。1998年5月,SGS-THOMSON Microelectronics将公司名称改为意法半导体有限公司。意法半导体是世界最大的半导体公司之一,公司销售收入在半导体工业五大高速增长市场之间分布均衡(五大市场占2007年销售收入的百分比):通信(35%),消费(17%),计算机(16%),汽车(16%),工业(16%)。 据最新的工业统计数据,意法半导体是全球第五大半导体厂商,在很多市场居世界领先水平。例如,意法半导体是世界第一大专用模拟芯片和电源转换芯片制造商,世界第一大工业半导体和机顶盒芯片供应商,而且在分立器件、手机相机模块和车用集成电路领域居世界前列.收起

查看更多

相关推荐

方案定制

去合作
方案开发定制化,2000+方案商即时响应!