FreeRTOS(3)----消息队列

这篇具有很好参考价值的文章主要介绍了FreeRTOS(3)----消息队列。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.消息队列的基本概念

队列成为消息队列,可以进行任务与任务间,中断和任务间传递信息,实现任务接收来自其他任务或中断的不固定长度的消息,任务可以从队列中读取消息,当队列消息为空的时候,读取消息的任务将会被阻塞,但是可以设定等待阻塞任务的时候xTicksToWait(),当队列中有了新的信息,被阻塞的任务就会被唤醒去处理新的信息。当等待的时间超过指定的阻塞时间,且队列无有效信息,任务就会从阻塞态转为就绪态。

一个任务可以将多个消息放入任何一个消息队列,同时一个或者多个任务可以从消息队列中获得信息,当有多个信息发送到消息队列时,通常会把先放入消息队列给任务【先进先出原则】,但也支持后进先出。

二.消息队列的运作机制

创建消息队列freertos会给消息队列分配一块内存空间,这块内存大小等于消息队列控制块大小加上(单个消息的内存空间与消息队列长度的乘积),接着再初始化消息队列,此时消息队列为空。

系统会为队列控制块分配对应的内存空间,保存消息队列的一些信息,例如:头指针pcHead,尾指针pcTail,消息大小uxItemSize,队列长度uxLength等。

在创建成功的时候,内存已经被占用了,只有删除了消息队列,这段内存才会被释放掉。  消息空间可以存放不大于消息大小uxItemSize的任意类型数据,消息空间总数即是消息队列的长度。

主要分析:

当发送信息的时候,如果队列未满,则会将消息拷贝到队尾,否则,会根据用户指定的阻塞超过时间进行阻塞,如果队列一直不允许入队,任务则一直保持阻塞状态,等待队列允许入队,在这段时间,当别的任务从其他等待队列读入了数据(队列未满),任务将自动从阻塞态变为就绪态,当等待的时间超过阻塞时间,即使队列还不允许入队,任务也会从阻塞态变为就绪态,且发送信息的任务或者中断程序会收到一个错误码。

发送紧急消息:唯一的不同在于,消息会直接放在队头,这样接收者就可以优先接收到紧急消息,从而进行处理。

当任务读取一个队列的时候,可以指定一个阻塞时间,在这段时间,任务将保持阻塞状态来等待队列数据的有效,当其他任务或者中断程序往等待的队列写入了数据,任务将从阻塞态转为就绪态。或者当阻塞时间到了且队列中还是没有数据,任务也会从阻塞态转为就绪态。

三.消息队列的阻塞机制

一个任务A要读取队列中的信息:

三种选择:

·队列无消息,直接却干别的事情

·进入阻塞态,等待队列中来信息,当阻塞时间到了,任务转为就绪态,返回一个错误代码

·一直在阻塞态,直到队列中有信息

在中断中不允许带有阻塞机制的,所以需要调用在中断发送信息的API函数接口。

注意:当多个任务阻塞在一个消息队列中,这些阻塞的任务会按照任务的优先级进排序,优先级高的任务将优先获得访问队列的权利。

四.消息队列创建函数

1.创建队列

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,

UBaseType_t uxItemSize ); 

uxQueueLength:队列能够存储的最大消息单元数目,即队列长度
uxItemSize :队列中消息单元的大小,以字节为单位。
eg. xQueueCreate(3,sizeof(int )*3)     --->创建三个消息,大小为12字节
2.创建静态队列
QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength,
UBaseType_t uxItemSize,
uint8_t *pucQueueStorageBuffer,
StaticQueue_t *pxQueueBuffer );
uxQueueLength :队列能够存储的最大单元数目,即队列深度。
uxItemSize :队列中数据单元的长度,以字节为单位。
pucQueueStorageBuffer: 指针,指向一个 uint8_t 类型的数组,数组的大小至少uxQueueLength* uxItemSize 个字节。当 uxItemSize 0 时,pucQueueStorageBuffer 可以为 NULL
pxQueueBuffer :指针,指向 StaticQueue_t 类型的变量,该变量用于存储队列
的数据结构。
3.发送消息到队列
BaseType_t xQueueSend(QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait);
xQueue :队列句柄。
pvItemToQueue: 指针,指向要发送到队列尾部的队列消息。
xTicksToWait:
队列满时,等待队列空闲的最大超时时间。如果队列满并 且
xTicksToWait 被设置成 0 ,函数立刻返回。超时时间的单位为系统
节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,
单位为 ms 。如果 INCLUDE_vTaskSuspend 设置成 1 ,并且指定延时
portMAX_DELAY 将导致任务挂起(没有超时)。

 4.中断发送消息到队列

BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken);
  
xQueue :队列句柄。
pvItemToQueue :指针,指向要发送到队列尾部的消息。
pxHigherPriorityTaskWoken 如果入队导致一个任务解锁,并且解锁的任务优先级高
于当前被中断的任务,则将 *pxHigherPriorityTaskWoken
设置成 pdTRUE ,然后在中断退出前需要进行一次上下
文切换, 去 执 行 被 唤醒 的 优 先 级 更高 的 任 务 。从
FreeRTOS V7.3.0 起, pxHigherPriorityTaskWoken 作为一
个可选参数,可以设置为 NULL
5.紧急任务发送消息到队头【不可以用在中断中】
BaseType_t xQueueSendToFront( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );
xQueue :队列句柄。
pvItemToQueue : 指针,指向要发送到队首的消息。
xTicksToWait:
队列满时,等待队列空闲的最大超时时间。如果队列满并 且
xTicksToWait 被设置成 0 ,函数立刻返回。超时时间的单位为系统
节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,
单位为 ms 。如果 INCLUDE_vTaskSuspend 设置成 1 ,并且指定延时
portMAX_DELAY 将导致任务无限阻塞(没有超时)。
xQueueSendToFrontFromISR() 【可以在中断中使用】
6.接收队列消息
BaseType_t xQueueReceive(QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait);
xQueue :队列句柄。
pvBuffer:指针,指向接收到要保存的数据。
xTicksToWait :
队列空时,阻塞超时的最大时间。如果该参数设置为 0 ,函数立刻返
回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS
于辅助计算真实的时间,单位为 ms 。如果 INCLUDE_vTaskSuspend
置成 1 ,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没
有超时)。

7. 中断接收消息

BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,
void *pvBuffer,
BaseType_t *pxHigherPriorityTaskWoken);
 
xQueue :队列句柄。
pvBuffer:指针,指向接收到要保存的数据。
pxHigherPriorityTaskWoken :
任务在往队列投递信息时,如果队列满,则任务将阻塞
在该队列上。如果 xQueueReceiveFromISR() 到账了一个
任 务 解 锁 了 则将 *pxHigherPriorityTaskWoken 设 置为
pdTRUE , 否 则 *pxHigherPriorityTaskWoken 的 值将不
变。从 FreeRTOS V7.3.0 起, pxHigherPriorityTaskWoken
作为一个可选参数,可以设置为 NULL

代码理解:

 创建开始代码,设置任务1和任务2

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
	
	//创建消息队列
    Key_Queue=xQueueCreate(KEYMSG_Q_NUM,sizeof(u8));        //创建消息Key_Queue
    Message_Queue=xQueueCreate(MESSAGE_Q_NUM,USART_REC_LEN); //创建消息Message_Queue,队列项长度是串口接收缓冲区长度
	
    //创建TASK1任务
    xTaskCreate((TaskFunction_t )task1_task,             
                (const char*    )"task1_task",           
                (uint16_t       )TASK1_STK_SIZE,        
                (void*          )NULL,                  
                (UBaseType_t    )TASK1_TASK_PRIO,        
                (TaskHandle_t*  )&Task1Task_Handler);   
    //创建TASK2任务
    xTaskCreate((TaskFunction_t )Keyprocess_task,     
                (const char*    )"keyprocess_task",   
                (uint16_t       )KEYPROCESS_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )KEYPROCESS_TASK_PRIO,
                (TaskHandle_t*  )&Keyprocess_Handler); 
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}

