利用STM32CubeMX和Keil模拟器,3天入门FreeRTOS(5.1) —— 队列集

前言

(1)FreeRTOS是我一天过完的,由此回忆并且记录一下。个人认为,如果只是入门,利用STM32CubeMX是一个非常好的选择。学习完本系列课程之后,再去学习网上的一些其他课程也许会简单很多。
(2)本系列课程是使用的keil软件仿真平台,所以对于没有开发板的同学也可也进行学习。
(3)叠甲,再次强调,本系列课程仅仅用于入门。学习完之后建议还要再去寻找其他课程加深理解。
(4)本系列博客对应代码仓库:gitee仓库

前期准备

(1)将前一章信号量的示例代码复制一份

实战

使用STM32CubeMX创建队列集

(1)STM32CubeMX中似乎没有添加队列集的选项。可能是因为,队列集本身就是队列,只不过队列集存放的是句柄而非数据,所以STM32CubeMX将队列集和队列当成同一个对待了。
(2)虽然STM32CubeMX将队列集和队列当成同样的方法对待,但是我们尽量还是进行区分编程。

使用keil端创建队列集

(1)首先,我们需要进入FreeRTOS.h中将configUSE_QUEUE_SETS宏定义置1。

在这里插入图片描述

(2)按Ctrl+F搜索Private variables即可找到如下代码块,进行补充。

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
osThreadId_t keilTaskHandle; //任务句柄

static xSemaphoreHandle KeilBinarySemHandle;   // 二进制信号量
static xSemaphoreHandle KeilCountingSemHandle; // 计数型信号量
static QueueHandle_t    KeilQueueHandle;       // 队列
static QueueSetHandle_t KeilQueuesetHandle;    // 队列集
/* USER CODE END Variables */

(3)按Ctrl+F搜索BEGIN RTOS_QUEUES即可找到如下代码块,进行补充。

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
	// 创建一个队列
	KeilQueueHandle   = xQueueCreate(3, sizeof(uint16_t));
	
	//创建一个队列集,可以存放2个队列或信号量
	KeilQueuesetHandle = xQueueCreateSet( 2 );
	if(KeilQueuesetHandle != NULL)
	{
		printf("xQueueCreateSet succeeded!\r\n");
	}
	
	//将队列存入队列集中
	xQueueAddToSet(KeilQueueHandle,KeilQueuesetHandle);
	//将信号量存入队列集中
	xQueueAddToSet(KeilBinarySemHandle,KeilQueuesetHandle);
  /* USER CODE END RTOS_QUEUES */

应用程序

(1)按下K0向队列中写入数据,按下K1释放信号量。如果检测到队列集中数据有余量,那么就打印是队列还是信号量。
(2)需要注意一个点,StartKeilTask()任务中的时候,存在printf()线程安全问题,所以我加了一个延时5ms,目的是为了StartKeilTask()任务中的数据能够打印出来。

