UCOSII 消息队列、信号量集、软件定时器

2023-05-16

1.消息队列:

  • 作用:在任务之间传递多条信息。

  • 组成:事件控制块、消息队列、消息。

  • 消息队列数据结构
    在这里插入图片描述

  • 队列控制块的结构定义:

typedef struct os_q
{
	struct os_q *OSQPtr;	//指向下一个空的队列控制块
	void **OSQStart; 		//指向消息指针数组的起始地址
	void **OSQEnd;			//指向消息指针数组结束单元的下一个单元,它使得数组构成了一个循环的缓冲区
	void **OSQIn;			//指向插入一条消息的位置。当它移动到与OSQEnd相等时,被调整到指向数组的起始单元。
	void **OSQOut;			//指向被取出消息的位置。当它移动到与OSQEnd相等时,被调整到指向数组的起始单元。
	INT16U OSQSize;			//数组长度
	INT16U OSQEntries;		//已存放消息指针的元素数目
}OS_Q;

PS:
可移动的指针为OSQIn 和OSQOut,而指针OSQStart 和OSQEnd只是一个标志(常指针)。当可移动的指针OSQIn 和OSQOut移动到数组末尾时,即与OSQEnd相等时,可移动指针将会被调整到数组的起始位置OSQStart。从效果上看,由消息指针构成的数组就头尾衔接起来形成了一个循环消息队列。如下图

在这里插入图片描述

  • 消息队列相关函数
//(1)创建消息队列函数
// 首先需要定义一个指针数组,然后把各个消息数据缓冲区的首地址存入这个数组中。
OS_EVENT *OSQCreate(void **start,INT16U size);
// start:存放消息缓冲区指针数组的地址;size:该数组的大小;返回值:消息队列指针;

//(2)请求消息队列函数
// 目的:为了从消息队列中获取信息。
void *OSQPend(OS_EVENT *pevent,INT16U timeout,INT8U *err);
// pevent:所请求的消息队列的指针;timeout:任务等待时限;err:错误信息;

//(3)向消息队列发送消息函数
INT8U OSQPost(OS_EVENT *pevent,void *msg);
INT8U OSQPostFront(OS_EVENT *pevent,void *msg);
// OSQPost函数以FIFO(先进先出)的方式组织消息队列;
// OSQPostFront函数以LIFO (后进先出)的方式组织消息队列;
// pevent:消息队列的指针;msg:待发送消息的指针;

2.信号量集

  • 在实际应用中,任务常常需要与多个事件同步,即 要根据多个信号量组合作用的结果来决定任务的运行方式。UCOSII为了实现多个信号量组合的功能定义了一种特殊的数据结构 —— 信号量集。

  • 信号量集所能管理的信号量 都是一些二值信号,所有信号量集实质上是一种可以对多个输入的逻辑信号进行基本逻辑运算的组合逻辑。

在这里插入图片描述

  • UCOSII 使用标志组的结构OS_FLAG_GRP来描述信号量集
typedef struct
{
	INT8U OSFlagType;				//识别是否为信号量集的标志
	void *OSFlagWaitList;			//指向等待任务链表的指针
	OS_FLAGS OSFlagFlags;			//所有信号列表
}OS_FLAG_GRP;

PS:
信号量集用一个双向链表来组织等待任务,每一个等待任务都是该链表中的一个节点(Node)。

  • 等待任务链表节点OS_FLAG_NODE的结构如下:
typedef struct
{
	void *OSFlagNodeNext; 		//指向下一个节点的指针
	void *OSFlagNodePrev;		//指向前一个节点的指针
	void *OSFlagNodeTCB;		//指向对应任务控制块的指针
	void *OSFlagNodeFlagGrp;	//反向指向信号量集的指针
	OS_FLAGS OSFlagNodeFlags;	//信号过滤器
	INT8U OSFlagNodeWaitType;	//定义逻辑运算关系的数据
}OS_FLAG_NODE;

OSFlagNodeWaitType 可选值

常数信号有效状态等待任务的就绪条件
WAIT_CLR_ALL或WAIT_CLR_AND0信号全部有效(全0)
WAIT_CLR_ANY或WAIT_CLR_OR0信号有1个或1个以上有效(有0)
WAIT_SET_ALL或WAIT_SET_AND1信号全部有效(全1)
WAIT_SET_ANY或WAIT_SET_ALL1信号有1个或1个以上有效(有1)
  • OSFlagFlags、OSFlagNodeFlags、OSFlagNodeWaitType 的关系如下图:
    在这里插入图片描述
    PS:
    OSFlagFlags,通过发送信号量集的任务设置;OSFlagNodeFlags,由请求信号量集的任务设置,用于选择性的挑选OSFlagFlags中的部分(或全部)位作为有效信号;OSFlagNodeWaitType 也是由请求信号量集的任务设置,用于选择有效信号的组合方式。

    举例:假设请求信号量集的任务设置OSFlagNodeFlags的值为0x0F,设置OSFlagNodeWaitType 的值为 WAIT_SET_ANY,那么只要OSFlagFlags的低4位的任何一位为1,请求信号量集的任务将得到有效的请求,从而执行相关操作。如果低4位都为0,那么请求信号量集的任务将得到无效的请求。

  • 信号量集的相关函数

