个人笔记--FreeRTOS消息队列学习总结

FreeRTOS消息队列

简介

  • 队列是为了任务与任务、任务与中断之间的通信而准备的。
  • 队列可以在任务与任务、任务与中断之间传递消息。
  • 队列中可以存储有限的、大小固定的数据项目。
  • 任务与任务、任务与中断之间要交流的数据保存在队列中,叫做队列项目。
  • 队列所能保存的最大数据项目的数量叫做队列的长度。
  • 创建队列时会指定数据项目的大小和队列的长度。
  • 由于队列是用来传递消息的所以队列也叫做 消息队列
  • FreeRTOS中的信号量也是通过队列实现的。
队列功能
  1. 数据存储
  2. 多任务访问
  3. 出队阻塞
  4. 入队阻塞

数据存储
  • 队列(queue)通常采用先进先出(FIFO)的存储机制,向队列发送数据时都是发送到队列的尾部,也叫入队。从队列取出数据时都是从队列的头部提取,也叫出队。
  • 有需要也可以使用后进先出(LIFO)机制
  • FreeRTOS的队列中的数据采用的是值传递,数据是通过赋值的形式传递进队列的,而不是使用指针,这样可以避免使用局部变量时函数结束变量删除造成的传输失败。(缺点:数据拷贝会浪费时间)

多任务访问
  • 队列不是属于某个特定任务的,任何任务都可以向队列中发送消息,或者从队列中提取消息。
  • 队列出现的目的就是为了多任务访问,在多个任务之间传递消息

出队阻塞
  • 当任务从一个队列中读取消息时,可以指定一个阻塞时间。确定当任务从队列中读取的消息无效时任务要阻塞多久。

举例:比如使用任务A来处理串口接收到的数据,首先串口接收到的数据会放到队列Q中,任务A从队列Q中读取数据。如果此队列Q是空的,那么A该怎么办?任务A有3种选择:

1.直接返回(阻塞时间为0)

2.等一小会儿(设置阻塞多久,就等多久)

3.死等到有数据(阻塞时间设置为portMAX_DELAY得最大值0xFFFF FFFF)


入队阻塞
  • 入队指的是任务向队列中发送数据。
  • 如果消息入队时队列中是满的,此时应设置入队阻塞时间
  • 入队阻塞时间设置和出队阻塞设置类似。
消息队列的运作机制
  • 通过FreeRTOS提供的函数创建消息队列,指定好队列的长度,每个消息的大小。
  • 任务或中断都可以给队列发送消息,如果队列未满或者使用覆盖入队,消息将会被拷贝(赋值)到队尾,否则任务会根据设置的阻塞时间进行阻塞。
  • 任务或中断都可以从队列读取消息,消息从队头开始被取出,如果队列为空则根据设置的阻塞时间进行阻塞。
  • FressRTOS队列还有发送紧急消息的功能,与发送消息几乎一样,只是消息直接被拷贝到对头,保证接受者优先接受到紧急消息。
  • 在这里插入图片描述
消息队列的应用场景
  1. 任务与任务之间消息交换
  2. 任务与中断之间信息交换
  3. 发送不定长消息