/* USER CODE BEGIN Header_StartCubemxTask */
/**
* @brief Function implementing the CubemxTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartCubemxTask */
#define Test_Binary   0 //如果为1测试二进制信号量,否则是测试计数型信号量
void StartCubemxTask(void *argument)
{
  /* USER CODE BEGIN StartCubemxTask */
  /* Infinite loop */
	BaseType_t status;
	uint16_t Buf = 0;
  for(;;)
  {
		// 按下K0队列写数据
		if(HAL_GPIO_ReadPin(Key_0_GPIO_Port,Key_0_Pin) == GPIO_PIN_SET)
		{
			Buf++;
			status = xQueueSendToFront(KeilQueueHandle, &Buf, 0);
			if (status == pdTRUE)
			{
				printf("xQueueSendToFront writes data successfully : %d\r\n", Buf);
			}
			else
			{
				printf("xQueueSendToFront failed to write data\r\n");
			}
			// 关于按键检测在讲解完FreeRTOS基础知识后会进行一次专题讲解
			while(HAL_GPIO_ReadPin(Key_0_GPIO_Port,Key_0_Pin) == GPIO_PIN_SET);
		}
		if(HAL_GPIO_ReadPin(Key_1_GPIO_Port,Key_1_Pin) == GPIO_PIN_SET)
		{
			status = xSemaphoreGive(KeilBinarySemHandle);
			if(status == pdTRUE)
			{
				printf("xSemaphoreGive succeed\r\n");
			}
			else
			{
				printf("xSemaphoreGive failed\r\n");
			}
			// 关于按键检测在讲解完FreeRTOS基础知识后会进行一次专题讲解
			while(HAL_GPIO_ReadPin(Key_1_GPIO_Port,Key_1_Pin) == GPIO_PIN_SET);
		}
  }
  /* USER CODE END StartCubemxTask */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
int fputc(int ch, FILE *f)
{
	unsigned char temp[1]={ch};
	HAL_UART_Transmit(&huart1,temp,1,0xffff);
	return ch;
}
void StartKeilTask(void *argument)
{
	char *KeilTaskPrintf = (char *)argument;
	QueueSetMemberHandle_t QueueSetHandle;
	uint16_t Buf = 0;
	while(1)
	{
		printf(KeilTaskPrintf);
		// 等待队列集中有数据
		QueueSetHandle = xQueueSelectFromSet( KeilQueuesetHandle,portMAX_DELAY);
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
		// 这个延时用于解决printf互斥问题,注意,这里只是因为任务简单才这么做。实际项目应考虑printf线程安全
		vTaskDelay(5/portTICK_PERIOD_MS);
		printf("xQueueSelectFromSet finish\r\n");
		if(QueueSetHandle == KeilQueueHandle)
		{
			xQueueReceive( QueueSetHandle,&Buf,portMAX_DELAY);
			printf("xQueueReceive was successfully :%d\r\n", Buf);		
		}
		else if(QueueSetHandle == KeilBinarySemHandle)
		{
			xSemaphoreTake( QueueSetHandle, portMAX_DELAY );
			printf("xSemaphoreTake was successful\r\n");
		}
	}
}
/* USER CODE END Application */

仿真结果

(1)

在这里插入图片描述

理论

(1)学习了前面的队列知识,我们会发现一个问题,就是队列只能存放同一种类型的数据,那如果我们需要在任务之间传输不同类型的消息,应该如何处理呢?例如我们的遥控车,可能是按键式的遥控器(传输的是整型数据),也可能会兼容遥感式的遥控器(传输的是浮点数据)。这两种遥控器都能够控制我们的小车,那么应当如何进行数据传输呢?此时,就需要利用到本讲的队列集了。
(2)队列集的本质也是队列,只不过里面存放的是队列句柄,而不是数据。
(3)如果感兴趣的话,我们可以看看队列集的函数实现,其实本质上就是调用的队列操作函数。因此,如果为了节约空间,可以把队列集的宏定义关闭,这样能够节约一点点code段。之后如果需要使用队列集的时候,直接使用队列的操作函数。不过个人不建议,因为这样会导致队列和队列集搞混,开发大型项目的时候尽量区分清楚好一点。

QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength )
{
QueueSetHandle_t pxQueue;

	pxQueue = xQueueGenericCreate( uxEventQueueLength, ( UBaseType_t ) sizeof( Queue_t * ), queueQUEUE_TYPE_SET );

	return pxQueue;
}

API函数介绍

创建队列集

(1)创建队列集

/**
 * @brief  创建队列集
 *
 * @param  uxEventQueueLength 队列集长度,最多能存放多少个数据(队列或信号量句柄)
 *
 * @return  如果成功创建队列集,则返回所创建队列集的句柄,否则返回 NULL
 */
QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength );

添加队列或信号量到队列集

(1)往队列集中添加队列或信号量。
(2)注意:队列被添加到队列集之前,队列中不能有效的消息。

/**
 * @brief  把队列或信号量加入队列集
 *
 * @param  xQueueOrSemaphore  队列或信号量的句柄
 *        -xQueueSet          队列集句柄
 *
 * @return  如果队列或信号量成功添加到队列集,那么返回 pdPASS,否则 pdFAIL 
 */
BaseType_t xQueueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore,QueueSetHandle_t xQueueSet);

从队列集中删除队列或信号量

(1)仅当队列或信号量为空时,才能从队列集中删除队列或信号量 。

/**
 * @brief  从队列集中删除队列或信号量
 *
 * @param  xQueueOrSemaphore  队列或信号量的句柄
 *        -xQueueSet          队列集句柄
 *
 * @return  如果队列或信号量已成功从队列集中删除, 则返回 pdPASS。 如果队列不在队列集中,或者 队列(或信号量)不为空,则返回 pdFAIL。
 */
BaseType_t xQueueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore,QueueSetHandle_t xQueueSet);

获取队列集中有效信息的队列

(1)在任务中获取队列集中有效信息的队列。

