• 方案介绍
  • 附件下载
  • 相关推荐
申请入驻 产业图谱

22.6-实现裸机的功能-(通过全局变量同步) FreeRTOS

02/15 08:17
1450
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

联系方式.txt

共1个文件

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


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

V3:HAL库开发、手把手教学下面功能:PID速度控制、PID循迹、PID跟随、遥控、避障、PID角度控制、openmv视觉控制、电磁循迹、FreeRTOS、K210视觉智能车(更新中)、K230视觉智能车(更新中)、MSPM0G3507视觉智能车(更新中)

在这里插入图片描述

22.6-实现裸机的功能-(通过全局变量同步)

开始调试实现之前裸机时候的六个模式,我们先使用全局变量同步和通信,再下面的章节讲解消息队列。

复制一份22-5_LED_FreeRTOS的代码,命名22-6_LED_FreeRTOS

为了方便理解我们需要一个执行模式0的任务,就是用来停止电机的任务,、
在这里插入图片描述
我们给之前的默认任务修改优先级和任务名字
在这里插入图片描述
然后设置新的任务名字和优先级为高,重新生成代码在这里插入图片描述
OLED刷新任务的电机停止函数注释掉
在这里插入图片描述
在停止任务中增加 电机停止和串口输出信息

在这里插入图片描述

/**
  * @brief  Function implementing the StopTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartStopTask */
void StartStopTask(void const * argument)
{
  /* USER CODE BEGIN StartStopTask */
  /* Infinite loop */
  for(;;)
  {
		UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);// 获取当前任务的栈高水位值
		printf("StartStopTask Mark: %u wordsn", (unsigned int)stackHighWaterMark);
		size_t freeHeapSize = xPortGetFreeHeapSize();// 获取系统的可用堆空间
		printf("StartStopTask Free Heap Size: %u bytesn", (unsigned int)freeHeapSize);
		
		if(g_ucMode == 0)
		{
			motorPidSetSpeed(0,0);			//设置小车速度为0
		}
		
    osDelay(10);
  }
  /* USER CODE END StartStopTask */
}