//(1)创建信号量集函数
OS_FLAG_GRP *OSFlagCreate(OS_FLAGS flags,INT8U *err);
// flags:信号量的初始值(即OSFlagFlags的值);err:错误信息;返回值:该信号量集的标志组的指针;

//(2)请求信号量集函数
OS_FLAGS OSFlagPend(OS_FLAG_GRP *pgrp,OS_FLAGS flags,INT8U wait_type,INT16U timeout,INT8U *err);
// pgrp:所请求的信号量集的指针;flags:滤波器(即OSFlagNodeFlags值);wait_type:逻辑运算类型(即OSFlagNodeWaitType 值)
// timeout:等待时限;err:错误信息;

//(3)向信号量集发送信号函数
OS_FLAGS OSFlagPost(OS_FLAG_GRP *pgrp,OS_FLAGS flags,INT8U opt,INT8U *err);
// pgrp:所请求的信号量集指针;flags:选择所要发送的信号;opt:信号有效选项;err:错误信息;

PS:
任务向信号量集发信号,就是对信号量集标志组中的信号进行置“1”或置“0”,至于对信号量集中的哪些信号进行操作,用参数flags指定,对指定的信号是置“1”还是置“0”,用参数opt来指定(opt = OS_FLAG_SET 为置“1”;opt = OS_FLAG_CLR为置“0”)。

3.软件定时器

  • 软件定时器由OSTimeTick提供时钟,但软件定时器的时钟还受OS_TMR_CFG_TICKS_PER_SEC设置的控制,也就是在UCOSII的时钟节拍上再做了一次“分频”。软件定时器的最快时钟节拍就等于UCOSII系统时钟节拍。
  • 软件定时器定义了一个单独的计数器OSTmrTime,用于软件定时器的计时。
  • UCOSII创建了一个高于应用程序中所有其他任务优先级的定时器管理任务OSTmr_Task,用于定时器的到时判断和处理。
  • UCOSII中软件定时器的实现方法:将定时器按定时时间分组,使得每次时钟节拍到来时,只对部分定时器进行比较操作,缩短了每次处理的时间。但这就需要动态地维护一个定时器组。定时器组的维护只是在每次定时器到时时才发生,而且定时器从组中移除和插入操作不需要排序。
  • UCOSII 软件定时器实现了3类链表的维护:
//(1)
OS_EXT OS_TMR	OSTmrTbl[OS_TMR_CFG_MAX];		//定时器控制块数组
// OS_TMR :定时器控制块,定时器控制块是软件定时器管理的基本单元,包含软件定时器名称、定时时间、在链表中的位置、
// 使用状态、使用方式,以及到时回调函数及其参数等基本信息。
//OSTmrTbl[OS_TMR_CFG_MAX] :以数组的形式静态分配定时器控制块所需的RAM空间,并存储所有已建立的定时器控制块,
// OS_TMR_CFG_MAX:最大软件定时器的个数。

//(2)
OS_EXT OS_TMR *OSTmrFreeList;	//空闲定时器控制块链表指针
// OSTmrFreeList:空闲的定时器控制块(OS_TMR)中,OSTmrnext 和 OSTmrPrev 两个指针分别指向空闲控制块的前一个和后一个,
// 组织了空闲控制块的双向链表,建立定时器时,从这个链表中搜索空闲定时器控制块。

//(3)
OS_EXT OS_TMR_WHEEL OSTmrWheelTbl[OS_TMR_CFG_WHEEL_SIZE]; //定时器轮
//OSTmrWheelTbl[OS_TMR_CFG_WHEEL_SIZE] :该数组的每个元素都是已开启定时器的一个分组,元素中记录了指向该分组中第一个
//定时器控制块的指针,以及定时器控制块的个数。

  • 软件定时器管理所需的数据结构示意图
    在这里插入图片描述
    PS:
    OS_TMR_CFG_WHEEL_SIZE 定义了 OSTmrWheelTbl 的大小,同时这个值也是定时器分组的依据。按照定时器到时值与OS_TMR_CFG_WHEEL_SIZE 相除的余数进行分组:不同余数的定时器放在不同的分组中,相同余数的定时器处在同一组中,由双向链表链接。这样 ,余数值 为 0~OS_TMR_CFG_WHEEL_SIZE -1 的不同定时器控制块,正好分别对应了数组元素OSTmrWheelTbl [0]~OSTmrWheelTbl [OS_TMR_CFG_WHEEL_SIZE -1]的不同分组。 每次时钟节拍到来时,时钟数OSTmrTime值加1,然后也进行求余操作,只有余数相同的那组定时器才有可能到时,所以只对该组定时器进行判断。

信号量唤醒定时器管理任务,计算出当前所要处理的分组后,程序遍历该分组中的所有控制块,将当前OSTmrTime值与定时器控制块中的到时值(OSTmrMatch)相比较。若相等(即到时),则调用该定时器到时回调函数;若不相等,则判断该组中下一个定时器控制块。如此操作直到该分组链表的结尾。

  • 软件定时器管理任务的流程:
    在这里插入图片描述
    PS:
    当运行完软件定时器的到时处理函数之后,需要进行该定时器控制块在链表中的移除和再插入操作。插入前需要重新计算定时器下次到时时所处的分组。计算公式如下:

    定时器下次到时的OSTmrTime值 (OSTmrMatch)= 定时器定时值 + 当前OSTmrTime值
    新分组 = 定时器下次到时的 OSTmrTime 值(OSTmrMatch) % OS_TMR_CFG_WHEEL_SIZE

  • UCOSII 软件定时器相关函数:

//(1)创建软件定时器函数
OS_TMR *OSTmrCreate(INT32U dly,INT32U period,INT8U opt,OS_TMR_CALLBACK callback,
					void *callback_arg,INT8U *pname,INT8U *perr);
// dly:用于初始化定时时间,对单次定时的软件定时器,这就是该定时器的定时时间,而对于周期定时的软件定时器来说,这是该定时器
// 第一次定时的时间,从第二次开始定时时间变为period。
// period:在周期定时,该值为软件定时器的周期溢出时间。
// opt:用于设置软件定时器工作模式。OS_TMR_OPT_ONE_SHOT :单次定时器,OS_TMR_OPT_PERIODIC :周期定时器
// callback:软件定时器的回调函数,当软件定时器的定时时间到达时,会调用该函数。
// callback_arg: 回调函数的参数
// pname:软件定时器的名字
// perr:错误信息

// 软件定时器的回调函数有固定的格式,必须按照这个格式编写:
void (*OS_TMR_CALLBACK)(void *ptmr,void *parg);
// 其中函数名可以随意设置,ptmr:软件定时器用来传递当前定时器的控制块指针。
// parg:回调函数的参数,可以根据自己需要设置,可以不用,但必须有这个参数。

//(2)开启软件定时器函数
BOOLEAN OSTmrStart(OS_TMR *ptmr,INT8U *perr);
// ptmr:要开启的软件定时器指针;perr:错误信息

//(3)停止软件定时器函数
OSTmrStop(OS_TMR *ptmr,INT8U opt,void *callback_arg,INT8U *perr);
// ptmr:要停止的软件定时器指针;
// opt:停止选项,可以设置的值如下:
//		OS_TMR_OPT_NONE,直接停止,不做任何其他处理;
//		OS_TMR_OPT_CALLBACK,停止,用初始化的参数执行一次回调函数;
//		OS_TMR_OPT_CALLBACK_ARG,停止,用新的参数执行一次回调函数;
// callback_arg:新的回调函数参数
// perr:错误信息
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UCOSII 消息队列、信号量集、软件定时器 的相关文章

随机推荐

  • 写论文的标题(title)和(abstract)的一些小感想

    最近老师在帮着改论文 xff0c 发现了自己的很多问题 在写论文的思路上海很不成熟 xff0c 根据最近的一些经验 xff0c 有一些小的建议可以分享给大家 xff0c 不一定对 xff0c 但是希望能帮助到需要小论文的盆友们 有什么说的不
  • Init container容器Volume文件复制脚本(共享存储)

    Deprecated 1 由于卷的挂载是直接在容器当前目录之上的 xff0c 即容器原本目录下文件被隐藏 xff0c 等umount才可见 xff0c 因此 xff0c 如果不希望发生把原本目录下的文件也影藏掉 xff0c 需要通过复制容器
  • Jeston nano+RealSense D455相机+ORB_SLAM3+ROS实时运行

    Jeston nano 43 RealSense D455相机 43 ORB SLAM3 43 ROS实时运行 安装各种ORB SLAM3依赖项编译编译正常版本编译ROS版本 外接相机实时运行ORB SLAM3修改订阅话题名运行效果 本人是
  • px4机架和混控文件读取

    在px4启动文件RCS中 xff0c 可以看到对autostart文件的读取 span class token macro property span class token directive hash span span class t
  • px4_add_module.cmake

    span class token macro property span class token directive hash span span class token expression span class token functi
  • 终于有人把Prometheus入门讲明白了 | 留言送书

    Prometheus既是一个时序数据库 xff0c 又是一个监控系统 xff0c 更是一套完备的监控生态解决方案 作为时序数据库 xff0c 在2020年2月的排名中 xff0c Prometheus已经跃居到第三名 xff0c 超越了老牌
  • Dockerfile文件全面详解

    Docker 可以通过读取 Dockerfile 中的指令自动构建镜像 Dockerfile 是一个文本文档 xff0c 其中包含了用户创建镜像的所有命令和说明 一 变量 变量用 variable name 或者 variable name
  • enum枚举类型作为成员变量

    Enum是枚举类型 定义形式一 xff1a Enum 枚举类型名 枚举常量1 xff08 61 0 xff09 xff0c 枚举常量2 xff08 61 0 xff09 xff0c 枚举变量1 xff08 xff0c 枚举变量2 xff0c
  • 嘉立创EDA持续进化,以创新引领板级EDA高质量发展

    从标准版到专业版 xff0c 嘉立创EDA正通过持续进化 xff0c 更好地满足中国企业应用需求 出品 常言道 作者 丁常彦 集成电路作为先进制造业的代表 数字经济的基石 xff0c 重要性日益凸显 近年来 xff0c 在一系列政策的持续推
  • 手把手教你设置路由器端口映射

    2012 06 11 18 33 45 来源 路饭网 大 中 小 浏览117 次 点击投稿 在前面其实 xff0c 我们也讲到过设置路由器端口映射方法 xff0c 今天之所以再次提及这个问题 xff0c 目的是想图文深化一下路由器端口映射的
  • 学习OpenCV(3)了解OpenCV的数据类型-1

    目录 openCV的数据类型 基础类型 固定向量类cv Vec lt gt 固定矩阵类 cv Matx lt gt cv Point lt gt 类 cv Scalar lt gt Size类 cv Rect类 cv RotatedRect
  • STM32 串口驱动,分层通信

    以前在使用串口的时候都是直接使用中断 xff0c 每收发一个字节都要进一次中断 xff0c 然后直接在中断进行封包 xff0c 现在做了一个简单的分层设计 xff0c 其实这个设计还是驱动设计 xff0c 后期将逻辑层划分再细致一点 xff
  • 【踩坑实录】Mission planner+Ardupilot飞控固件配置教程

    写在前面 飞控 xff1a 雷迅CUAV V5 43 固件 xff1a Arudupilot Arduplane Stable 地面站 xff1a Mission Planner 1 3 74 之前为飞控刷写了px4固件 xff0c 并采用
  • leetcode 专题:动态规划 python 版(持续更新中)

    递归 xff1a 自己调用自己 代码比较简洁 xff0c 但是浪费空间 xff0c 有许多重复计算 迭代 xff1a 利用已知的变量值 xff0c 根据递推公式不断得到新的值 xff0c 一直到解决问题为止 代码相对复杂一点 递归中一定有迭
  • FreeRTOS中数据结构链表思考的几个问题

    使用的资料为野火FreeRTOS教程 xff1a 其中有一个 将节点插入链表的尾部的一个函数如下 void vListInsertEnd List t const pxList ListItem t const pxNewListItem
  • Looking for pthread_create in pthreads - not found

    版权申明 转载请附上出处链接 Looking for pthread create in pthreads not found error log xff1a Looking span class token keyword for spa
  • Ubuntu系统备份、恢复至其他电脑

    以A电脑的系统向B电脑移植为例 xff08 Intel 64 NUC xff09 xff0c 第一步 xff08 将A电脑的系统打包 xff09 xff1a 进入A电脑根目录并获取权限 cd sudo su 将根目录所需文件打包为backu
  • git报错fatal: unable to access ‘https://github.com/.......‘: OpenSSL SSL_read: Connection was reset, e

    fatal unable to access https github com OpenSSL SSL read Connection was reset errno 10054 产生原因 一般是因为服务器的SSL证书没有经过第三方机构的签
  • 仿真器的知识

    目前 xff0c 在线仿真器 In Circuit Emulator xff0c ICE 在嵌入式系统开发中被越来越多的工程师所采用 尤其是在国外嵌入式开发公司中 xff0c ICE是一种必备的调试工具 xff0c 被大规模地应用 xff0
  • UCOSII 消息队列、信号量集、软件定时器

    1 消息队列 xff1a 作用 xff1a 在任务之间传递多条信息 组成 xff1a 事件控制块 消息队列 消息 消息队列数据结构 队列控制块的结构定义 xff1a typedef struct os q struct os q OSQPt