/**
 * @brief  在任务中获取队列集中有效信息的队列
 *
 * @param  xQueueSet     队列集
 *        -xTicksToWait  阻塞超时时间,表示立即返回,portMAX_DELAY表示无限等待
 *
 * @return  返回队列集中包含数据的队列的句柄或队列集中可用信号量的句柄,如果在指定的阻塞时间到期之前不存在这样的队列或信号量,则返回NULL
 */
QueueSetMemberHandle_t xQueueSelectFromSet(QueueSetHandle_t xQueueSet,const TickType_t xTicksToWait);

获取队列集中有效信息的队列(中断)

(1)在中断中获取队列集中有效信息的队列。

/**
 * @brief  在中断中获取队列集中有效信息的队列
 *
 * @param  xQueueSet 正在查询的队列集
 *
 * @return  返回队列集中包含数据的队列的句柄或队列集中可用信号量的句柄,如果在指定的阻塞时间到期之前不存在这样的队列或信号量,则返回NULL
 */
 QueueSetMemberHandle_t xQueueSelectFromSetFromISR(QueueSetHandle_t xQueueSet);

参考

(1)FreeRTOS官方文档:队列集
(2)B站:【正点原子】手把手教你学FreeRTOS实时系统 第47讲
(3)百问网:韦东山freeRTOS快速入门 08-3_队列集

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/351421.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通过css隐藏popover的效果:即hover显示或隐藏另一个元素

场景一&#xff1a;隐藏旁边的兄弟元素 在原生的微信小程序上实现下图hover后出现提示的效果&#xff0c;如果是PC端就可以直接使用el-popover&#xff0c;但是小程序&#xff0c;我没有看到适合的组件。 样式代码<van-field value"{{ username }}" clearable pl…

数据结构与算法——队列

概述 计算机科学中&#xff0c;queue 是以顺序的方式维护的一组数据集合&#xff0c;在一端添加数据&#xff0c;从另一端移除数据。添加的一端称为尾&#xff0c;移除的一端称为头。 功能 插入offer(value : E) : boolean  取值并移除poll() : E  取值peek() : E  判断…

RCC——使用HSE/HSI配置时钟

RCC 文章目录 前言一、背景二、 2.1 2.2 总结 前言 前期疑问&#xff1a;1、RCC是什么意思。 2、最终配好的72M是系统时钟吗&#xff1f; 3、一共有哪些时钟 本文目标&#xff1a;将PLL时钟配置成72M 疑问解答&#xff1a;最终配好的时钟是PLL时钟。可以看一下时钟图就知道…

面向对象编程(进阶)(上)

文章目录 一. 关键字&#xff1a;this1.1 this是什么&#xff1f;1.2 什么时候使用this1.2.1 实例方法或构造器中使用当前对象的成员1.2.2 同一个类中构造器互相调用 1.3 练习 二. 面向对象特征二&#xff1a;继承(Inheritance)2.1 继承的概述2.1.1 生活中的继承2.1.2 Java中的…

LeetCode刷题---二叉树的最大深度

解题思路: 二叉树的最大深度是从根节点到最远叶子节点的最长路径上的节点数。 对于任意一个节点&#xff0c;其深度为其左子树深度和右子树深度的最大值加1。 最大高度是从根节点到最远叶子节点的最长路径的长度。 使用先序遍历的方法来找出二叉树的最大深度&#xff0c;即先访…

linuxshell日常脚本命令之if判断

shell脚本if中判断大于、小于、等于、不等于的符号 脚本有问题&#xff0c;有没有哪位大佬能帮忙检查一下&#xff1f; #!/bin/bash#run_num$(squeue | grep shifting | wc -l) run_numsqueue | grep shifting | wc -l #run_num$(squeue | grep shifting | wc -l 2>&1…

Qt环境搭建及基础

目录 Qt背景及环境搭建 ​编辑 基础语法 数据类型 内联函数 inline Lambda表达式 通过函数调用中加lambda匿名函数 参数捕获 Lambda和内联函数区别​编辑 函数指针 Lambda匿名函数小案例 通过结构体初始化&#xff0c;和指针初始化结构体 c类的引入 &#xff1a;&am…

python黑马模块

1、使用内置模块 # import通过.使用模块内部的全部功能 """ import time print("ff") time. sleep(5) print("as")# 使用from 导入某个功能 from time import sleep print("ff") sleep(5) print("as")# 使用 * 导入全部…

