UcOs-III 源码阅读: os_msg.c

lanlincmos / 2025-02-21 / 原文

//作用:用于处理消息的代码。 提供消息队列和针对任务的消息队列。 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