freertos消息队列

目录

一、消息队列是什么?

二、消息队列的使用

1.相关函数

1.xQueueCreate()

2.xQueueCreateStatic()

3.xQueueSend()与xQueueSendToBack()

 4.xQueueReceive()

 5.xQueueReceiveFromISR()

三、代码测试:

1.两个任务间的数据通讯:

2..任务与中断的数据通讯:


一、消息队列是什么?

        在实际的应用中,常常会遇到 一个任务或者中断服务需要和另外一个任务进行“沟通交流” ,这个“沟通交流”的过程 其实就是 消息传递 的过程。在 没有操作系统 的时候两个应用 程序进行消息传递一般使用 全局变量 的方式,但是如果在使 操作系统 的应用中用 全局变量来传递消息就会涉及到“资源管理”的问题 FreeRTOS 对此提供了一个叫做“ 队列 的机制来 完成任务与任务、任务与中断之间的消息传递 ,由 于队列用来传递消息的,所以也称为消息队列。
        此外,任务从队列读数据或者写入数据到队列时,都可能被阻 塞。这个特性使得 任务可以被设计成基于事件驱动 的运行模式, 大大提高了 CPU 的执行效率。     
        通常队列采用 先进先出 (FIFO) 的存储缓冲机制,也就是 往队列 发送数据的时候 ( 也叫入队 ) 永远都是发送到队列的尾部,而从 队列 提取数据的时候 ( 也叫出队 ) 是从队列的头部提取的。但是 也可以使用 LIFO 的存储缓冲,也就是后进先出, FreeRTOS中的队列也提供了 LIFO 的存储缓冲机制。

 

二、消息队列的使用

1.相关函数

 

使用原则:先创建后使用、配对使用

1.xQueueCreate()

动态创建一个新的队列并返回可用于访问这个队列的句柄
函数原型:
QueueHandle_t xQueueCreate(
UBaseType_t uxQueueLength, /* 消息个数 */
UBaseType_t uxItemSize ); /* 每个消息大小,单位字节 */
返回值:
如果创建成功会返回消息队列的句柄 ,如果由于FreeRTOSConfig.h 文件中 heap 大小不足,无法为此消息队列提供所需的 空间会 返回 NULL

2.xQueueCreateStatic()

静态方法创建队列,队列所需要的内存由用户自行分配

函数原型:

QueueHandle_t xQueueCreateStatic(
UBaseType_t uxQueueLength,
uxUBaseType_t uxItemSize,
uint8_t* pucQueueStorageBuffer,
StaticQueue_t* pxQueueBuffer)
uxQueueLength :要创建的队列的队列长度,这里是队列的项目数
uxItemSize :队列中每个项目(消息)的长度,单位为字节
pucQueueStorageBuffer :指向队列项目的存储区,也就是消息的存储区,
这个存储区需要用户自行分配。此参数必须指向一个 uint8_t 类型的数组。
这个存储区要大于等于(uxQueueLength*uxItemsSize) 字节
pxQueueBuffer : 此参数指向一个 StaticQueue_t 类型的变量,用来保存队列
结构体
返回值 : 创建成功返回队列句柄

3.xQueueSend()与xQueueSendToBack()

两者等价,用于将数据发送到队列尾
函数原型:
BaseType_t xQueueSendToBack(
xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait );
参数:
参数 xQueue :目标队列的句柄。
参数 pvItemToQueue :入队元素的指针。队列将存储此指针指向的数据的备份。
参数 xTicksToWait :指定等待队列有空间 可以容纳新元素入队的最长等待(阻塞)时间 ,这种情况发生在队列已经满了的时候。如果需要等待,则任务会因为调用这个函 数而进入阻塞状态,直到队列非满而能让这个任务写入数据或者指定的阻塞时间过期, 才会转变为就绪态。 如果此参数使用0,则当队列已经满了的时候,此函数立即返回而不阻塞。使用portMAX_DELAY作为参数将使得等待时间为无限长 ,直到队列非满 而能让这个任务写入数据,注意如果要使用 portMAX_DELAY 为参数, 则必须在FreeRTOSConfig.h将INCLUDE_vTaskSuspend定义为1
返回值: pdPASS ,当元素成功入队时返回 pdPASS
              errQUEUE_FULL,因为队列满而无法入队时返回errQUEUE_FULL
注:xQueueSendFromISR()是在中断中将数据发送到队列尾,它共有三个参数,前两个参数与xQueueSend()的一样,由于中断中不允许一直等待,第三各参数默认将任务锁住,等待队列中的可用空间,成功将消息入队就解锁,使用方法与后面的xQueueReceiveFromISR()函数一样
运行过程图例:
当任务向消息队列中发送消息时,它首先判断是否有任务在等待消息队列的消息, 有的话,要先给高优先级的任务获得信息 :

 

如果没有任务在等待消息队列的消息,那么就会再判断消息队列当前是否已满:

 4.xQueueReceive()

接收消息队列中的数据

 函数原型:

BaseType_t xQueueReceive(
QueueHandle_t xQueue, /* 消息队列句柄 */
void *pvBuffer, /* 接收消息队列数据的缓冲地址 */
TickType_t xTicksToWait /* 等待消息队列有数据的最大等待时间 */
);
参数:
1 个参数:消息队列句柄。
2 个参数:从消息队列中复制出数据后所储存的缓冲地址,缓冲 区空间要 大于等于 消息队列创建函数 xQueueCreate 所指定的单个消 息大小,否则取出的数据无法全部存储到缓冲区,从而造成内存溢出。
3 个参数:消息队列为空时,等待消息队列有数据的最大等待时 间,单位系统时钟节拍。参数为 0 ,那么此函数会立即返回。
返回值:如果接收到消息返回 pdTRUE ,否则返回 pdFALSE
运行过程图例:
消息队列中已存在消息,通过内核服务将消息传递给 等待消息的任务中优先级最高的任务,或最先进入等待消息任务列表的任务。
如果消息队列为空,则等待消息的任务被放入等待消息的任务列表中,直到有其它任务向消息队列发送消息后才能解除该阻塞状态 或在超时的情况下运行

 5.xQueueReceiveFromISR()