消息队列的使用
  • 使用消息队列首先要创建消息队列句柄,然后对队列的操作都要通过这个队列句柄。

    /*----------------------------------------------------------------------------------------------
    	信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核对象,必须先创建,
    	创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我们就可以通过这个句柄操作这些内核对象。
    	内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,任务间的事件同步
    	等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数来完成的
    ------------------------------------------------------------------------------------------------*/
    QueueHandle_t Test_Queue =NULL;//创建队列句柄
    
  • 消息队列创建函数xQueueCreate()—动态创建函数(通常不使用静态创建,所以不做介绍)

    /*
    	uxQueueLength:队列长度
    	uxItemSize:队列中消息单元的大小,字节为单位
    	此函数用于创建一个消息队列,创建成功则返回一个可用于访问所创建队列的句柄;创建失败返回NULL。
    */
    QueueHandle_t xQueueCreate( uxQueueLength, uxItemSize );
    
    //例如:创建一个长度为4,单元大小为1的队列
    QueueHandle_t Test_Queue =NULL;//首先创建队列句柄
    Test_Queue = xQueueCreate( 4, 1 );//调用队列创建函数
    if(Test_Queue!=NULL)
        printf("队列创建成功\r\n");
    else 
        printf("队列创建失败\r\n");
    
  • 消息队列删除函数—创建的队列如果不需要使用了应该删除以释放内存空间。

    /*-----------------------------------------------------------------------------
    	队列删除函数是根据队列句柄直接删除的,删除后队列空间被回收
    	未创建的队列不能被删除
    --------------------------------------------------------------------------------*/
    void vQueueDelete( QueueHandle_t xQueue );//队列删除函数原型
    vQueueDelete(Test_Queue);//删除队列Test_Queue
    
  • 向队列发送消息函数

    • xQueueSend() ----任务中调用

    • 有些版本的FreeRTOS中会看到xQueueSendToBack() ,此函数与xQueueSend()相同,只是为了向后兼容

      /*---------------------------------------------------------------------------------------
      	此函数用于向队列尾部发送一个队列消息
      	xQueue:队列句柄
      	pvItemToQueue:指针,指向要发送到队列尾部的队列消息
      	TicksToWait:队列满时,阻塞等待的最大超时时间(此参数设置为0,函数立即返回,设置为portMAX_DELAY函数被挂起)
      	消息发送成功返回pdTRUE,否则返回errQUEUE_FULL
      ------------------------------------------------------------------------------------------*/
      BaseType_t xQueueSend(QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t TicksToWait);//队列发送函数原型
      //使用示例
      void Send_Task(void * parameter)//队列发送任务
      {
          BaseType_t xReturn = pdPASS;//定义一个创建信息返回值,默认为 pdPASS (必须赋初值)
          uint32_t send_data1 = 1;//要发送的数据1
       	uint32_t send_data2 = 2;//要发送的数据2   
          while(1)
          {
              if(Key_Scan(1))//如果按键被按下
              {
                  printf("发送消息send_data1\r\n");
                  xReturn = xQueueSend(Test_Queue,&send_data1,0);
                  if(xReturn==pdPASS)
                      printf("消息send_data1发送成功\r\n");
              }
              if(Key_Scan(2))//如果按键被按下
              {
                  printf("发送消息send_data2\r\n");
                  xReturn = xQueueSend(Test_Queue,&send_data2,0);
                  if(xReturn==pdPASS)
                      printf("消息send_data2发送成功\r\n");
              }
          }
      }
      
    • xQueueSendFromISR() -----专用于中断服务程序中向队列中发送消息

      /*-------------------------------------------------------------------------------
      	在中断服务程序中用于向队列尾部发送一个消息
      	xQueue:队列句柄
      	pvItemToQueue:指针,指向要发送队列尾部的消息
      	pxHigherPriorityTaskWoken:如果入队导致某个人物解锁,并且解锁的任务优先级高于当前
      	被中断的任务,则将*pxHigherPriorityTaskWoken设置为pdTRUE,然后在退出前需要进行一次任务切换
      	消息发送成功返回 pdTRUE,否则返回 errQUEUE_FULL。
      -------------------------------------------------------------------------------*/
      BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t pxHigherPriorityTaskWoken);
      //使用示例
      void vBufferISR(void)
      {
          char cIn;
          BaseType_t xHigherPriorityTaskWoken = pdFALSE;//在ISR开始时,不唤醒任务
          do{
              cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );//从接受缓冲区获取一个字节数据
              xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );//发送这个数据
          }while( portINPUT_BYTE( BUFFER_COUNT ) );//循环到接收缓冲区为空
          if(xHigherPriorityTaskWoken)
              taskYIELD_FROM_ISR ();//任务切换
      }
      
    • xQueueSendToFront() ----用于向队列队首发送一个消息(不能在中断中调用)

      /*------------------------------------------------------------------------------------
      	用于向队列队首发送一个消息
      	xQueue:队列句柄
      	pvItemToQueue:指针,指向要发送的数据
      	TicksToWait:阻塞等待时间
      	消息发送成功返回 pdTRUE,否则返回 errQUEUE_FULL。
      ------------------------------------------------------------------------------------*/
      BaseType_t xQueueSendToFront( QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t TicksToWait );
      //使用与向队尾发送消息的函数类似
      
    • xQueueSendToFrontFromISR() ------用于向队列队首发送一个消息(专用在中断中调用)

      /*-------------------------------------------------------------------------------
      	在中断服务程序中用于向队列首部发送一个消息
      	xQueue:队列句柄
      	pvItemToQueue:指针,指向要发送队列首部的消息
      	pxHigherPriorityTaskWoken:如果入队导致某个人物解锁,并且解锁的任务优先级高于当前
      	被中断的任务,则将*pxHigherPriorityTaskWoken设置为pdTRUE,然后在退出前需要进行一次任务切换
      	消息发送成功返回 pdTRUE,否则返回 errQUEUE_FULL。
      -------------------------------------------------------------------------------*/
      BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t  pxHigherPriorityTaskWoken);
      

    • xQueueReceive() -----用于从队列中接收消息并把消息从队列中删除,接收消息是以拷贝的形式进行的。(不能用于中断函数中)

      /*---------------------------------------------------------------------------------------
      	此函数用于从队列中接收消息,并把接收的消息从队列中删除
      	xQueue:队列句柄
      	pvBuffer:指针,数据要存放的地方
      	TicksToWait:队列空时,阻塞等待的最大超时时间(此参数设置为0,函数立即返回,设置为portMAX_DELAY函数被挂起)
      	消息发送成功返回pdTRUE,否则返回errQUEUE_FULL
      ------------------------------------------------------------------------------------------*/
      BaseType_t xQueueReceive(QueueHandle_t xQueue,void *pvBuffer,TickType_t xTicksToWait);
      
      //使用示例
      void Receive_Task(void* parameter)
      {
          BaseType_t xReturn = pdTRUE;//定义一个创建信息返回值,默认为 pdPASS
          uint32_t r_queue; //定义一个接收消息的变量
          while (1) 
          {
              xReturn = xQueueReceive(Test_Queue, 	//消息队列的句柄
                                      &r_queue, 		//发送的消息内容
                                      portMAX_DELAY); //等待时间 一直等
              if (pdTRUE== xReturn)
                  printf("本次接收到的数据是: %d\n\n",r_queue);
              else
                  printf("数据接收出错,错误代码: 0x%lx\n",xReturn);
      	}
      }
      
    • xQueuePeek() —从队列中接收消息-并且不删除消息(具体用法同上个函数↑↑↑↑↑↑↑↑↑↑↑)

    • xQueueReceiveFromISR ( ) -----是 xQueueReceive ()的中断版本,用于在中断服务程序中接收
      一个队列消息并把消息从队列中删除;

      /*---------------------------------------------------------------------------------------
      	此函数用于从队列中接收消息,并把接收的消息从队列中删除
      	xQueue:队列句柄
      	pvBuffer:指针,数据要存放的地方
      	pxHigherPriorityTaskWoken:任务在往队列投递信息时, 如果队列满, 则任务将阻塞在该队列上。 如果 xQueueReceiveFromISR()到账了一个任 务 解 锁 了 则 将 *pxHigherPriorityTaskWoken 设 置 为pdTRUE ,
      	消息发送成功返回pdTRUE,否则返回errQUEUE_FULL
      ------------------------------------------------------------------------------------------*/
      BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,void *pvBuffer,BaseType_t *pxHigherPriorityTaskWoken);
      
    • xQueuePeekFromISR() ------从队列中接收消息-并且不删除消息(同上个函数一样只用于中断中)

