0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于FreeRTOS的STM32F103系统—队列

冬至子 来源:月月望归鸟 作者:K.Fire 2023-11-10 11:37 次阅读

FreeRTOS中,队列是实现任务之间同步、互斥和通信的一种重要方法(其他的实现方法有:任务通知、事件组、信号量、互斥量)。

任何任务都可以向队列里存放任何数据,任何任务也可以从队列里读取数据,实现不同任务之间的通信。

1

队列特性

队列的数据的操作采用先进先出的方法(FIFO,First In First Out):写数据时放到尾部,读数据时从头部读,逻辑顺序如下图所示。

图片

使用队列传输数据时有两种方法:

  • 拷贝:把数据、把变量的值复制进队列里
  • 引用:把数据、把变量的地址复制进队列里

FreeRTOS中的队列一般都使用拷贝的方式传输数据,局部变量的值可以发送到队列中,后续即使函数退出、局部变量被回收,也不会影响队列中的数据,发送任务、接收任务解耦时,接收任务不需要知道这数据是谁的、也不需要发送任务来释放数据。

如果数据实在太大,还是可以使用队列传输它的地址。

2

队列函数

1.创建

队列的创建有两种方法:动态分配内存、静态分配内存。

一般都用动态分配内存的方法,使用函数:xQueueCreate()

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

参数解释:

  • uxQueueLength :队列长度
  • uxItemSize:每个数据的大小,以字节为单位
  • 返回值:非0:成功,返回句柄,以后使用句柄来操作队列;NULL:失败,因为内存不足

2.删除

删除队列的函数为 vQueueDelete() ,只能删除使用动态方法创建的队列,它会释放内存。

void vQueueDelete( QueueHandle_t xQueue );

参数解释:

  • xQueue:队列句柄

3.写队列

可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在 ISR 中使用。

在任务中使用:

BaseType_t xQueueSend( QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait );

在ISR中使用:

BaseType_t xQueueSendToBackFromISR( QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken );

参数解释:

  • xQueue :队列句柄,要写哪个队列
  • pvItemToQueue : 数据指针,这个数据的值会被复制进队列
  • xTicksToWait :如果队列满则无法写入新数据,可以让任务进入阻塞状态,xTicksToWait表示阻塞的最大时间(Tick Count)。如果被设为0,无法写入数据时函数会立刻返回;如果被设为portMAX_DELAY,则会一直阻塞直到有空间可写
  • 返回值:pdPASS:数据成功写入了队列;errQUEUE_FULL:写入失败,因为队列满了。

4.读队列

使用 xQueueReceive() 函数读队列,读到一个数据后,队列中该数据会被移除。这个函数有两个版 本:在任务中使用、在ISR 中使用。

BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait );
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxTaskWoken );

参数解释:

  • xQueue :队列句柄,要写哪个队列
  • pvBuffffer: bufer 指针,队列的数据会被复制到这个 buffer
  • xTicksToWait :如果队列空则无法读出数据,可以让任务进入阻塞状态,xTicksToWait表示阻塞的最大时间(Tick Count)。如果被设为0,无法读出数据时函数会立刻返回;如果被设为portMAX_DELAY,则会一直阻塞直到有数据可写
  • 返回值:pdPASS:从队列读出数据入;errQUEUE_EMPTY:读取失败,因为队列空了。

5.其他

复位:队列刚被创建时,里面没有数据;使用过程中可以调用 xQueueReset() 把队列恢复为初始状态。

/* 
pxQueue : 复位哪个队列; 
 * 返回值: pdPASS(必定成功)
 */ 
BaseType_t xQueueReset( QueueHandle_t pxQueue);

查询:可以查询队列中有多少个数据、有多少空余空间。

/** 返回队列中可用数据的个数 */ 
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ); 
/** 返回队列中可用空间的个数 */ 
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );

覆盖:当队列长度为 1 时,可以使用 xQueueOverwrite() 或 xQueueOverwriteFromISR() 来覆盖数据。注意,队列长度必须为1。当队列满时,这些函数会覆盖里面的数据,这也以为着这些函数不会被阻塞。

