//作用:用于处理消息的代码。 提供消息队列和针对任务的消息队列。 os_msg.c为这两个服务提供通用代码
/*
*********************************************************************************************************
* uC/OS-III
* The Real-Time Kernel
*
* Copyright 2009-2022 Silicon Laboratories Inc. www.silabs.com
*
* SPDX-License-Identifier: APACHE-2.0
*
* This software is subject to an open source license and is distributed by
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 消息处理服务
*
* 文件 : os_msg.c
* 版本 : V3.08.02
*********************************************************************************************************
*/
#define MICRIUM_SOURCE
#include "os.h"
#ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
const CPU_CHAR *os_msg__c = "$Id: $";
#endif
#if (OS_MSG_EN > 0u)
/*
************************************************************************************************************************
* 初始化 'OS_MSG' 池
*
* 描述: 该函数由 OSInit() 调用,用于初始化 OS_MSG 的空闲列表。
*
* 参数: p_err 是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*
* OS_ERR_MSG_POOL_NULL_PTR
* OS_ERR_MSG_POOL_EMPTY
* OS_ERR_NONE
*
* 返回: 无
*
* 注意: 1) 该函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void OS_MsgPoolInit(OS_ERR *p_err)
{
OS_MSG *p_msg1;
OS_MSG *p_msg2;
OS_MSG_QTY i;
OS_MSG_QTY loops;
#if (OS_CFG_ARG_CHK_EN > 0u)
// 检查消息池基址是否为空
if (OSCfg_MsgPoolBasePtr == (OS_MSG *)0) {
*p_err = OS_ERR_MSG_POOL_NULL_PTR;
return;
}
// 检查消息池大小是否为零
if (OSCfg_MsgPoolSize == 0u) {
*p_err = OS_ERR_MSG_POOL_EMPTY;
return;
}
#endif
// 初始化消息池指针
p_msg1 = OSCfg_MsgPoolBasePtr;
p_msg2 = OSCfg_MsgPoolBasePtr;
p_msg2++;
loops = OSCfg_MsgPoolSize - 1u;
// 初始化空闲消息列表
for (i = 0u; i < loops; i++) {
p_msg1->NextPtr = p_msg2; // 设置下一个消息指针
p_msg1->MsgPtr = (void *)0; // 初始化消息指针
p_msg1->MsgSize = 0u; // 初始化消息大小
#if (OS_CFG_TS_EN > 0u)
p_msg1->MsgTS = 0u; // 初始化消息时间戳
#endif
p_msg1++;
p_msg2++;
}
// 初始化最后一个消息
p_msg1->NextPtr = (OS_MSG *)0;
p_msg1->MsgPtr = (void *)0;
p_msg1->MsgSize = 0u;
#if (OS_CFG_TS_EN > 0u)
p_msg1->MsgTS = 0u;
#endif
// 初始化消息池控制结构
OSMsgPool.NextPtr = OSCfg_MsgPoolBasePtr; // 设置第一个空闲消息
OSMsgPool.NbrFree = OSCfg_MsgPoolSize; // 设置空闲消息数量
OSMsgPool.NbrUsed = 0u; // 初始化已使用消息数量
#if (OS_CFG_DBG_EN > 0u)
OSMsgPool.NbrUsedMax = 0u; // 初始化最大已使用消息数量
#endif
*p_err = OS_ERR_NONE; // 设置错误代码为无错误
}
/*
************************************************************************************************************************
* 释放消息队列中的所有消息
*
* 描述: 该函数将消息队列中的所有消息返回到空闲列表。
*
* 参数: p_msg_q 是指向包含要释放的消息的 OS_MSG_Q 结构的指针。
* -------
*
* 返回: 返回到空闲列表的消息数量
*
* 注意: 1) 该函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
OS_MSG_QTY OS_MsgQFreeAll (OS_MSG_Q *p_msg_q)
{
OS_MSG *p_msg;
OS_MSG_QTY qty;
// 初始化要释放的消息计数
qty = p_msg_q->NbrEntries; /* 获取将要释放的消息数量 */
// 如果消息队列不为空,则继续释放消息
if (p_msg_q->NbrEntries > 0u) {
// 设置指向消息链末端的指针
p_msg = p_msg_q->InPtr; /* 指向消息链末端 */
// 将释放的消息链链接到现有的空闲消息池
p_msg->NextPtr = OSMsgPool.NextPtr;
OSMsgPool.NextPtr = p_msg_q->OutPtr; /* 指向消息链起始位置 */
// 更新消息池的使用统计信息
OSMsgPool.NbrUsed -= p_msg_q->NbrEntries; /* 更新空闲消息列表的统计信息 */
OSMsgPool.NbrFree += p_msg_q->NbrEntries;
// 重置消息队列的统计信息和指针
p_msg_q->NbrEntries = 0u; /* 清空消息队列 */
#if (OS_CFG_DBG_EN > 0u)
p_msg_q->NbrEntriesMax = 0u;
#endif
p_msg_q->InPtr = (OS_MSG *)0;
p_msg_q->OutPtr = (OS_MSG *)0;
}
// 返回被释放的消息数量
return (qty);
}
/*
************************************************************************************************************************
* 初始化消息队列
*
* 描述: 此函数用于初始化一个消息队列
*
* 参数: p_msg_q 是指向要初始化的消息队列的指针
* -------
*
* size 是消息队列可以拥有的最大条目数。
*
* 返回: 无
*
* 注意: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void OS_MsgQInit (OS_MSG_Q *p_msg_q,
OS_MSG_QTY size)
{
p_msg_q->NbrEntriesSize = size;
p_msg_q->NbrEntries = 0u;
#if (OS_CFG_DBG_EN > 0u)
p_msg_q->NbrEntriesMax = 0u;
#endif
p_msg_q->InPtr = (OS_MSG *)0;
p_msg_q->OutPtr = (OS_MSG *)0;
}
/*
************************************************************************************************************************
* 从消息队列中检索消息
*
* 描述: 此函数从消息队列中检索一条消息
*
* 参数: p_msg_q 是指向要从中提取消息的消息队列的指针
* -------
*
* p_msg_size 是指向存储消息大小(以字节为单位)的位置的指针
*
* p_ts 是指向存储时间戳的位置的指针
*
* p_err 是指向从本次调用返回的错误代码的指针。
*
* OS_ERR_Q_EMPTY 队列为空
* OS_ERR_NONE 没有错误
*
* 返回: 消息(一个指针)
*
* 注意: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void *OS_MsgQGet(OS_MSG_Q *p_msg_q, OS_MSG_SIZE *p_msg_size, CPU_TS *p_ts, OS_ERR *p_err)
{
OS_MSG *p_msg;
void *p_void;
#if (OS_CFG_TS_EN == 0u)
(void)p_ts; /* 防止编译器警告未使用 'ts' */
#endif
if (p_msg_q->NbrEntries == 0u) { /* 队列是否为空? */
*p_msg_size = 0u; /* 是 */
#if (OS_CFG_TS_EN > 0u)
if (p_ts != (CPU_TS *)0) {
*p_ts = 0u;
}
#endif
*p_err = OS_ERR_Q_EMPTY;
return ((void *)0);
}
p_msg = p_msg_q->OutPtr; /* 不是,获取队列中的下一条消息 */
p_void = p_msg->MsgPtr;
*p_msg_size = p_msg->MsgSize;
#if (OS_CFG_TS_EN > 0u)
if (p_ts != (CPU_TS *)0) {
*p_ts = p_msg->MsgTS;
}
#endif
p_msg_q->OutPtr = p_msg->NextPtr; /* 指向下一条要提取的消息 */
if (p_msg_q->OutPtr == (OS_MSG *)0) { /* 队列中还有其他消息吗? */
p_msg_q->InPtr = (OS_MSG *)0; /* 没有 */
p_msg_q->NbrEntries = 0u;
} else {
p_msg_q->NbrEntries--; /* 有,队列中的消息数量减少一个 */
}
p_msg->NextPtr = OSMsgPool.NextPtr; /* 将消息控制块返回到空闲列表 */
OSMsgPool.NextPtr = p_msg;
OSMsgPool.NbrFree++;
OSMsgPool.NbrUsed--;
*p_err = OS_ERR_NONE;
return (p_void);
}
/*
************************************************************************************************************************
* 将消息存入消息队列
*
* 描述: 此函数将一条消息放入消息队列中
*
* 参数: p_msg_q 是指向要发送消息的任务的 OS_TCB 的指针
* -------
*
* p_void 是指向要发送的消息的指针。
*
* msg_size 是消息的大小(以字节为单位)
*
* opt 指定消息是以 FIFO 还是 LIFO 顺序存入
*
* OS_OPT_POST_FIFO FIFO 顺序
* OS_OPT_POST_LIFO LIFO 顺序
*
* ts 是消息存入的时间戳
*
* p_err 是指向一个变量的指针,该变量将包含此函数返回的错误代码。
*
* OS_ERR_Q_MAX 如果队列已满
* OS_ERR_MSG_POOL_EMPTY 如果没有可用的 OS_MSG
* OS_ERR_NONE 消息已成功存入队列
*
* 返回: 无
*
* 注意: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void OS_MsgQPut(OS_MSG_Q *p_msg_q, void *p_void, OS_MSG_SIZE msg_size, OS_OPT opt, CPU_TS ts, OS_ERR *p_err)
{
OS_MSG *p_msg;
OS_MSG *p_msg_in;
#if (OS_CFG_TS_EN == 0u)
(void)ts; /* 防止编译器警告未使用 'ts' */
#endif
if (p_msg_q->NbrEntries >= p_msg_q->NbrEntriesSize) {
*p_err = OS_ERR_Q_MAX; /* 消息队列无法接受更多消息 */
return;
}
if (OSMsgPool.NbrFree == 0u) {
*p_err = OS_ERR_MSG_POOL_EMPTY; /* 没有可用的 OS_MSG */
return;
}
p_msg = OSMsgPool.NextPtr; /* 从空闲列表中移除消息控制块 */
OSMsgPool.NextPtr = p_msg->NextPtr;
OSMsgPool.NbrFree--;
OSMsgPool.NbrUsed++;
#if (OS_CFG_DBG_EN > 0u)
if (OSMsgPool.NbrUsedMax < OSMsgPool.NbrUsed) {
OSMsgPool.NbrUsedMax = OSMsgPool.NbrUsed;
}
#endif
if (p_msg_q->NbrEntries == 0u) { /* 这是队列中的第一条消息吗? */
p_msg_q->InPtr = p_msg; /* 是 */
p_msg_q->OutPtr = p_msg;
p_msg_q->NbrEntries = 1u;
p_msg->NextPtr = (OS_MSG *)0;
} else { /* 否 */
if ((opt & OS_OPT_POST_LIFO) == OS_OPT_POST_FIFO) { /* 是 FIFO 还是 LIFO? */
p_msg_in = p_msg_q->InPtr; /* FIFO,添加到头部 */
p_msg_in->NextPtr = p_msg;
p_msg_q->InPtr = p_msg;
p_msg->NextPtr = (OS_MSG *)0;
} else {
p_msg->NextPtr = p_msg_q->OutPtr; /* LIFO,添加到尾部 */
p_msg_q->OutPtr = p_msg;
}
p_msg_q->NbrEntries++;
}
#if (OS_CFG_DBG_EN > 0u)
if (p_msg_q->NbrEntriesMax < p_msg_q->NbrEntries) {
p_msg_q->NbrEntriesMax = p_msg_q->NbrEntries;
}
#endif
p_msg->MsgPtr = p_void; /* 将消息存入消息队列条目 */
p_msg->MsgSize = msg_size;
#if (OS_CFG_TS_EN > 0u)
p_msg->MsgTS = ts;
#endif
*p_err = OS_ERR_NONE;
}
#endif