消息队列使用至于事项
  1. 使用 xQueueSend()、 xQueueSendFromISR()、 xQueueReceive()等这些函数之前应先
    创建需消息队列,并根据队列句柄进行操作。
  2. 队列读取采用的是先进先出( FIFO)模式,会先读取先存储在队列中的数据。当
    然也 FreeRTOS 也支持后进先出(LIFO)模式,那么读取的时候就会读取到后进
    队列的数据。
  3. 在获取队列中的消息时候,我们必须要定义一个存储读取数据的地方,并且该数
    据区域大小不小于消息大小,否则,很可能引发地址非法的错误。
  4. 无论是发送或者是接收消息都是以拷贝的方式进行, 如果消息过于庞大,可以将
    消息的地址作为消息进行发送、接收。
  5. 队列是具有自己独立权限的内核对象,并不属于任何任务。所有任务都可以向同
    一队列写入和读出。一个队列由多任务或中断写入是经常的事,但由多个任务读
    出倒是用的比较少。
关于FreeRTOS的消息队列学习笔记–引用于《野火FreeRTOS内核实现与应用开发指南》以后有新的感悟会更新到此笔记中-----2020-11-1
  • 9
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
FreeRTOS提供了一些与消息队列相关的API。引用中提到了一些关键的API函数和机制,如任务从阻塞态转为就绪态的机制以及先进先出原则和后进先出原则。引用中介绍了消息队列的创建及初始化过程,并提到了消息队列控制块的结构和成员。引用中展示了消息队列删除函数vQueueDelete()的源码。 因此,FreeRTOS消息队列API包括但不限于以下几个函数: 1. xQueueCreate():用于创建一个消息队列,并返回一个消息队列句柄。 2. xQueueSend():用于向消息队列发送一条消息。 3. xQueueReceive():用于从消息队列接收一条消息。 4. xQueuePeek():用于查看消息队列中的下一条消息,但不会将其从队列中移除。 5. xQueueReset():用于重置消息队列,清空其中的所有消息。 6. xQueueDelete():用于删除消息队列,释放相应的内存空间。 这些API函数可以在FreeRTOS的API文档中找到详细的说明和使用示例。它们提供了一种方便和灵活的方式来实现任务之间的异步通信和数据传递。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [【FreeRTOSFreeRTOS学习笔记(14)— FreeRTOS消息队列(原生API)](https://blog.csdn.net/qq_39400113/article/details/120381691)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值