任务1发送按键状态到消息队列

void task1_task(void *pvParameters)
{
	u8 key,i=0;
    BaseType_t err;
	while(1)
	{
		key=KEY_Scan(0);            	//扫描按键
        if((Key_Queue!=NULL)&&(key))   	//消息队列Key_Queue创建成功,并且按键被按下
        {
            err=xQueueSend(Key_Queue,&key,10);
            if(err==errQUEUE_FULL)   	//发送按键值
            {
                printf("队列Key_Queue已满,数据发送失败!\r\n");
            }
        }
        i++;
        if(i%10==0) check_msg_queue();//检Message_Queue队列的容量
        if(i==50)
        {
            i=0;
            LED0=!LED0;
        }
        vTaskDelay(10);                           //延时10ms,也就是10个时钟节拍	
	}
}

任务2接收队列中的按键消息

void Keyprocess_task(void *pvParameters)
{
	u8 num,key;
	while(1)
	{
        if(Key_Queue!=NULL)
        {
            if(xQueueReceive(Key_Queue,&key,portMAX_DELAY))//请求消息Key_Queue
            {
                switch(key)
                {
                    case WKUP_PRES:		//KEY_UP控制LED1
                        LED1=!LED1;
                        break;
                    case KEY0_PRES:		//KEY0刷新LCD背景
                        num++;
                        LCD_Fill(126,111,233,313,lcd_discolor[num%14]);
                        break;
                }
            }
        } 
		vTaskDelay(10);      //延时10ms,也就是10个时钟节拍	
	}
}

在串口1中断中将接收到的信息发送到消息队列,清零任务标志位以及数组内容【为下一次接收做准备】

//就向队列发送接收到的数据
	if((USART_RX_STA&0x8000)&&(Message_Queue!=NULL))
	{
		xQueueSendFromISR(Message_Queue,USART_RX_BUF,&xHigherPriorityTaskWoken);//向队列中发送数据
		
		USART_RX_STA=0;	
		memset(USART_RX_BUF,0,USART_REC_LEN);//清除数据接收缓冲区USART_RX_BUF,用于下一次数据接收
	
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换
	}

 设置定时器在定时器中断中接收串口发送到队列的消息,将接收到的信息打印在LCD显示屏上文章来源地址https://www.toymoban.com/news/detail-841308.html

//定时器2中断服务函数
void TIM2_IRQHandler(void)
{
	u8 *buffer;
	BaseType_t xTaskWokenByReceive=pdFALSE;
	BaseType_t err;
	
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) //溢出中断
	{
		buffer=mymalloc(USART_REC_LEN);
        if(Message_Queue!=NULL)
        {
			memset(buffer,0,USART_REC_LEN);	//清除缓冲区
			err=xQueueReceiveFromISR(Message_Queue,buffer,&xTaskWokenByReceive);//请求消息Message_Queue
            if(err==pdTRUE)			//接收到消息
            {
				disp_str(buffer);	//在LCD上显示接收到的消息
            }
        }
		myfree(buffer);		//释放内存
		
		portYIELD_FROM_ISR(xTaskWokenByReceive);//如果需要的话进行一次任务切换
	}
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);  //清除中断标志位
}

到了这里,关于FreeRTOS(3)----消息队列的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FreeRTOS教程4 消息队列

    正点原子stm32f407探索者开发板V2.4 STM32CubeMX软件(Version 6.10.0) Keil µVision5 IDE(MDK-Arm) 野火DAP仿真器 XCOM V2.6串口助手 本文主要学习 FreeRTOS 消息队列的相关知识, 包括消息队列概述、创建删除复位队列、写入/读取数据到队列等关于队列的基础知识 在一个实时操作系统构成的

    2024年03月14日
    浏览(43)
  • FreeRTOS 消息队列 详解

    目录 什么是队列? 消息队列特点 1. 数据入队出队方式 2. 数据传递方式 3. 多任务访问 4. 出队、入队阻塞 消息队列相关 API 函数 1. 创建队列 2. 写队列 3. 读队列 消息队列实操 队列又称 消息队列 ,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任 务

    2024年02月08日
    浏览(44)
  • STM32F429 Discovery开发板应用:使用FreeRTOS队列+DMA双缓存实现串口数据接收

      参考帖子:https://blog.csdn.net/freedompoi/article/details/122350866 目前想要实现STM32F4自带的DMA双缓冲区,尝试过一版,结果不能预期,就使用了RxHalfCplt和RxCplt去实现DMA双缓冲区的效果。 现在有时间了,又重新实现STM32F4自带的DMA双缓冲区,作为参考。   MCU:STM32F429ZIT6 开发环境:

    2024年02月08日
    浏览(51)
  • 【FreeRTOS】详细讲解FreeRTOS中消息队列并通过示例讲述其用法

      在裸机系统中,两个程序间需要共享某个资源通常使用全局变量来实现;但在含操作系统(下文就拿 FreeRTOS 举例)的开发中,则使用消息队列完成。那么这两者有啥区别呢?🤔🤔🤔   其实在FreeRTOS系统中也能够像裸机似的,使用全局变量实现多程序共享某个资源(这里

    2024年02月09日
    浏览(40)
  • FreeRTOS源码分析-7 消息队列

    目录 1 消息队列的概念和作用 2 应用 2.1功能需求 2.2接口函数API 2.3 功能实现 3 消息队列源码分析 3.1消息队列控制块 3.2消息队列创建 3.3消息队列删除 3.4消息队列在任务中发送 3.5消息队列在中断中发送 3.6消息队列在任务中接收 3.7消息队列在中断中接收  消息队列(queue),可

    2024年02月14日
    浏览(39)
  • 【FreeRTOS】【应用篇】消息队列【下篇】

    本篇文章主要对 FreeRTOS 中消息队列的概念和相关函数进行了详解 消息队列【下篇】详细剖析了消息队列中发送、接收时队列消息控制块中各种指针的行为,以及几个发送消息和接收消息的函数的运作流程 笔者有关于 【FreeRTOS】【应用篇】消息队列【上篇】——队列基本概念

    2024年02月10日
    浏览(41)
  • FreeRTOS通过消息队列实现串口命令解析(串口中断)

    作者:Jack_G 时间:2023.08.08 版本:V1.0 上次修改时间: 环境: quad quad quad quad STM32Cube MX V6.8.1 quad quad quad quad STM32CubeH7 Firmware Package V1.11.0 / 04-Nov-2022 quad quad quad quad Keil: V5.29 正常配置,不过 需要勾选全局中断 ,后续在接收中断中将接收到的数据送入消息队列。 在usa

    2024年02月14日
    浏览(36)
  • FreeRTOS中断调用API消息队列发送函数导致系统死机(memcpy函数卡死)

    背景:写一组在FreeRTOS系统下的串口驱动 ,芯片使用的是杰发科的 AC781x系列 , ARM® CortexM3 内核,96MHz主频。 项目场景:计划使用dma接收数据,设置dma半满中断与全满中断,在半满中断中把前半部分数据传入消息队列,在全满中断中把后半部分数据传入消息队列。 问题1: 在中

    2024年02月15日
    浏览(47)
  • 【FreeRTOS】FreeRTOS移植stm32详细步骤介绍

    我在查找FreeRTOS移植的相关教程特别少,所以想非常详细的介绍FreeRTOS移植stm32详细步骤,包括源码的下载,源码介绍,系统移植,代码验证等,每一步都有对应的介绍和解释,希望可以帮助到你们。 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分

    2024年02月08日
    浏览(48)
  • 基于STM32的实时操作系统FreeRTOS移植教程(手动移植)_stm32移植freertos(1)

    直接意识代码: 这是我们大脑最希望的添加代码方式,很显然他是 错的 , 两个任务之间产生了相互的影响 ,使得两个任务都执行错误,这种思想在 裸机开发 中肯定是 错的 ,但是在我们的 RTOS 中他就可以是 对的 。 任务型代码: 这是 独立的两个任务内容 ,我们只需要把

    2024年04月10日
    浏览(109)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包