2024年,我与CSDN的邂逅之旅:一段燃烧激情、成就梦想的博客专家之路

文章目录 初入CSDN知识分享的本质什么有用学什么 成为博客专家的经历成为博客专家有什么好处实际好处隐形好处 今天获得了CSDN认证的博客专家&#xff0c;感觉很开心~分享一下这个始末。分享一下我和CSDN的5年邂逅。 初入CSDN 进入CSDN已经五年了&#xff0c;大一开始写博客&a…

Azure Private endpoint DNS 记录是如何解析的

Private endpoint 从本质上来说是Azure 服务在Azure 虚拟网络中安插的一张带私有地址的网卡。 举例来说如果Storage account在没有绑定private endpoint之前&#xff0c;查询Storage account的DNS记录会是如下情况&#xff1a; Seq Name …

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Swiper容器组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Swiper容器组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Swiper容器组件 滑块视图容器&#xff0c;提供子组件滑动轮播显示的能力。…

浪花 - 响应拦截器(强制登录)

1. 配置响应拦截器 import axios from axios;const myAxios axios.create({baseURL: http://localhost:8080/api/, });myAxios.defaults.withCredentials true;// 请求拦截器 myAxios.interceptors.request.use(function (config) {// Do something before request is sentc…

windows上使用anconda安装tensorrt环境

windows上使用anconda安装tensorrt环境 1 安装tensorrt1.1 下载最新的稳定的tensorrt 8.6.1(tensorrt对应的cuda、cudnn等版本是参考链接4)1.2 将tensorrt添加到环境变量1.3 安装tensorrt依赖1.4 安装Pycuda1.5 安装pytorch 2 测试2.1 测试TensorRT 样例(这个测试主要来源于参考…

03-Nacos-服务注册基于spring cloud实现

本项目基于spring boot 多模块 注意spring -boot、spring-cloud、spring-cloud-alibaba的版本兼容性 1.1、父级pom依赖 <properties><spring-boot.version>2.7.18</spring-boot.version><spring.cloud.version>2021.0.1</spring.cloud.version&g…

python函数式编程

函数式编程是一种编程范式&#xff0c;它强调使用纯函数、无副作用和不可变性。在Python中&#xff0c;可以使用高阶函数&#xff08;接受其他函数作为参数的函数&#xff09;和lambda表达式来实现函数式编程。 Python函数式编程主要包括以下内容&#xff1a; 头等函数&#…

RustDesk私有化部署,自建远程桌面搭建教程

以linux操作系统为例&#xff1a; 解压安装 # 使用wget进行下载1.1.8-2版本&#xff08;最新版本可以看上述发布地址&#xff09; wget https://github.com/rustdesk/rustdesk-server/releases/download/1.1.8-2/rustdesk-server-linux-amd64.zip # 使用unzip解压 unzip rust…

UBUNTU中NGINX的负载均衡和环境搭建

1.准备三台ubuntu版本的虚拟机 2.开始安装&#xff0c;下载&#xff0c;解压&#xff0c;以及编译nginx所需的环境依赖 这里需要注意我们创建了一个新的目录 /home/nginx,所以在编译中记得更改 然后再编译过程中我们会发现提示无法编译&#xff0c;原因是缺少c语言的插件&…

林浩然的哲学冒险乐园:尼采与超人哲学的诙谐解读与深度探索

林浩然的哲学冒险乐园&#xff1a;尼采与超人哲学的诙谐解读与深度探索 Lin Haoran’s Philosophical Adventureland: A Whimsical Exploration of Nietzsche and the Philosophy of the Superman 在一场思维的盛宴中&#xff0c;林浩然同学勇敢地踏入了尼采哲学的探险乐园&…

WPS复制时不能对多重选定区域使用此命令问题

今天在进行两个表格复制过程中频繁出现下面这个提示&#xff0c;就很烦&#xff0c;且不知道是什么原因。 后来在操作过程中发现了规律&#xff0c;所以记录一下。 问题复现&#xff1a; 1、这里鼠标随机点了一个方格 2、与此同时&#xff0c;我按着ctrl键选中第一列&…

蓝桥杯省赛无忧 编程13 肖恩的投球游戏

#include <iostream> #include <vector> using namespace std; int main() {int n, q;cin >> n >> q;vector<int> a(n 1);vector<int> diff(n 2, 0); // 初始化差分数组// 读取初始球数&#xff0c;构建差分数组for (int i 1; i < …