基于FreeRTOS的STM32F103系统—队列

发布者:和谐的24号最新更新时间:2024-04-22 来源: elecfans关键字:FreeRTOS  队列 手机看文章 扫描二维码
随时随地手机看文章

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


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


1


队列特性


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

8a6e6d5fa90f5ea19b4133588696e9ba_wKgZomVNpTyAXx3cAAGW2jtnoYI649.jpg

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


拷贝:把数据、把变量的值复制进队列里

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

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  队列 引用地址:基于FreeRTOS的STM32F103系统—队列

上一篇:STM32H5 DA证书链实战经验
下一篇:STM32基础知识:串口通信-DMA方式

推荐阅读最新更新时间:2024-11-02 10:54

51单片机的FIFO(先入先出)循环队列实现
////////////////////////////////////////////////////////// // 文件:config.h ////////////////////////////////////////////////////////// #ifndef __CONFIG_H #define __CONFIG_H //这一段无需改动 //This segment should not be modified #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif typedef unsigned char
[单片机]
单片机的FIFO(先入先出)循环队列实现
////////////////////////////////////////////////////////// // 文件:config.h ////////////////////////////////////////////////////////// #ifndef __CONFIG_H #define __CONFIG_H //这一段无需改动 //This segment should not be modified #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif typedef unsigned char
[单片机]
单片机的FIFO(先入先出)循环<font color='red'>队列</font>实现
STM32+FreeRTOS+CUBEMX_学习笔记(七 )输入捕获
前言 我们可以利用输入捕获模式来测量脉冲宽度或者测量频率。 1、cube设置 2、函数详解: /** * @brief Read the captured value from Capture Compare unit 读取通道捕获值 * @param htim TIM handle. * @param Channel TIM Channels to be enabled * This parameter can be one of the following values: * @arg TIM_CHANNEL_1: TIM Channel 1 selected * @arg T
[单片机]
STM32+<font color='red'>FreeRTOS</font>+CUBEMX_学习笔记(七 )输入捕获
stm32 freertos 之串口中断
一、中断处理函数 void USART1_IRQHandler(void) { BaseType_t xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; u8 cChar; if(USART_GetITStatus (USART1,USART_IT_RXNE)!=RESET) { cChar=USART_ReceiveData(USART1); xQueueSendToBackFromISR (xQueueRx,&cChar,&xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPri
[单片机]
STM32+FreeRTOS+CUBEMX_学习笔记(三)
一、前言和目的: 在使用freertos的过程中,难免会遇到关于任务优先级和时间片分配的问题。为了更好的使用该系统,学习了下面的一些知识。 看freertos源文档 时间片调度 抢占式调度 对比两种调度的方式 二、freertos文档: 2.0、看看源文档: 源文档是个好东西啊,虽然网上的论坛和帖子里面都存在很多的答案,但是任何转述的东西或者自己的语言都经过了一定的加工和改造。虽然可能会让人更加容易理解,但是终究是不如源文档的正式和官方。 以下为对源文档的学习和翻译: 2.1、freertos任务定义: Tasks are implemented as C functions. The only thing speci
[单片机]
STM32+<font color='red'>FreeRTOS</font>+CUBEMX_学习笔记(三)
STM32+FreeRTOS+CUBEMX_学习笔记(五 )ADC采样
前言 ADC采样是一种常见的功能 1、STM系列ADC的参数: 手册中关于ADC的介绍 ● 12位分辨率 ● 转换结束、注入转换结束和发生模拟看门狗事件时产生中断 ● 单次和连续转换模式 ● 从通道0到通道n的自动扫描模式 ● 自校准 ● 带内嵌数据一致性的数据对齐 ● 采样间隔可以按通道分别编程 ● 规则转换和注入转换均有外部触发选项 ● 间断模式 ● 双重模式(带2个或以上ADC的器件) ● ADC转换时间: ─ STM32F103xx增强型产品:时钟为56MHz时为1μs(时钟为72MHz为1.17μs) ─ STM32F101xx基本型产品:时钟为28MHz时为1μs(时钟为36MHz为1.55μs) ─ STM3
[单片机]
STM32+<font color='red'>FreeRTOS</font>+CUBEMX_学习笔记(五 )ADC采样
嵌入式操作系统FreeRTOS的原理与实现
在嵌入式领域中,嵌入式实时操作系统正得到越来越广泛的应用。采用嵌入式实时操作系统(RTOS)可以更合理、更有效地利用CPU的资源,简化应用软件的设计,缩短系统开发时间,更好地保证系统的实时性和可靠性。由于RTOS需占用一定的系统资源(尤其是RAM资源),只有μC/OS-II、embOS、salvo、FreeRTOS等少数实时操作系统能在小RAM单片机上运行。相对于C/OS-II、 embOS等商业操作系统,FreeRTOS操作系统是完全免费的操作系统,具有源码公开、可移植、可裁减、调度策略灵活的特点,可以方便地移植到各种单片机上运行,其最新版本为2.6版。 1 FreeRTOS操作系统功能 作为一个轻量级的操作系统,FreeR
[嵌入式]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved