七,FreeRTOS之——队列同步与互斥

Posted 菜鸟江多多

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了七,FreeRTOS之——队列同步与互斥相关的知识,希望对你有一定的参考价值。

声明:本专栏参考韦东山,野火,正点原子以及其他博主的FreeRTOS教程,如若侵权请告知,马上删帖致歉,个人总结,如有不对,欢迎指正。

转:同步与互斥概念理解link






实验一:同步

还是来看看同步实验怎么做吧

实验代码

/**
   FreeRTOS v9.0.0 + STM32 动态创建任务
   实验平台:韦东山 STM32F103ZE开发板 
**/ 
 
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"

/* 任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
static TaskHandle_t Task1_Handle = NULL;
static TaskHandle_t Task2_Handle = NULL;
static TaskHandle_t Task3_Handle = NULL;

static xQueueHandle MsgQueue;  /*队列句柄*/


/* 函数声明 */
static void AppTaskCreate(void);/* 用于创建任务 */
static void Flag1_Task(void* pvParameters);/* LED1_Task任务实现 */
static void Flag2_Task(void* pvParameters);/* LED2_Task任务实现 */
static void Flag3_Task(void* pvParameters);/* LED3_Task任务实现 */
static void BSP_Init(void);/* 用于初始化板载相关资源 */

/*****************************************************************
  * @brief  主函数
  * @param  无
  * @retval 无
  * @note   第一步:开发板硬件初始化 
            第二步:创建APP应用任务
            第三步:启动FreeRTOS,开始多任务调度
  ****************************************************************/
int main(void)
{	
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */

  /* 开发板硬件初始化 */
  BSP_Init();
  printf("这是一个STM32F103ZE开发板-FreeRTOS-动态创建任务!\\r\\n");
	
	MsgQueue=xQueueCreate(1,sizeof(int));/*创建队列:参数,队列长度,每个长度大小(字节)*/
	
	
	
	
   /* 创建AppTaskCreate任务 */
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数 */
                        (const char*    )"AppTaskCreate",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )1, /* 任务的优先级 */
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针 */ 
  /* 启动任务调度 */           
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */
  else
    return -1;  
  
  while(1);   /* 正常不会执行到这里 */    
}


/***********************************************************************
  * @ 函数名  : AppTaskCreate
  * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  * @ 参数    : 无  
  * @ 返回值  : 无
  **********************************************************************/
static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  taskENTER_CRITICAL();           //进入临界区
  
  /* 创建Flag1_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Flag1_Task, /* 任务入口函数 */
                        (const char*    )"Flag1_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )2,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Task1_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建Flag1_Task任务成功!\\r\\n");
	
	 /* 创建Flag2_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Flag2_Task, /* 任务入口函数 */
                        (const char*    )"Flag2_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )2,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Task2_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建Flag2_Task任务成功!\\r\\n");
	
	
	 /* 创建Flag3_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Flag3_Task, /* 任务入口函数 */
                        (const char*    )"Flag3_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )1,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Task3_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建Flag3_Task任务成功!\\r\\n");
  
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}



/**********************************************************************
  * @ 函数名  : LED_Task
  * @ 功能说明: LED_Task任务主体
  ********************************************************************/
uint8_t flag1;
uint8_t flag2;
uint8_t flag3;

/*优先级2*/
static void Flag1_Task(void* parameter)
{
	static int value,i;
	 while (1)
    {
			
			  for(i=10000;i>1;i--)
			  flag1=1;
        flag2=0;
			  flag3=0;
			  /* 向队列中填充内容 */  
			
			  value++;
			  printf("Task1 Running! value=%d\\r\\n",value);
			  xQueueSend( MsgQueue, ( void* )&value, queueSEND_TO_BACK);  /*发送队列消息,*/
			
			 // vTaskDelay(10);   /* 延时1个tick */
    }
}


