个人笔记--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队列还有发送紧急消息的功能,与发送消息几乎一样,只是消息直接被拷贝到对头,保证接受者优先接受到紧急消息。
  • 个人笔记--FreeRTOS消息队列学习总结_第1张图片

消息队列的应用场景

  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

你可能感兴趣的:(FreeRTOS学习笔记)