发现模式6的显示openmv发送过来数据的显示位置,被之前显示位置占用,所以我们换个,显示在更下面的一行,
在这里插入图片描述

			sprintf((char*)OledString, "lHW:%d  ", g_lHW_State);//视觉识别结果
			OLED_ShowString(0,7,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,

调试发现OLED刷新有时候比较乱,因为StartOledTask任务里面的OLED刷新合串口输出都比较费时间,为避免影响到其他任务执行,我们降低任务执行频率,设置延时时间osDelay(100)
在这里插入图片描述
并且把OLED刷新的操作的放到一个任务中执行,把openmv视觉识别结果的值放到OLED刷新任务中执行,把模式6里面的视觉识别结果OLED刷新的注释掉。
在这里插入图片描述
OLED刷新任务中加入 视觉结果刷新。

		sprintf((char*)OledString, "lHW:%d  ", g_lHW_State);//视觉识别结果
		OLED_ShowString(0,7,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,

在这里插入图片描述
因为我们已经通过串口输出测试了,每个任务的栈高水位值,还有获取了系统可用的栈空间都是可用正常执行的,所以为了提高系统执行效率就把四个任务里面的相关任务栈空间和系统堆空间注释掉。
在这里插入图片描述
这样修改后四个任务内容如下:

void StartStopTask(void const * argument)
{
  /* USER CODE BEGIN StartStopTask */
  /* Infinite loop */
  for(;;)
  {
//		UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);// 获取当前任务的栈高水位值
//		printf("StartLedTask Mark: %u wordsn", (unsigned int)stackHighWaterMark);
//		size_t freeHeapSize = xPortGetFreeHeapSize();// 获取系统的可用堆空间
//		printf("StartLedTask Free Heap Size: %u bytesn", (unsigned int)freeHeapSize);
		
		if(g_ucMode == 0)
		{
			motorPidSetSpeed(0,0);			//设置小车速度为0
		}
		
    osDelay(10);
  }
  /* USER CODE END StartStopTask */
}
void StartLedTask(void const * argument)
{
  /* USER CODE BEGIN StartLedTask */
  /* Infinite loop */
  for(;;)
  {
//		UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);// 获取当前任务的栈高水位值
//		printf("StartLedTask Mark: %u wordsn", (unsigned int)stackHighWaterMark);
//		size_t freeHeapSize = xPortGetFreeHeapSize();// 获取系统的可用堆空间
//		printf("StartLedTask Free Heap Size: %u bytesn", (unsigned int)freeHeapSize);
		
		HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);//切换LED GPIO状态
    osDelay(300);
  }
  /* USER CODE END StartLedTask */
}
void StartOledTask(void const * argument)
{
  /* USER CODE BEGIN StartOledTask */
  /* Infinite loop */
  for(;;)
  {
		
//		UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);// 获取当前任务的栈高水位值
//		printf("StartOledTask Mark: %u wordsn", (unsigned int)stackHighWaterMark);
//		size_t freeHeapSize = xPortGetFreeHeapSize();// 获取系统的可用堆空间
//		printf("StartOledTask Free Heap Size: %u bytesn", (unsigned int)freeHeapSize);
		
		sprintf((char *)OledString," g_ucMode:%d",g_ucMode);//显示g_ucMode 当前模式
		OLED_ShowString(0,6,OledString,12);	//显示在OLED上
		
		sprintf((char*)OledString, "lHW:%d  ", g_lHW_State);//视觉识别结果
		OLED_ShowString(0,7,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:计算字符串大小
		
			//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){}  //这个可以解决经常读不出数据的问题
		
		//在显示模式电机停转 设置小车速度为0
		//motorPidSetSpeed(0,0);
		
    osDelay(100);
  }
  /* USER CODE END StartOledTask */
}
void StartMultiModTask(void const * argument)
{
  /* USER CODE BEGIN StartMultiModTask */
  /* Infinite loop */
  for(;;)
  {
//		UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);// 获取当前任务的栈高水位值
//		printf("StartMultiModTask Mark: %u wordsn", (unsigned int)stackHighWaterMark);
//		size_t freeHeapSize = xPortGetFreeHeapSize();// 获取系统的可用堆空间
//		printf("StartMultiModTask Free Heap Size: %u bytesn", (unsigned int)freeHeapSize);
		
		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;//进行限幅 限幅速度在0-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);//前运动
				osDelay(100);
			}
			else{	//前方有障碍物
				motorPidSetSpeed(-1,1);//右边运动 原地	
				osDelay(500);
				if(HC_SR04_Read() > 25)//右边无障碍物
				{
					motorPidSetSpeed(1,1);//前运动
					osDelay(100);
				}
				else{//右边有障碍物
					motorPidSetSpeed(1,-1);//左边运动 原地
					osDelay(1000);
					if(HC_SR04_Read() >25)//左边无障碍物
					{
						 motorPidSetSpeed(1,1);//前运动
						osDelay(100);
					}
					else{
						motorPidSetSpeed(-1,-1);//后运动
						osDelay(1000);
						motorPidSetSpeed(-1,1);//右边运动
						osDelay(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 没有东西就停止
			osDelay(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);//将最后计算的目标速度 通过motorPidSetSpeed控制电机
		
		}
		if(g_ucMode == 6)
		{

//			sprintf((char*)OledString, "lHW:%d  ", g_lHW_State);//视觉识别结果
//			OLED_ShowString(0,7,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
			
			g_fHW_PID_Out = PID_realize(&pidOpenmv_Tracking,g_cThisState);//PID计算输出目标速度 这个速度,会和基础速度加减

			g_fHW_PID_Out1 = 0.5 + g_fHW_PID_Out;//电机1速度=基础速度+循迹PID输出速度
			g_fHW_PID_Out2 = 0.5 - g_fHW_PID_Out;//电机1速度=基础速度-循迹PID输出速度
			if(g_fHW_PID_Out1 >1.2) g_fHW_PID_Out1 =1.2;//进行限幅 限幅速度在0-1.2之间
			if(g_fHW_PID_Out1 <0) g_fHW_PID_Out1 =0;
			if(g_fHW_PID_Out2 >1.2) g_fHW_PID_Out2 =1.2;//进行限幅 限幅速度在0-1.2之间
			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;//保存上次红外对管状态	

		}
    osDelay(1);
  }
  /* USER CODE END StartMultiModTask */
}

22.6.1-实际测试几个功能是否正常

先烧录一下 "22-6_LED_FreeRTOS"的代码

然后进行测试,测试结果如下

模式0-可以正常刷新屏幕(除了6050数据外)

模式1-可以完成红外对管PID循迹

模式2-可以进行蓝牙遥控

模式3-可以进行超声波避障 偶尔出现超声波测距值错误

模式4-可以进行PID跟随 但是有些抖动

模式5-不能进行MPU6050角度控制

模式6-可以进行openmv视觉循迹

  • 联系方式.txt
    下载
意法半导体

意法半导体

意法半导体(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+方案商即时响应!