五、freeRTOS_队列的使用

2023-05-16

目录

1. 队列的理论讲解

1.1 常规操作

 2. 队列的常规使用

3. 队列集


1. 队列的理论讲解

1.1 常规操作


队列的简化操如入下图所示,从此图可知:
队列可以包含若干个数据:队列中有若干项,这被称为"长度"(length)
每个数据大小固定
创建队列时就要指定长度、数据大小
数据的操作采用先进先出的方法(FIFO,First In First Out):写数据时放到尾部,读数据时从头部

也可以强制写队列头部:覆盖头部数据

本节源码:13_freertos_example_queue,基于12_freertos_example_sync_exclusion修改。

 猜测队列中应该有:

  • 有等待数据的任务列表 list1
  • 有等待写数据的任务列表 list2
  • 指向内存的 *buf

 以下为xQueueCreate 队列源码:

  • 有头部,有尾部,是一个环形buf。
  • 有两个链表,等待发送数据,等待接收数据

创建环形队列的图解: 

 写队列

读队列

 

往头部写队列,xQueueSendToFront 这个函数写的数据,会最先被读出来,因为数据被插到了队列的头部。

假如已经在0、1位置已经写入了数据,之后再头部写入数据会在N-1③位置,在向后移动指针pcReadFrom,pcReadFrom -= ItemSize,因为pcReadFrom表示的是上一次的位置,所以向后移动之后再次读到的数据为N-1③位置。

有很多个写队列,或者有很多个读队列时,都有等待函数,那现在该唤醒谁呢?

一般会先执行优先级最高的那个,或者等待时间最长的那个,理所应当是这样的。

 2. 队列的常规使用

本节源码:13_freertos_example_queue,基于12_freertos_example_sync_exclusion修改。

队列使用了同步:

任务1、任务2使用队列实现了同步。

任务1计算完数值之后,写入队列中,任务2读取队列,当队列中有数据的时候就会打印出来,当队列中没有数据的时候就会进入到阻塞状态,任务2在等待数据的过程中,就不会参与cpu的调度。

static int sum = 0;
static volatile int flagCalcEnd = 0;
static QueueHandle_t xQueueCalcHandle;

void Task1Function(void * param)
{
	volatile int i = 0;
	while (1)
	{
		for (i = 0; i < 10000000; i++)
			sum++;
		//printf("1");
		//flagCalcEnd = 1;
		//vTaskDelete(NULL);
		xQueueSend(xQueueCalcHandle, &sum, portMAX_DELAY);
		sum = 1;
	}
}

void Task2Function(void * param)
{
	int val;
	
	while (1)
	{
		//if (flagCalcEnd)
		flagCalcEnd = 0;
		xQueueReceive(xQueueCalcHandle, &val, portMAX_DELAY);
		flagCalcEnd = 1;
		printf("sum = %d\r\n", val);
	}
}

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

int main( void )
{
	TaskHandle_t xHandleTask1;
		
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();

	printf("Hello, world!\r\n");

	xQueueCalcHandle = xQueueCreate(2, sizeof(int));
	if (xQueueCalcHandle == NULL)
	{
		printf("can not create queue\r\n");
	}

	InitUARTLock();

	xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
	xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

运行结果,在 “四、freeRTOS_同步互斥与通信概述” 中,任务2与任务1竞争cpu的资源,导致任务1实现同步花了4s,现在使用队列同步的方法来做,任务2在等待数据的过程中,就不会参与cpu的调度,此时任务2大概花费了2s。

URAT1串口打印

队列使用了互斥:

 任务3、任务4使用队列实现了互斥。

临界资源是 TaskGenericFunction() 函数,使用初始化串口锁,上锁,下锁,来完成队列的互斥。

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

static int sum = 0;
static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;
static QueueHandle_t xQueueCalcHandle;
static QueueHandle_t xQueueUARTcHandle;


int InitUARTLock(void)
{	
	int val;
	xQueueUARTcHandle = xQueueCreate(1, sizeof(int));
	if (xQueueUARTcHandle == NULL)
	{
		printf("can not create queue\r\n");
		return -1;
	}
	xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);
	return 0;
}

void GetUARTLock(void)
{	
	int val;
	xQueueReceive(xQueueUARTcHandle, &val, portMAX_DELAY);
}

void PutUARTLock(void)
{	
	int val;
	xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);
}


void Task1Function(void * param)
{
	volatile int i = 0;
	while (1)
	{
		for (i = 0; i < 10000000; i++)
			sum++;
		//printf("1");
		//flagCalcEnd = 1;
		//vTaskDelete(NULL);
		xQueueSend(xQueueCalcHandle, &sum, portMAX_DELAY);
		sum = 1;
	}
}

void Task2Function(void * param)
{
	int val;
	
	while (1)
	{
		//if (flagCalcEnd)
		flagCalcEnd = 0;
		xQueueReceive(xQueueCalcHandle, &val, portMAX_DELAY);
		flagCalcEnd = 1;
		printf("sum = %d\r\n", val);
	}
}

void TaskGenericFunction(void * param)
{
	while (1)
	{
		GetUARTLock();
		printf("%s\r\n", (char *)param);
		   // task 3 is waiting
		PutUARTLock(); /* task 3 ==> ready, task 4 is running  */
		vTaskDelay(1);
	}
}


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

int main( void )
{
	TaskHandle_t xHandleTask1;
		
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();

	printf("Hello, world!\r\n");

	xQueueCalcHandle = xQueueCreate(2, sizeof(int));
	if (xQueueCalcHandle == NULL)
	{
		printf("can not create queue\r\n");
	}

	InitUARTLock();

	xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
	xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

	xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
	xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);

	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

运行结果:

任务3、任务4在互斥的交替执行。

 传输大块数据:

FreeRTOS的队列使用拷贝传输,也就是要传输uint32_t时,把4字节的数据拷贝进队列;要传输一个8字节的结构体时,把8字节的数据拷贝进队列。
如果要传输1000字节的结构体呢?写队列时拷贝1000字节,读队列时再拷贝1000字节?不建议这么做,影响效率!
这时候,我们要传输的是这个巨大结构体的地址:把它的地址写入队列,对方从队列得到这个地址,使用地址去访问那1000字节的数据。
使用地址来间接传输数据时,这些数据放在RAM里,对于这块RAM,要保证这几点:

  • RAM的所有者、操作者,必须清晰明了,这块内存,就被称为"共享内存"。要确保不能同时修改RAM。比如,在写队列之前只有由发送者修改这块RAM,在读队列之后只能由接收者访问这块RAM。
  • RAM要保持可用,这块RAM应该是全局变量,或者是动态分配的内存。对于动然分配的内存,要确保它不能提前释放:要等到接收者用完后再释放。另外,不能是局部变量。

程序会创建一个队列,然后创建1个发送任务、1个接收任务:

  • 创建的队列:长度为1,用来传输 "char *" 指针
  • 发送任务优先级为1,在字符数组中写好数据后,把它的地址写入队列
  • 接收任务优先级为2,读队列得到 "char *" 值,把它打印出来

这个程序故意设置接收任务的优先级更高,在它访问数组的过程中,接收任务无法执行、无法写这个数组。

结构体读写队列:

 字符串读写队列:

3. 队列集

本节源码:14_freertos_example_queue_set

从多个队列中得到数据,就使用队列集。

在实际工作中需要从多个队列中得到数据吗?是需要的。

如果你的鼠标既支持鼠标、键盘、触摸屏,任意一个队列有数据,都可以唤醒app去工作,这时候就需要用到队列集合。

让Task1、Task2往Queue1、Queue2中写数据,使用Task3,监测两个Queue。 

 main()函数

  1. 创建2个queue
  2. 创建queue set
  3. 把两个queue添加进queue set
  4. 创建3个任务

任务执行过程

  1. task3先运行,此时没有数据,先阻塞在这里,
  2. 然后task1、task2竞争cpu资源去运行,假设task1先运行,把 &i 先写入队列,在把对应的handle传到队列集(queue set)中,在从&i地址中读到数据,在打印出来,为0;
  3. 接着task2运行,是基于task1高风亮节,使用了该函数vTaskDelay(10);才有task2得以运行,task2也把 &i 先写入队列,在把对应的handle传到队列集(queue set)中,在从&i地址中读到数据,在打印出来,为-1;
  4. 此后打印的数据依次出来,为0 -1 1 -2 2 -3 3 ....运行结果如下图所示
static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;
static QueueHandle_t xQueueHandle1;
static QueueHandle_t xQueueHandle2;
static QueueSetHandle_t xQueueSet;

void Task1Function(void * param)
{
	int i = 0;
	while (1)
	{
		xQueueSend(xQueueHandle1, &i, portMAX_DELAY);
		i++;
		vTaskDelay(10);
	}
}

void Task2Function(void * param)
{
	int i = -1;
	while (1)
	{
		xQueueSend(xQueueHandle2, &i, portMAX_DELAY);
		i--;
		vTaskDelay(20);
	}
}