/* 覆盖队列
 * xQueue: 写哪个队列
 * pvItemToQueue: 数据地址
 * 返回值: pdTRUE表示成功, pdFALSE表示失败
 */ 
BaseType_t xQueueOverwrite(QueueHandle_t xQueue, const void * pvItemToQueue ); 
BaseType_t xQueueOverwriteFromISR( QueueHandle_t xQueue, const void * pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken );

偷看:如果想让队列中的数据供多方读取,也就是说读取时不要移除数据,要留给后来人。那么可以使用" 窥 视" ,也就是 xQueuePeek() 或 xQueuePeekFromISR() 。这些函数会从队列中复制出数据,但是不移除数据。这也意味着,如果队列中没有数据,那么" 偷看 " 时会导致阻塞;一旦队列中有数据,以后每次 " 偷看" 都会成功。

/* 偷看队列
 * xQueue: 偷看哪个队列
 * pvItemToQueue: 数据地址, 用来保存复制出来的数据
 * xTicksToWait: 没有数据的话阻塞一会
 * 返回值: pdTRUE表示成功, pdFALSE表示失败
 */ 
BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait );
BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void *pvBuffer, );

3

队列实验

代码:

/* vSenderTask被用来创建2个任务,用于写队列
 * vReceiverTask被用来创建1个任务,用于读队列
 */
static void vSenderTask( void *pvParameters );
static void vReceiverTask( void *pvParameters );

/*-----------------------------------------------------------*/

/* 队列句柄, 创建队列时会设置这个变量 */
QueueHandle_t xQueue;

int main( void )
{
  prvSetupHardware();

    /* 创建队列: 长度为5,数据大小为4字节(存放一个整数) */
    xQueue = xQueueCreate( 5, sizeof( int32_t ) );

  if( xQueue != NULL )
  {
    /* 创建2个任务用于写队列, 传入的参数分别是100、200
     * 任务函数会连续执行,向队列发送数值100、200
     * 优先级为1
     */
    xTaskCreate( vSenderTask, "Sender1", 1000, ( void * ) 100, 1, NULL );
    xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL );

    /* 创建1个任务用于读队列
     * 优先级为2, 高于上面的两个任务
     * 这意味着队列一有数据就会被读走
     */
    xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );

    /* 启动调度器 */
    vTaskStartScheduler();
  }
  else
  {
    /* 无法创建队列 */
  }

  /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
  return 0;
}
/*-----------------------------------------------------------*/



/*-----------------------------------------------------------*/

static void vSenderTask( void *pvParameters )
{
  int32_t lValueToSend;
  BaseType_t xStatus;

  /* 我们会使用这个函数创建2个任务
   * 这些任务的pvParameters不一样
    */
  lValueToSend = ( int32_t ) pvParameters;

  /* 无限循环 */
  for( ;; )
  {
    /* 写队列
     * xQueue: 写哪个队列
     * &lValueToSend: 写什么数据? 传入数据的地址, 会从这个地址把数据复制进队列
     * 0: 不阻塞, 如果队列满的话, 写入失败, 立刻返回
     */
    xStatus = xQueueSendToBack( xQueue, &lValueToSend, 0 );

    if( xStatus != pdPASS )
    {
      printf( "Could not send to the queue.rn" );
    }
  }
}
/*-----------------------------------------------------------*/

static void vReceiverTask( void *pvParameters )
{
  /* 读取队列时, 用这个变量来存放数据 */
  int32_t lReceivedValue;
  BaseType_t xStatus;
  const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );

  /* 无限循环 */
  for( ;; )
  {
    /* 读队列
     * xQueue: 读哪个队列
     * &lReceivedValue: 读到的数据复制到这个地址
     * xTicksToWait: 如果队列为空, 阻塞一会
     */
    xStatus = xQueueReceive( xQueue, &lReceivedValue, xTicksToWait );

    if( xStatus == pdPASS )
    {
      /* 读到了数据 */
      printf( "Received = %drn", lReceivedValue );
    }
    else
    {
      /* 没读到数据 */
      printf( "Could not receive from the queue.rn" );
    }
  }
}

在这个程序中,有一个接收队列数据的任务,两个发送队列数据的任务,接收队列数据的任务优先级高,先执行,但是这时队列为空,触发该任务阻塞,这时低优先级的任务交替执行,向队列中发送数据,接收任务发现队列不为空后(解除触发的事件),立刻被唤醒从队列中读取数据并打印出来,实验结果和逻辑图如下:

图片

图片

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FreeRTOS
    +关注

    关注

    12

    文章

    483

    浏览量

    61993
  • STM32F103
    +关注

    关注

    33

    文章

    476

    浏览量

    63464
  • ISR
    ISR
    +关注

    关注

    0

    文章

    38

    浏览量

    14401
  • FIFO存储
    +关注

    关注

    0

    文章

    103

    浏览量

    5963
  • 调度器
    +关注

    关注

    0

    文章

    98

    浏览量

    5237
收藏 人收藏

    评论

    相关推荐

    基于STM32F103的SVPWM算法实现

    基于STM32F103的SVPWM算法实现
    发表于 03-22 12:12 90次下载

    基于STM32F103的振动监测系统设计

    基于STM32F103的振动监测系统设计。
    发表于 11-09 17:49 43次下载

    STM32F103例程

    STM32F103例程。
    发表于 03-23 14:57 0次下载

    STM32F103封装方式与功能配置

    本文介绍STM32F103封装方式和STM32F103管脚功能的配置。
    发表于 08-03 17:44 2.2w次阅读
    <b class='flag-5'>STM32F103</b>封装方式与功能配置

    STM32F103的振动监测系统设计

    STM32F103的振动监测系统设计
    发表于 09-28 14:45 49次下载
    <b class='flag-5'>STM32F103</b>的振动监测<b class='flag-5'>系统</b>设计

    STM32F103的汽车远程防盗系统设计

    STM32F103的汽车远程防盗系统设计
    发表于 09-28 15:12 17次下载
    <b class='flag-5'>STM32F103</b>的汽车远程防盗<b class='flag-5'>系统</b>设计

    STM32F103使用总结

    STM32F103使用总结
    发表于 10-24 10:22 152次下载

    stm32f103移植

    stm32f103移植
    发表于 10-27 09:03 43次下载
    <b class='flag-5'>stm32f103</b>移植

    STM32F103教程之STM32F103单片机的使用心得资料免费下载

    本文档的主要内容详细介绍的是STM32F103教程之STM32F103单片机的使用心得资料免费下载目前很火的设计STM32教程。
    发表于 09-26 08:00 254次下载

    STM32F103 CAN模板程序

    STM32F103 CAN模板程序
    发表于 11-09 11:08 67次下载
    <b class='flag-5'>STM32F103</b> CAN模板程序

    stm32f030和stm32f103功能差异

    stm32f030和stm32f103功能差异主要表现在哪?
    的头像 发表于 07-22 09:35 2.2w次阅读

    STM32F103 模拟IIC时序

    STM32F103 模拟IIC时序
    发表于 11-25 09:51 34次下载
    <b class='flag-5'>STM32F103</b>  模拟IIC时序

    GD32F103STM32F103的区别 2021.6.2

    GD32F103STM32F103区别介绍关键词Key words:GD32F103STM32F103摘要Abstract:本文主要是GD32F
    发表于 12-08 11:06 83次下载
    GD32<b class='flag-5'>F103</b>与<b class='flag-5'>STM32F103</b>的区别 2021.6.2

    RT-Thread系统移植到STM32f103

    RT-Thread系统移植到STM32f103
    发表于 12-09 12:51 26次下载
    RT-Thread<b class='flag-5'>系统</b>移植到<b class='flag-5'>STM32f103</b>

    STM32F103开源分享

    电子发烧友网站提供《STM32F103开源分享.zip》资料免费下载
    发表于 08-22 10:42 105次下载
    <b class='flag-5'>STM32F103</b>开源分享