中断中信息队列出队函数

函数原型:

BaseType_t xQueueReceive FromISR (
xQueueHandle pxQueue,
void *pvBuffer,
portBASE_TYPE *pxTaskWoken
);
pxQueue :发送项目的队列句柄
pvBuffer  :指向缓冲区的指针,将接收的项目被复制进去。
pxTaskWoken 任务将锁住,等待队列中的可用空间。如果 xQueueReceiveFromISR 引起一个任务解锁, *pxTaskWoken 将设置为pdTRUE,否则*pxTaskWoken保留不变
返回值 : pdTRUE :如果项目成功从队列接收。否则为: pdFALSE

三、代码测试:

1.两个任务间的数据通讯:

//led初始化和按键初始化过程已经省略
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
#include "string.h"
#include "queue.h"



//任务优先级
#define LED0_TASK_PRIO		1
//任务堆栈大小	
#define LED0_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);

//任务优先级
#define KEY_TASK_PRIO		4
//任务堆栈大小	
#define KEY_STK_SIZE 		50  
//任务句柄
TaskHandle_t KEYTask_Handler;
//任务函数
void key_task(void *pvParameters);

QueueHandle_t que1;
int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4(freertos)	 
	delay_init();	    				//延时函数初始化	  
	LED_Init();		  					//初始化LED
	KEY_Init();
	que1 = xQueueCreate(1,sizeof(u8));//动态创建消息队列
	//创建LED0任务
   xTaskCreate((TaskFunction_t )led0_task,   //任务函数  	
                (const char*    )"led0_task", //任;务名称  	
                (uint16_t       )LED0_STK_SIZE,//任务堆栈大小 
                (void*          )NULL,				//传递给任务函数的参数
                (UBaseType_t    )LED0_TASK_PRIO,//任务优先级	
                (TaskHandle_t*  )&LED0Task_Handler); //任务句柄   

		//创建KEY任务
    xTaskCreate((TaskFunction_t )key_task,     
                (const char*    )"key_task",   
                (uint16_t       )KEY_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )KEY_TASK_PRIO,
                (TaskHandle_t*  )&KEYTask_Handler);

    vTaskStartScheduler();          //开启任务调度
		/*
			调度器并非自动运行的,需要人为启动它。 API函
			数vTaskStartScheduler()用于启动调度器,它会创建一个空闲任务、初始化一些
			静态变量,最主要的,它会初始化系统节拍定时器并设置好相应的中断,然后启
			动第一个任务
		*/
		
}

u8 KEY_scan(void)
{
	static u8 key_up = 1;
	if(key_up && ( KEY5==0 || KEY6==0))
	{
		delay_xms(20);
		key_up = 0;
		if(KEY5==0) return KEY5_PRESS;
		else if(KEY6==0) return KEY6_PRESS;
	}
	else if (KEY5==1&&KEY6==1) 
	{
		key_up=1;
		return 0;
	}
}

void key_task(void *pvParameters)
{
	u8 key;
	while(1)
	{
		key=KEY_scan();
		if(key!=0)
			xQueueSend(que1,&key,portMAX_DELAY);
		vTaskDelay(100);
	}
}

void led0_task(void *pvParameters)
{
	u8 num;
	while(1)
	{
		xQueueReceive(que1,&num,portMAX_DELAY);
		switch(num)
		{
			case KEY5_PRESS:
				LED0=!LED0;
				break;
			case KEY6_PRESS:
				LED1=!LED1;
				break;
			vTaskDelay(300);
		}
		
	}

}   





相关功能实现:通过按键扫描返回一个键值,然后在key_task这个任务函数调用xQueueSend()将此键值入队,led0_task此任务调用xQueueReceive()函数接收信息队列中的信息,然后根据此消息做出相应操作

2..任务与中断的数据通讯:

消息队列用于实现18b20采集与定时器中断数码管显示

已省略很多代码,此处主要看在中断中如何接收队列中的数据

main.c中的一个任务函数:

void led0_task(void *pvParameters)
{
	unsigned int temperature = 0;
	while(1)
	{
		if(Tens==1)//后续用事件标志组来实现
		{
			temperature=DS18B20_Get_Temp();			
			Tens=0;
			xQueueSend(que1,&temperature,portMAX_DELAY);//参数1:事件句柄;参数2:要发的内容地址;参数3:等待时间
		}
		vTaskDelay(300);
	}
}   

定时器3的中断服务函数:

void TIM3_IRQHandler(void)
{
	
	BaseType_t pxHigherPriorityTaskWoken;
	pxHigherPriorityTaskWoken = pdFALSE;
	if(TIM_GetITStatus(TIM3,TIM_IT_Update) == SET) 
	{
		Digital_Display(temperature,T);
		if(T<3)
			T++;
		else
			T=0;
		if(counter<500)
			counter++;
		else	
		{
			counter=0;
			Tens=1;
			xQueueReceiveFromISR(que1,&temperature,&pxHigherPriorityTaskWoken);//参数1:事件句柄;参数2:将接收到的数据存入该地址;参数3:pdFALSE的地址
		}
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
	}

}

注:注意中断接收消息队列消息函数的第三个参数的设置。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香菜是个好东西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值