void Task3Function(void * param)
{
	QueueSetMemberHandle_t handle;
	int i;
	while (1)
	{
		/* 1. read queue set: which queue has data */
		handle = xQueueSelectFromSet(xQueueSet, portMAX_DELAY);

		/* 2. read queue */
		xQueueReceive(handle, &i, 0);

		/* 3. print */
		printf("get data : %d\r\n", i);
	}
}

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

int main( void )
{
	TaskHandle_t xHandleTask1;
		
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();

	printf("Hello, world!\r\n");

	/* 1. 创建2个queue */

	xQueueHandle1 = xQueueCreate(2, sizeof(int));
	if (xQueueHandle1 == NULL)
	{
		printf("can not create queue\r\n");
	}

	xQueueHandle2 = xQueueCreate(2, sizeof(int));
	if (xQueueHandle2 == NULL)
	{
		printf("can not create queue\r\n");
	}

	/* 2. 创建queue set */
	xQueueSet = xQueueCreateSet(3);

	/* 3. 把2个queue添加进queue set */
	xQueueAddToSet(xQueueHandle1, xQueueSet);
	xQueueAddToSet(xQueueHandle2, xQueueSet);

	/* 4. 创建3个任务 */
	xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
	xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
	xTaskCreate(Task3Function, "Task3", 100, NULL, 1, NULL);

	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

运行结果 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

五、freeRTOS_队列的使用 的相关文章

  • 心跳跟随的心形灯(STM32(HAL)+WS2812+MAX30102)

    文章目录 前言 介绍 系统框架 原项目地址 本项目开发开源地址 硬件PCB 软件功能 详细内容 硬件外壳制作 WS2812级联及控制 MAX30102血氧传感器 0 96OLED FreeRTOS 效果视频 总结 前言 在好几年前 我好像就
  • FreeRTOS例程4-串口DMA收发不定长数据

    FreeRTOS例程4 串口DMA收发不定长数据 知乎 zhihu com
  • STM32CubeMX+FreeRTOS学习笔记(一)

    嵌入式实时操作系统FreeRTOS 基本概述 在嵌入式领域当中 实时操作系统的应用越来越广泛了 目前嵌入式操作系统种类很多 例如 Clinux C OS II C OS III FreeRTOS RT Thread等等 这篇文章所记录的就是
  • FreeRTOS-内核控制函数

    FreeRTOS 内核控制函数 FreeRTOS中有一些内核函数 一般来说这些内核函数在应用层不会使用 但是内核控制函数是理解FreeRTOS中断的基础 接下来我们逐一分析这些内核函数 taskYIELD 该函数的作用是进行任务切换 这是一
  • FreeRTOS软件定时器创建、复位、开始和停止(备忘)

    目录 一 简介 1 1 开发环境 1 2 摘要 二 STM32CubeIDE配置 三 创建定时器 3 1 头文件声明 3 2 工程文件定义 3 3 创建定时器 3 4 开启 复位 和关闭定时器 四 定时器回调函数 一 简介 1 1 开发环境
  • FreeRTOS学习笔记 6 - 互斥量

    目录 1 创建 2 获取 3 释放 4 测试 FreeRTOS不支持调度方式的设置 所以下面2个宏定义可以随意设置值 define RTOS IPC FLAG FIFO 0x00 define RTOS IPC FLAG PRIO 0x01
  • FreeRTOS学习(八) 延时函数

    声明及感谢 跟随正点原子资料学习 在此作为学习的记录和总结 环境 keil stm32f103 FreeRTOS延时函数有两个 分别是 vTaskDelay vTaskDelayUntil 1 vTaskDelay 任务相对延时 函数原型
  • 【FreeRTOS开发问题】FreeRTOS内存溢出

    FreeRTOS内存溢出 如下图所示 FreeRTOS编译完成后可以看到 系统提示无法分配内存到堆 Objects Template axf Error L6406E No space in execution regions with A
  • FreeRTOS系列

    1 RTOS简介 RTOS全称为 Real Time Operation System 即实时操作系统 RTOS强调的是实时性 又分为硬实时和软实时 硬实时要求在规定的时间内必须完成操作 不允许超时 而软实时里对处理过程超时的要求则没有很严
  • ZYNQ中FreeRTOS中使用定时器

    使用普通的Timer中断方式时 Timer中断可以正常运行 但是UDP通信进程无法启动 其中TimerIntrHandler是中断服务程序 打印程序运行时间与从BRAM中读取的数据 void SetupInterruptSystem XSc
  • 基于HAL库的FREERTOS----------一.任务

    FreeROTS 就是一个免费的 RTOS 类系统 这里要注意 RTOS 不是指某一个确定的系统 而是指一类系统 比如 UCOS FreeRTOS RTX RT Thread 等这些都是 RTOS 类操作系统 FreeRTOS 是 RTOS
  • FreeRTOS临界区

    FreeRTOS临界区是指那些必须完整运行 不能被打断的代码段 比如有的外设的初始化需要严格的时序 初始化过程中不能被打断 FreeRTOS 在进入临界区代码的时候需要关闭中断 当处理完临界区代码以后再打开中断 FreeRTOS 系统本身就
  • STM32F103移植FreeRTOS必须搞明白的系列知识---2(FreeRTOS任务优先级)

    STM32F103移植FreeRTOS必须搞明白的系列知识 1 Cortex CM3中断优先级 STM32F103移植FreeRTOS必须搞明白的系列知识 2 FreeRTOS任务优先级 STM32F103移植FreeRTOS必须搞明白的系
  • FreeRTOS死机原因

    1 中断回调函数中没有使用中断级API xxFromISR 函数 xSemaphoreGiveFromISR uart busy HighterTask 正确 xSemaphoreGive uart busy 错误 2 比configMAX
  • 【FreeRTOS 事件】任务通知事件

    普通任务通知事件创建创建及运行 参阅安富莱电子demo define BIT 0 1 lt lt 0 define BIT 1 1 lt lt 1 static TaskHandle t xHandleTaskUserIF NULL sta
  • FreeRTOS笔记(二)

    FreeRTOS笔记 二 静态任务 文章目录 FreeRTOS笔记 二 静态任务 一 任务定义 二 任务创建 2 1 定义任务栈 2 2 定义任务函数 2 3 定义任务控制块 2 4 实现任务创建函数 三 实现就绪列表 3 1 定义就绪列表
  • FreeRTOS实时操作系统(三)任务挂起与恢复

    系列文章 FreeRTOS实时操作系统 一 RTOS的基本概念 FreeRTOS实时操作系统 二 任务创建与任务删除 HAL库 FreeRTOS实时操作系统 三 任务挂起与恢复 FreeRTOS实时操作系统 四 中断任务管理 FreeRTO
  • 使用 GCC 编译器的 ARM 内核的堆栈回溯(当存在 MSP 到 PSP 切换时)

    核心 ARM Cortex M4 编译器 GCC 5 3 0 ARM EABI 操作系统 免费 RTOS 我正在使用 gcc 库函数 Unwind Reason Code Unwind Backtrace Unwind Trace Fn v
  • 如何将 void* 转换为函数指针?

    我在 FreeRTOS 中使用 xTaskCreate 其第四个参数 void const 是传递给新线程调用的函数的参数 void connect to foo void const task params void on connect
  • GNU Arm Cortex m4 上的 C++ 异常处理程序与 freertos

    2016 年 12 月更新现在还有一个关于此行为的最小示例 https community nxp com message 862676 https community nxp com message 862676 我正在使用带有 free

随机推荐

  • cas 配置相关

    默认配置 span class token comment span span class token comment CAS Cloud Bus Configuration span span class token comment sp
  • Elasticsearch分词器

    内置分词器 中文分词器 这篇博客主要讲 xff1a 分词器概念 ES内置分词器 ES中文分词器 一 分词器概念 1 Analysis 和 Analyzer Analysis xff1a 文本分析是把全文本转换一系列单词 term token
  • java中的引用

    背景 最近在研究ThreadLocal中发现最终存储的ThreadLocalMap中的key为弱引用 xff0c 因此来分析下使用弱引用的原因 实验 引用链为 list 61 gt gt person1 因此在GC的时候 list还强引用三
  • charles

    Charles 的简介如何安装 Charles将 Charles 设置成系统代理Charles 主界面介绍过滤网络请求截取 iPhone 上的网络封包截取 Https 通讯信息模拟慢速网络修改网络请求内容给服务器做压力测试修改服务器返回内容
  • CAN通信

    CAN通信控制器通过两根线上的电位差来判断总线电平 xff0c 是ISO国际标准化的串行通信协议 总线电平分为显性电平和隐形电平 xff0c 二者必居其一 发送方通过总线上电平的变化将信息发送给接收方 CAN通讯是半双工的 xff0c 收发
  • maddpg 复现过程中遇到的问题

    最近在复现论文Multi Agent Actor Critic for Mixed Cooperative Competitive Environments https github com openai multiagent partic
  • 【解决】VSCode在windows下不能打开标准头文件

    鼠标放到标准头文件上 xff0c VSCode提示一下错误 xff1a include errors detected Please update your includePath IntelliSense features for thi
  • SPI通信方式总结

    SPI xff08 Serial Peripheral interface xff09 是一种同步串行传输规范 xff0c 也是单片机外设芯片串行外设扩展接口 xff0c 该接口是一种高速 xff0c 全双工 xff0c 同步的通信总线 x
  • 轮询机制的介绍

    轮询是一种CPU决策如何提供周边设备服务的方式 xff0c 又称 程控输入输出 xff08 Programmed I O xff09 是由CPU定时发出询问 xff0c 依序询问每一个周边设备是否需要其服务 xff0c 有即给予服务 xff
  • stm32面试题总结

    1 嵌入式系统中ROM RAM Register的概念和作用是什么 xff1f ROM是只读存储器 断电后能保证数据不会丢失 xff08 硬盘 xff09 RAM是随机存储器 断电后数据会丢失 xff08 内存 xff09 Register
  • 有刷电机,无刷电机和电调的总结

    有刷直流电机工作原理 xff1a 有刷直流电机的主要结构就是定子 43 转子 43 电刷 xff0c 通过旋转磁场获得转动力矩 xff0c 从而输出动能 电刷与换向器不断接触摩擦 xff0c 在转动中起到导电和换相作用 有刷直流电机采用机械
  • leetcode刷题(五)——找出数组中唯一出现的数

    给定一个只包含整数的有序数组 nums xff0c 每个元素都会出现两次 xff0c 唯有一个数只会出现一次 xff0c 请找出这个唯一的数字 你设计的解决方案必须满足 O log n 时间复杂度和 O 1 空间复杂度 示例 1 输入 nu
  • leetcode刷题(六)——快乐数

    编写一个算法来判断一个数 n 是不是快乐数 快乐数 定义为 xff1a 对于一个正整数 xff0c 每一次将该数替换为它每个位置上的数字的平方和 然后重复这个过程直到这个数变为 1 xff0c 也可能是 无限循环 但始终变不到 1 如果这个
  • leetcode刷题(七)——移动零

    给定一个数组 nums xff0c 编写一个函数将所有 0 移动到数组的末尾 xff0c 同时保持非零元素的相对顺序 请注意 xff0c 必须在不复制数组的情况下原地对数组进行操作 示例 1 输入 nums 61 0 1 0 3 12 输出
  • STM32 HAL库 串口接收不定长数据(帧头)

    写的比较垃圾 xff0c 将就着用 欢迎各位大佬指导 xff0c 我这里要用串口中断接收两种帧头的数据 xff0c 1 以0x0D 0x0A为帧头的数据 2 xff0c 以0x55 0xA5为帧头的数据 两数据包帧头不同 大小不同 其中定义
  • freeRTOS系列教程之【第一章】FreeRTOS概述与体验

    文章目录 教程目录1 1 FreeRTOS目录结构1 1 FreeRTOS目录结构1 2 核心文件1 3 移植时涉及的文件1 4 头文件相关 1 4 1 头文件目录1 4 2 头文件 1 5 内存管理1 6 Demo1 7 数据类型和编程规
  • 【RTOS的最通俗理解】行业大佬用一篇文章带你快速理解RTOS

    文章目录 单片机 RTOS 架构 1 RTOS的概念 1 1 用人来类比单片机程序和RTOS 1 1 1 我无法一心多用1 2 2 我可以一心多用 1 2 程序简单示例 2 架构的概念 2 1 用人来类比电子产品2 2 要深入理解RTOS就
  • 开源网络模拟器ns-3 架构与实践

  • 四、freeRTOS_同步互斥与通信概述

    目录 1 同步与互斥的概念 2 同步的例子 xff1a 有缺陷 3 互斥的例子 xff1a 有缺陷 4 通信的例子 xff1a 有缺陷 5 FreeRTOS的解决方案 对应程序 xff1a 12 freertos example sync
  • 五、freeRTOS_队列的使用

    目录 1 队列的理论讲解 1 1 常规操作 2 队列的常规使用 3 队列集 1 队列的理论讲解 1 1 常规操作 队列的简化操如入下图所示 xff0c 从此图可知 xff1a 队列可以包含若干个数据 xff1a 队列中有若干项 xff0c