/*优先级2*/
static void Flag2_Task(void* parameter)
{
	static int value1;
  	while (1)
    {
		  /*如果读取到队列消息*/
			if(xQueueReceive(MsgQueue,(void *)&value1,pdFALSE)==pdPASS)
			{
			    flag1=0;
          flag2=1;
			    flag3=0;
				  printf("Task2 Running! value=%d\\r\\n",value1);/*将读取到的数值打印出来*/
			}
			
      //vTaskDelay(10);   /* 延时1个tick */		 		
     
    }
}

/*优先级2*/
static void Flag3_Task(void* parameter)
{
	static int value2;
  	while (1)
    {
		  flag1=0;
      flag2=0;
			flag3=1;
		  printf("Task3 Running! value=%d\\r\\n",value2);
		  vTaskDelay(10);   /* 延时1个tick */		 		
     
    }
}

/***********************************************************************
  * @ 函数名  : BSP_Init
  * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  * @ 参数    :   
  * @ 返回值  : 无
  *********************************************************************/
static void BSP_Init(void)
{
	/*
	 * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
	 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
	 * 都统一用这个优先级分组,千万不要再分组,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* LED 初始化 */
	LED_GPIO_Config();

	/* 串口初始化	*/
	USART_Config();
  
}

/********************************END OF FILE****************************/

划重点

创建三个任务,优先级为321,

创建队列任务句柄,在这之前得要加入队列头文件

创建队列函数

xQueueCreate();

各任务处理事件

flag1Task

flag2Task

flag3Task

根据以上信息可以猜想任务运行过程是怎样的???

任务一和任务二在执行过程中都没有进入到阻塞状态,任务3的优先级是要比任务一和任务二要低的,所以任务三肯定不会运行,那么任务一和任务二相同优先级要如何运行呢??

直接仿真看看

神奇的是串口居然不乱了,任务一先运行,接着任务二打印了信息,这就是通过队列传递信息来实现任务同步

再来做个实验看看任务互斥

实验二:互斥

实验代码

/**
   FreeRTOS v9.0.0 + STM32 动态创建任务
   实验平台:韦东山 STM32F103ZE开发板 
**/ 
 
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"

/* 任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
static TaskHandle_t Task1_Handle = NULL;
static TaskHandle_t Task2_Handle = NULL;
//static TaskHandle_t Task3_Handle = NULL;

//static QueueHandle_t xQueueCalcHandle;
static QueueHandle_t xQueueUARTcHandle;

/* 函数声明 */
static void AppTaskCreate(void);/* 用于创建任务 */
static void Flag1_Task(void* pvParameters);/* LED1_Task任务实现 */
static void Flag2_Task(void* pvParameters);/* LED2_Task任务实现 */
//static void Flag3_Task(void* pvParameters);/* LED3_Task任务实现 */
static void BSP_Init(void);/* 用于初始化板载相关资源 */





/*创建队列*/
int InitUARTLock(void)
{	
	int val;
	xQueueUARTcHandle = xQueueCreate(1, sizeof(int));/*创建队列*/
	if (xQueueUARTcHandle == NULL)
	{
		printf("can not create queue\\r\\n");
		return -1;/*创建失败*/
	}
	xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);/*死等*/
	return 0;/*创建成功*/
}


/*接收队列消息*/
void GetUARTLock(void)
{	
	int val;
	xQueueReceive(xQueueUARTcHandle, &val, portMAX_DELAY);
}

/*发送队列消息*/
void PutUARTLock(void)
{	
	int val;
	xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);
}





/*****************************************************************
  * @brief  主函数
  * @param  无
  * @retval 无
  * @note   第一步:开发板硬件初始化 
            第二步:创建APP应用任务
            第三步:启动FreeRTOS,开始多任务调度
  ****************************************************************/
int main(void)
{	
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */

  /* 开发板硬件初始化 */
  BSP_Init();
  printf("这是一个STM32F103ZE开发板-FreeRTOS-动态创建任务!\\r\\n");
	
	InitUARTLock();/*调用创建队列函数*/
	
	
	
	
	
   /* 创建AppTaskCreate任务 */
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数 */
                        (const char*    )"AppTaskCreate",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )1, /* 任务的优先级 */
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针 */ 
  /* 启动任务调度 */           
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */
  else
    return -1;  
  
  while(1);   /* 正常不会执行到这里 */    
}


/***********************************************************************
  * @ 函数名  : AppTaskCreate
  * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  * @ 参数    : 无  
  * @ 返回值  : 无
  **********************************************************************/
static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  taskENTER_CRITICAL();           //进入临界区
  
  /* 创建Flag1_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Flag1_Task, /* 任务入口函数 */
                        (const char*    )"Flag1_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )2,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Task1_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建Flag1_Task任务成功!\\r\\n");
	
	 /* 创建Flag2_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Flag2_Task, /* 任务入口函数 */
                        (const char*    )"Flag2_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )2,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Task2_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建Flag2_Task任务成功!\\r\\n");
	
	
	 /* 创建Flag3_Task任务 */
//  xReturn = xTaskCreate((TaskFunction_t )Flag3_Task, /* 任务入口函数 */
//                        (const char*    )"Flag3_Task",/* 任务名字 */
//                        (uint16_t       )512,   /* 任务栈大小 */
//                        (void*          )NULL,	/* 任务入口函数参数 */
//                        (UBaseType_t    )1,	    /* 任务的优先级 */
//                        (TaskHandle_t*  )&Task3_Handle);/* 任务控制块指针 */
//  if(pdPASS == xReturn)
//    printf("创建Flag3_Task任务成功!\\r\\n");
  
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}



/**********************************************************************
  * @ 函数名  : LED_Task
  * @ 功能说明: LED_Task任务主体
  ********************************************************************/
uint8_t flag1;
uint8_t flag2;
uint8_t flag3;

/*优先级2*/
static void Flag1_Task(void* parameter)
{

	 while (1)
    {
			GetUARTLock();/*调用接收消息函数获得使用权*/  
			
			printf("Task1 Running!\\r\\n");
			
			PutUARTLock();/*调用发送消息函数释放使用权*/
			  
		
			vTaskDelay(1);   /* 延时1个tick */		 		
    }
}


/*优先级2*/
static void Flag2_Task(void* parameter)
{

  	while (1)
    {
      GetUARTLock();/*调用接收消息函数获得使用权*/
			
			printf("Task2 Running!\\r\\n");
			
			PutUARTLock();/*调用发送消息函数释放使用权*/
			
      vTaskDelay(1);   /* 延时1个tick */		 		
     
    }
}

/*优先级1*/
//static void Flag3_Task(void* parameter)
//{
//	static int value2;
//  	while (1)
//    {
//		  flag1=0;
//      flag2=0;
//			flag3=1;
//		  printf("Task3 Running! value=%d\\r\\n",value2);
//		  vTaskDelay(10);   /* 延时1个tick */		 		
//     
//    }
//}

/***********************************************************************
  * @ 函数名  : BSP_Init
  * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  * @ 参数    :   
  * @ 返回值  : 无
  *********************************************************************/
static void BSP_Init(void)
{
	/*
	 * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
	 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
	 * 都统一用这个优先级分组,千万不要再分组,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* LED 初始化 */
	LED_GPIO_Config();

	/* 串口初始化	*/
	USART_Config();
  
}

/********************************END OF FILE****************************/

划重点

这次实验创建了两个任务,任务优先级相同都为2


创建这几个函数

在任务处理事件中,像临界保护一样进行首先调用接收消息的函数获得使用权,再调用发送消息函数释放使用权。(队列里面有数据表示别人可以读这个队列)

仿真来看看实验效果

git仓库源码地址:https://gitee.com/he-dejiang/free-rtos.git

以上是关于七,FreeRTOS之——队列同步与互斥的主要内容,如果未能解决你的问题,请参考以下文章

韦东山freeRTOS系列教程之第四章同步互斥与通信

FreeRTOS高级篇5---FreeRTOS队列分析

Freertos-事件标志组,消息队列,信号量,二值信号量,互斥信号量

FreeRTOS学习笔记 ——二值信号量

FreeRTOS学习笔记 ——二值信号量

FreeRTOS学习笔记 ——二值信号量