UcOs-III 源码阅读: os_q.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_q.c
* 版本    : V3.08.02
*********************************************************************************************************
*/

#define  MICRIUM_SOURCE
#include "os.h"

#ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
const  CPU_CHAR  *os_q__c = "$Id: $";
#endif


#if (OS_CFG_Q_EN > 0u)
/*
************************************************************************************************************************
*                                               创建消息队列
*
* 描述: 此函数由您的应用程序调用以创建一个消息队列。消息队列必须在使用前创建。
*
* 参数  : p_q         指向消息队列的指针
*
*        p_name      是一个指向 ASCII 字符串的指针,用于命名消息队列
*
*        max_qty     表示消息队列的最大大小(必须是非零值)。请注意,它也不能超过可用的 OS_MSG 数量的最大值。
*
*        p_err       是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*
*                              OS_ERR_NONE                    调用成功
*                              OS_ERR_CREATE_ISR              不能从 ISR 中创建
*                              OS_ERR_ILLEGAL_CREATE_RUN_TIME 如果在调用 OSSafetyCriticalStart() 之后尝试创建队列
*                              OS_ERR_OBJ_PTR_NULL            如果传递了 'p_q' 的空指针
*                              OS_ERR_Q_SIZE                  如果指定的大小为 0
*                              OS_ERR_OBJ_CREATED             如果消息队列已创建
*
* 返回: 无
*
* 注意: 无
************************************************************************************************************************
*/

/**
 * @brief 创建一个消息队列
 *
 * @param p_q 指向消息队列结构的指针
 * @param p_name 队列的名称
 * @param max_qty 队列的最大消息数量
 * @param p_err 指向错误代码的指针
 */
void OSQCreate(OS_Q *p_q, CPU_CHAR *p_name, OS_MSG_QTY max_qty, OS_ERR *p_err)
{
    CPU_SR_ALLOC(); // 分配CPU状态寄存器变量

    // 安全关键系统检查:确保错误参数不为空
#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

    // 安全关键系统检查:禁止在运行时创建
#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
        *p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
        return;
    }
#endif

    // 检查是否在中断服务程序中调用
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
    if (OSIntNestingCtr > 0u) {
        *p_err = OS_ERR_CREATE_ISR;
        return;
    }
#endif

    // 参数有效性检查
#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_q == (OS_Q *)0) {
        *p_err = OS_ERR_OBJ_PTR_NULL;
        return;
    }
    if (max_qty == 0u) {
        *p_err = OS_ERR_Q_SIZE;
        return;
    }
#endif

    CPU_CRITICAL_ENTER(); // 进入临界区

#if (OS_OBJ_TYPE_REQ > 0u)
#if (OS_CFG_OBJ_CREATED_CHK_EN > 0u)
    if (p_q->Type == OS_OBJ_TYPE_Q) {
        CPU_CRITICAL_EXIT();
        *p_err = OS_ERR_OBJ_CREATED;
        return;
    }
#endif
    p_q->Type = OS_OBJ_TYPE_Q; // 标记数据结构为消息队列
#endif

#if (OS_CFG_DBG_EN > 0u)
    p_q->NamePtr = p_name; // 设置队列名称
#else
    (void)p_name; // 忽略名称参数
#endif

    OS_MsgQInit(&p_q->MsgQ, max_qty); // 初始化队列
    OS_PendListInit(&p_q->PendList); // 初始化等待列表

#if (OS_CFG_DBG_EN > 0u)
    OS_QDbgListAdd(p_q); // 将队列添加到调试列表
    OSQQty++; // 队列数量加一
#endif

    OS_TRACE_Q_CREATE(p_q, p_name); // 记录队列创建事件
    CPU_CRITICAL_EXIT(); // 退出临界区
    *p_err = OS_ERR_NONE; // 设置错误代码为无错误
}


/*
************************************************************************************************************************
*                                               删除消息队列
*
* 描述: 此函数删除一个消息队列,并准备好所有在该队列上等待的任务。
*
* 参数  : p_q       是指向要删除的消息队列的指针
*
*              opt       确定删除选项,如下所示:
*
*                            OS_OPT_DEL_NO_PEND          仅当没有任务等待时删除队列
*                            OS_OPT_DEL_ALWAYS           即使有任务在等待,也删除队列。
*                                                        在这种情况下,所有等待的任务将被准备好。
*
*              p_err     是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*
*                            OS_ERR_NONE                    调用成功且队列已删除
*                            OS_ERR_DEL_ISR                 如果尝试从ISR中删除队列
*                            OS_ERR_ILLEGAL_DEL_RUN_TIME    如果在调用OSStart()之后尝试删除消息队列
*                            OS_ERR_OBJ_PTR_NULL            如果传递的'p_q'指针为NULL
*                            OS_ERR_OBJ_TYPE                如果消息队列未创建
*                            OS_ERR_OPT_INVALID             指定了无效的选项
*                            OS_ERR_OS_NOT_RUNNING          如果uC/OS-III尚未运行
*                            OS_ERR_TASK_WAITING            一个或多个任务正在等待队列
*
* 返回值   : == 0          如果没有任务在等待队列,或者发生错误。
*              >  0          如果一个或多个等待队列的任务现在已被准备好并通知。
*
* 注意     : 1) 使用此函数必须小心。通常期望队列存在的任务必须检查OSQPend()的返回代码。
*
*              2) 因为所有在队列上等待的任务都将被准备好,所以在应用程序中使用队列进行互斥时必须小心,
*                 因为资源将不再受队列保护。
************************************************************************************************************************
*/

#if (OS_CFG_Q_DEL_EN > 0u)
OS_OBJ_QTY OSQDel(OS_Q *p_q, OS_OPT opt, OS_ERR *p_err)
{
    OS_OBJ_QTY     nbr_tasks;
    OS_PEND_LIST  *p_pend_list;
    OS_TCB        *p_tcb;
    CPU_TS         ts;
    CPU_SR_ALLOC();

    // 安全关键系统检查:确保错误参数不为空
#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return (0u);
    }
#endif

    // 记录队列删除进入事件
    OS_TRACE_Q_DEL_ENTER(p_q, opt);

    // 安全关键系统检查:禁止在运行时删除
#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
        OS_TRACE_Q_DEL_EXIT(OS_ERR_ILLEGAL_DEL_RUN_TIME);
        *p_err = OS_ERR_ILLEGAL_DEL_RUN_TIME;
        return (0u);
    }
#endif

    // 检查是否在中断服务程序中调用
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
    if (OSIntNestingCtr > 0u) {                                 /* 不能从ISR中删除消息队列 */
        OS_TRACE_Q_DEL_EXIT(OS_ERR_DEL_ISR);
        *p_err = OS_ERR_DEL_ISR;
        return (0u);
    }
#endif

    // 检查内核是否正在运行
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
    if (OSRunning != OS_STATE_OS_RUNNING) {                     /* 内核是否正在运行? */
        OS_TRACE_Q_DEL_EXIT(OS_ERR_OS_NOT_RUNNING);
        *p_err = OS_ERR_OS_NOT_RUNNING;
        return (0u);
    }
#endif

    // 参数有效性检查
#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_q == (OS_Q *)0) {                                     /* 验证 'p_q' */
        OS_TRACE_Q_DEL_EXIT(OS_ERR_OBJ_PTR_NULL);
        *p_err =  OS_ERR_OBJ_PTR_NULL;
        return (0u);
    }
#endif

    // 对象类型检查
#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
    if (p_q->Type != OS_OBJ_TYPE_Q) {                           /* 确保消息队列已创建 */
        OS_TRACE_Q_DEL_EXIT(OS_ERR_OBJ_TYPE);
        *p_err = OS_ERR_OBJ_TYPE;
        return (0u);
    }
#endif

    CPU_CRITICAL_ENTER(); // 进入临界区
    p_pend_list = &p_q->PendList;
    nbr_tasks   = 0u;

    switch (opt) {
        case OS_OPT_DEL_NO_PEND:                                /* 仅当没有任务等待时删除消息队列 */
            if (p_pend_list->HeadPtr == (OS_TCB *)0) {
#if (OS_CFG_DBG_EN > 0u)
                OS_QDbgListRemove(p_q);
                OSQQty--;
#endif
                OS_TRACE_Q_DEL(p_q);
                OS_QClr(p_q);
                CPU_CRITICAL_EXIT();
                *p_err = OS_ERR_NONE;
            } else {
                CPU_CRITICAL_EXIT();
                *p_err = OS_ERR_TASK_WAITING;
            }
            break;

        case OS_OPT_DEL_ALWAYS:                                 /* 始终删除消息队列 */
#if (OS_CFG_TS_EN > 0u)
            ts = OS_TS_GET();                                  /* 获取本地时间戳,以便所有任务获得相同的时间 */
#else
            ts = 0u;
#endif
            while (p_pend_list->HeadPtr != (OS_TCB *)0) {      /* 从等待列表中移除所有任务 */
                p_tcb = p_pend_list->HeadPtr;
                OS_PendAbort(p_tcb,
                             ts,
                             OS_STATUS_PEND_DEL);
                nbr_tasks++;
            }
#if (OS_CFG_DBG_EN > 0u)
            OS_QDbgListRemove(p_q);
            OSQQty--;
#endif
            OS_TRACE_Q_DEL(p_q);
            OS_QClr(p_q);
            CPU_CRITICAL_EXIT();
            OSSched();                                         /* 查找最高优先级的可运行任务 */
            *p_err = OS_ERR_NONE;
            break;

        default:
            CPU_CRITICAL_EXIT();
            *p_err = OS_ERR_OPT_INVALID;
            break;
    }

    OS_TRACE_Q_DEL_EXIT(*p_err);
    return (nbr_tasks);
}
#endif


/*
************************************************************************************************************************
*                                                     清空队列
*
* 描述: 此函数用于清空消息队列的内容。
*
* 参数   : p_q        是指向要清空的消息队列的指针
*
*               p_err      是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*                              - OS_ERR_NONE: 成功
*                              - OS_ERR_FLUSH_ISR: 如果从ISR中调用了此函数
*                              - OS_ERR_OBJ_PTR_NULL: 如果传递的'p_q'指针为NULL
*                              - OS_ERR_OBJ_TYPE: 如果消息队列未创建
*                              - OS_ERR_OS_NOT_RUNNING: 如果uC/OS-III尚未运行
*
* 返回值   : == 0       如果没有条目被释放,或者发生错误。
*               >  0       被释放的条目数量。
*
* 注意     : 1) 使用此函数时应非常小心,因为在清空队列时,你会失去队列条目所指向的引用,从而可能导致“内存泄漏”。换句话说,队列条目引用的数据应该被释放(即,解除分配)。
************************************************************************************************************************
*/

#if (OS_CFG_Q_FLUSH_EN > 0u)
// 函数OSQFlush用于清空一个消息队列中的所有消息
// 参数p_q指向要清空的消息队列,p_err用于返回错误代码
// 返回值为清空的消息数量
OS_MSG_QTY  OSQFlush (OS_Q    *p_q,
                      OS_ERR  *p_err)
{
    OS_MSG_QTY  entries;
    CPU_SR_ALLOC(); // 分配CPU状态寄存器,用于临界段处理



    // 安全关键选项:检查错误参数是否为空
    #ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return (0u);
    }
    #endif

    // 检查是否在中断服务例程中调用:不允许在ISR中清空消息队列
    #if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
    if (OSIntNestingCtr > 0u) {
       *p_err = OS_ERR_FLUSH_ISR;
        return (0u);
    }
    #endif

    // 检查内核是否正在运行
    #if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
    if (OSRunning != OS_STATE_OS_RUNNING) {
       *p_err = OS_ERR_OS_NOT_RUNNING;
        return (0u);
    }
    #endif

    // 参数检查:确保消息队列指针不为空
    #if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_q == (OS_Q *)0) {
       *p_err = OS_ERR_OBJ_PTR_NULL;
        return (0u);
    }
    #endif

    // 检查消息队列类型是否正确
    #if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
    if (p_q->Type != OS_OBJ_TYPE_Q) {
       *p_err = OS_ERR_OBJ_TYPE;
        return (0u);
    }
    #endif

    // 临界段开始:保护消息队列清空操作
    CPU_CRITICAL_ENTER();
    entries = OS_MsgQFreeAll(&p_q->MsgQ);                       // 将所有消息返回到OS_MSG池
    CPU_CRITICAL_EXIT();                                        // 临界段结束
   *p_err   = OS_ERR_NONE;
    return (entries);
}
#endif


/*
************************************************************************************************************************
*                                            在消息队列上等待消息
*
* 描述: 此函数等待消息队列中的消息。
*
* 参数: p_q           是指向消息队列的指针
*
*       timeout       是一个可选的超时时间(以时钟滴答为单位)。如果非零,任务将等待消息到达队列,最长等待时间为该参数指定的时间。如果指定为0,
*                     则任务将在指定队列上无限期等待,直到消息到达。
*
*       opt           确定用户是否希望在队列为空时阻塞:
*
*                                OS_OPT_PEND_BLOCKING
*                                OS_OPT_PEND_NON_BLOCKING
*
*       p_msg_size    是指向一个变量的指针,该变量将接收消息的大小
*
*       p_ts          是指向一个变量的指针,该变量将接收消息接收到的时间戳、挂起被中止的时间或消息队列被删除的时间。如果你传递一个NULL指针(即
*                     (CPU_TS *)0),则不会获取时间戳。换句话说,传递NULL指针是有效的,表示你不需要时间戳。
*
*       p_err         是指向一个变量的指针,该变量将包含此函数返回的错误代码。
*
*                                OS_ERR_NONE               调用成功,任务收到了消息
*                                OS_ERR_OBJ_DEL            如果 'p_q' 被删除
*                                OS_ERR_OBJ_PTR_NULL       如果你传递了 'p_q' 的NULL指针
*                                OS_ERR_OBJ_TYPE           如果消息队列未创建
*                                OS_ERR_OPT_INVALID        你指定了无效的选项
*                                OS_ERR_OS_NOT_RUNNING     如果 uC/OS-III 尚未运行
*                                OS_ERR_PEND_ABORT         挂起被中止
*                                OS_ERR_PEND_ISR           如果你从ISR中调用了此函数
*                                OS_ERR_PEND_WOULD_BLOCK   如果你指定了非阻塞但队列为空
*                                OS_ERR_PTR_INVALID        如果你传递了 'p_msg_size' 的NULL指针
*                                OS_ERR_SCHED_LOCKED       调度器被锁定
*                                OS_ERR_STATUS_INVALID     如果挂起状态有无效值
*                                OS_ERR_TIMEOUT            在指定的超时时间内没有收到消息
*                                                          会导致挂起。
*                                OS_ERR_TICK_DISABLED      如果内核滴答被禁用且指定了超时时间
*
* 返回值: != (void *)0  是指向接收到的消息的指针
*         == (void *)0  如果你收到了NULL指针消息,或者
*                       如果没有收到消息,或者
*                       如果 'p_q' 是NULL指针,或者
*                       如果你没有传递队列的指针。
*
* 注意: 此API '不得' 从定时器回调函数中调用。
************************************************************************************************************************
*/

void  *OSQPend (OS_Q         *p_q,
                OS_TICK       timeout,
                OS_OPT        opt,
                OS_MSG_SIZE  *p_msg_size,
                CPU_TS       *p_ts,
                OS_ERR       *p_err)
{
    void  *p_void;
    CPU_SR_ALLOC();


#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((void *)0);
    }
#endif

    OS_TRACE_Q_PEND_ENTER(p_q, timeout, opt, p_msg_size, p_ts);

#if (OS_CFG_TICK_EN == 0u)
    if (timeout != 0u) {
       *p_err = OS_ERR_TICK_DISABLED;
        OS_TRACE_Q_PEND_FAILED(p_q);
        OS_TRACE_Q_PEND_EXIT(OS_ERR_TICK_DISABLED);
        return ((void *)0);
    }
#endif

#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
    if (OSIntNestingCtr > 0u) {                                 /* Not allowed to call from an ISR                      */
        if ((opt & OS_OPT_PEND_NON_BLOCKING) != OS_OPT_PEND_NON_BLOCKING) {
            OS_TRACE_Q_PEND_FAILED(p_q);
            OS_TRACE_Q_PEND_EXIT(OS_ERR_PEND_ISR);
           *p_err = OS_ERR_PEND_ISR;
            return ((void *)0);
        }
    }
#endif

#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
    if (OSRunning != OS_STATE_OS_RUNNING) {                     /* Is the kernel running?                               */
        OS_TRACE_Q_PEND_EXIT(OS_ERR_OS_NOT_RUNNING);
       *p_err = OS_ERR_OS_NOT_RUNNING;
        return ((void *)0);
    }
#endif

#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_q == (OS_Q *)0) {                                     /* Validate arguments                                   */
        OS_TRACE_Q_PEND_FAILED(p_q);
        OS_TRACE_Q_PEND_EXIT(OS_ERR_OBJ_PTR_NULL);
       *p_err = OS_ERR_OBJ_PTR_NULL;
        return ((void *)0);
    }
    if (p_msg_size == (OS_MSG_SIZE *)0) {
        OS_TRACE_Q_PEND_FAILED(p_q);
        OS_TRACE_Q_PEND_EXIT(OS_ERR_PTR_INVALID);
       *p_err = OS_ERR_PTR_INVALID;
        return ((void *)0);
    }
    switch (opt) {
        case OS_OPT_PEND_BLOCKING:
        case OS_OPT_PEND_NON_BLOCKING:
             break;

        default:
             OS_TRACE_Q_PEND_FAILED(p_q);
             OS_TRACE_Q_PEND_EXIT(OS_ERR_OPT_INVALID);
            *p_err = OS_ERR_OPT_INVALID;
             return ((void *)0);
    }
#endif

#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
    if (p_q->Type != OS_OBJ_TYPE_Q) {                           /* Make sure message queue was created                  */
        OS_TRACE_Q_PEND_FAILED(p_q);
        OS_TRACE_Q_PEND_EXIT(OS_ERR_OBJ_TYPE);
       *p_err = OS_ERR_OBJ_TYPE;
        return ((void *)0);
    }
#endif

    if (p_ts != (CPU_TS *)0)
    {
        *p_ts = 0u; // 初始化返回的时间戳
    }

    CPU_CRITICAL_ENTER();           // 进入临界区以确保原子操作
    p_void = OS_MsgQGet(&p_q->MsgQ, // 尝试从消息队列中获取消息
                        p_msg_size,
                        p_ts,
                        p_err);

    if (*p_err == OS_ERR_NONE)
    {
        OS_TRACE_Q_PEND(p_q);              // 跟踪消息队列挂起
        CPU_CRITICAL_EXIT();               // 退出临界区
        OS_TRACE_Q_PEND_EXIT(OS_ERR_NONE); // 跟踪消息队列挂起退出
        return (p_void);                   // 成功,返回接收到的消息
    }

    if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u)
    {                                                  // 调用者是否希望在不可用时阻塞?
        CPU_CRITICAL_EXIT();                           // 退出临界区
        OS_TRACE_Q_PEND_FAILED(p_q);                   // 跟踪消息队列挂起失败
        OS_TRACE_Q_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK); // 跟踪消息队列挂起退出
        *p_err = OS_ERR_PEND_WOULD_BLOCK;              // 不会阻塞
        return ((void *)0);                            // 返回 NULL
    }
    else
    {
        if (OSSchedLockNestingCtr > 0u)
        {                                              // 当调度器被锁定时不能挂起
            CPU_CRITICAL_EXIT();                       // 退出临界区
            OS_TRACE_Q_PEND_FAILED(p_q);               // 跟踪消息队列挂起失败
            OS_TRACE_Q_PEND_EXIT(OS_ERR_SCHED_LOCKED); // 跟踪消息队列挂起退出
            *p_err = OS_ERR_SCHED_LOCKED;              // 调度器被锁定
            return ((void *)0);                        // 返回 NULL
        }
    }

    OS_Pend((OS_PEND_OBJ *)((void *)p_q), // 任务在消息队列上挂起
            OSTCBCurPtr,
            OS_TASK_PEND_ON_Q,
            timeout);
    CPU_CRITICAL_EXIT();        // 退出临界区
    OS_TRACE_Q_PEND_BLOCK(p_q); // 跟踪消息队列挂起阻塞
    OSSched();                  // 查找下一个最高优先级的任务

    CPU_CRITICAL_ENTER(); // 进入临界区以确保原子操作
    switch (OSTCBCurPtr->PendStatus)
    {
    case OS_STATUS_PEND_OK: // 从 TCB 中提取消息(由 Post 放置)
        p_void = OSTCBCurPtr->MsgPtr;
        *p_msg_size = OSTCBCurPtr->MsgSize;
#if (OS_CFG_TS_EN > 0u)
        if (p_ts != (CPU_TS *)0)
        {
            *p_ts = OSTCBCurPtr->TS;
        }
#endif
        OS_TRACE_Q_PEND(p_q); // 跟踪消息队列挂起
        *p_err = OS_ERR_NONE;
        break;

    case OS_STATUS_PEND_ABORT: // 表示我们已中止
        p_void = (void *)0;
        *p_msg_size = 0u;
#if (OS_CFG_TS_EN > 0u)
        if (p_ts != (CPU_TS *)0)
        {
            *p_ts = OSTCBCurPtr->TS;
        }
#endif
        OS_TRACE_Q_PEND_FAILED(p_q); // 跟踪消息队列挂起失败
        *p_err = OS_ERR_PEND_ABORT;
        break;

    case OS_STATUS_PEND_TIMEOUT: // 表示我们在超时时间内没有接收到事件
        p_void = (void *)0;
        *p_msg_size = 0u;
        OS_TRACE_Q_PEND_FAILED(p_q); // 跟踪消息队列挂起失败
        *p_err = OS_ERR_TIMEOUT;
        break;

    case OS_STATUS_PEND_DEL: // 表示挂起的对象已被删除
        p_void = (void *)0;
        *p_msg_size = 0u;
#if (OS_CFG_TS_EN > 0u)
        if (p_ts != (CPU_TS *)0)
        {
            *p_ts = OSTCBCurPtr->TS;
        }
#endif
        OS_TRACE_Q_PEND_FAILED(p_q); // 跟踪消息队列挂起失败
        *p_err = OS_ERR_OBJ_DEL;
        break;

    default:
        p_void = (void *)0;
        *p_msg_size = 0u;
        OS_TRACE_Q_PEND_FAILED(p_q); // 跟踪消息队列挂起失败
        *p_err = OS_ERR_STATUS_INVALID;
        break;
    }
    CPU_CRITICAL_EXIT();          // 退出临界区
    OS_TRACE_Q_PEND_EXIT(*p_err); // 跟踪消息队列挂起退出
    return (p_void);              // 返回消息指针
}


/*
************************************************************************************************************************
*                                             中断消息队列上的等待
*
* 描述: 此函数中断并准备好当前正在队列上等待的任何任务。此函数应用于故障中断队列上的等待,而不是通过 OSQPost() 正常信号队列。
*
* 参数: p_q       是指向消息队列的指针
*
*       opt       确定执行的中断类型:
*
*                            OS_OPT_PEND_ABORT_1          中断单个任务(最高优先级任务)在队列上的等待
*                            OS_OPT_PEND_ABORT_ALL        中断所有在队列上等待的任务
*                            OS_OPT_POST_NO_SCHED         不调用调度器
*
*       p_err     是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*
*                            OS_ERR_NONE                  至少有一个在队列上等待的任务被准备好并被告知等待已中断;检查返回值以获取队列上等待被中断的任务数量
*                            OS_ERR_OBJ_PTR_NULL          如果传递的 'p_q' 是 NULL 指针
*                            OS_ERR_OBJ_TYPE              如果消息队列未创建
*                            OS_ERR_OPT_INVALID           指定了无效的选项
*                            OS_ERR_OS_NOT_RUNNING        如果 uC/OS-III 尚未运行
*                            OS_ERR_PEND_ABORT_ISR        如果此函数是从 ISR 中调用的
*                            OS_ERR_PEND_ABORT_NONE       没有任务在等待
*
* 返回值: == 0      如果没有任务在队列上等待,或者发生错误。
*              >  0      如果一个或多个在队列上等待的任务现在已被准备好并被告知。
*
* 注意: 无
************************************************************************************************************************
*/

#if (OS_CFG_Q_PEND_ABORT_EN > 0u)
OS_OBJ_QTY OSQPendAbort(OS_Q *p_q, OS_OPT opt, OS_ERR *p_err)
{
    OS_PEND_LIST *p_pend_list;
    OS_TCB *p_tcb;
    CPU_TS ts;
    OS_OBJ_QTY nbr_tasks;
    CPU_SR_ALLOC();

#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return (0u);
    }
#endif

#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
    if (OSIntNestingCtr > 0u) {                                 /* 不允许从 ISR 中中断等待 */
        *p_err = OS_ERR_PEND_ABORT_ISR;
        return (0u);
    }
#endif

#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
    if (OSRunning != OS_STATE_OS_RUNNING) {                     /* 内核是否在运行? */
        *p_err = OS_ERR_OS_NOT_RUNNING;
        return (0u);
    }
#endif

#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_q == (OS_Q *)0) {                                     /* 验证 'p_q' */
        *p_err = OS_ERR_OBJ_PTR_NULL;
        return (0u);
    }
    switch (opt) {                                              /* 验证 'opt' */
        case OS_OPT_PEND_ABORT_1:
        case OS_OPT_PEND_ABORT_ALL:
        case OS_OPT_PEND_ABORT_1 | OS_OPT_POST_NO_SCHED:
        case OS_OPT_PEND_ABORT_ALL | OS_OPT_POST_NO_SCHED:
            break;

        default:
            *p_err = OS_ERR_OPT_INVALID;
            return (0u);
    }
#endif

#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
    if (p_q->Type != OS_OBJ_TYPE_Q) {                           /* 确保队列已创建 */
        *p_err = OS_ERR_OBJ_TYPE;
        return (0u);
    }
#endif

    CPU_CRITICAL_ENTER();
    p_pend_list = &p_q->PendList;
    if (p_pend_list->HeadPtr == (OS_TCB *)0) {                  /* 有任何任务在队列上等待吗? */
        CPU_CRITICAL_EXIT();                                    /* 没有 */
        *p_err = OS_ERR_PEND_ABORT_NONE;
        return (0u);
    }

    nbr_tasks = 0u;
#if (OS_CFG_TS_EN > 0u)
    ts = OS_TS_GET();                                           /* 获取本地时间戳,以便所有任务获得相同的时间 */
#else
    ts = 0u;
#endif
    while (p_pend_list->HeadPtr != (OS_TCB *)0) {
        p_tcb = p_pend_list->HeadPtr;
        OS_PendAbort(p_tcb, ts, OS_STATUS_PEND_ABORT);
        nbr_tasks++;
        if (opt != OS_OPT_PEND_ABORT_ALL) {                     /* 是否中断所有在队列上等待的任务? */
            break;                                              /* 否 */
        }
    }
    CPU_CRITICAL_EXIT();

    if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
        OSSched();                                              /* 运行调度器 */
    }

    *p_err = OS_ERR_NONE;
    return (nbr_tasks);
}
#endif


/*
************************************************************************************************************************
*                                               向队列发送消息
*
* 描述: 此函数向队列发送一条消息。通过 'opt' 参数,您可以指定消息是否广播到所有等待的任务,以及您是将消息发布到队列的前端(LIFO)还是通常发布到队列的末尾(FIFO)。
*
* 参数: p_q           是一个指向必须由 OSQCreate() 创建的消息队列的指针。
*
*       p_void        是一个指向要发送的消息的指针。
*
*       msg_size      指定消息的大小(以字节为单位)。
*
*       opt           确定执行的 POST 类型:
*
*                                OS_OPT_POST_ALL          向所有在队列上等待的任务发送消息。此选项可以与 OS_OPT_POST_FIFO 或 OS_OPT_POST_LIFO 之一组合。
*                                OS_OPT_POST_FIFO         将消息发布到队列末尾(FIFO)并唤醒一个等待的任务。
*                                OS_OPT_POST_LIFO         将消息发布到队列前端(LIFO)并唤醒一个等待的任务。
*                                OS_OPT_POST_NO_SCHED     不调用调度器。
*
*                            注意: 1) OS_OPT_POST_NO_SCHED 可以与其它选项之一组合(或按位或)。
*                                  2) OS_OPT_POST_ALL 可以与其它选项之一组合(或按位或)。
*                                  3) 可能的选项组合包括:
*
*                                        OS_OPT_POST_FIFO
*                                        OS_OPT_POST_LIFO
*                                        OS_OPT_POST_FIFO + OS_OPT_POST_ALL
*                                        OS_OPT_POST_LIFO + OS_OPT_POST_ALL
*                                        OS_OPT_POST_FIFO + OS_OPT_POST_NO_SCHED
*                                        OS_OPT_POST_LIFO + OS_OPT_POST_NO_SCHED
*                                        OS_OPT_POST_FIFO + OS_OPT_POST_ALL + OS_OPT_POST_NO_SCHED
*                                        OS_OPT_POST_LIFO + OS_OPT_POST_ALL + OS_OPT_POST_NO_SCHED
*
*       p_err         是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*
*                                OS_ERR_NONE              调用成功且消息已发送。
*                                OS_ERR_MSG_POOL_EMPTY    如果没有更多的 OS_MSG 可用于放置消息。
*                                OS_ERR_OBJ_PTR_NULL      如果 'p_q' 是 NULL 指针。
*                                OS_ERR_OBJ_TYPE          如果消息队列未初始化。
*                                OS_ERR_OPT_INVALID       指定了无效的选项。
*                                OS_ERR_OS_NOT_RUNNING    如果 uC/OS-III 尚未运行。
*                                OS_ERR_Q_MAX             如果队列已满。
*
* 返回值: 无
*
* 注意: 无
************************************************************************************************************************
*/

void OSQPost(OS_Q *p_q, void *p_void, OS_MSG_SIZE msg_size, OS_OPT opt, OS_ERR *p_err)
{
    OS_OPT post_type;
    OS_PEND_LIST *p_pend_list;
    OS_TCB *p_tcb;
    OS_TCB *p_tcb_next;
    CPU_TS ts;
    CPU_SR_ALLOC();

#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

    OS_TRACE_Q_POST_ENTER(p_q, p_void, msg_size, opt);

#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
    if (OSRunning != OS_STATE_OS_RUNNING) {                     /* 内核是否在运行? */
        OS_TRACE_Q_POST_EXIT(OS_ERR_OS_NOT_RUNNING);
        *p_err = OS_ERR_OS_NOT_RUNNING;
        return;
    }
#endif

#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_q == (OS_Q *)0) {                                     /* 验证 'p_q' */
        OS_TRACE_Q_POST_FAILED(p_q);
        OS_TRACE_Q_POST_EXIT(OS_ERR_OBJ_PTR_NULL);
        *p_err = OS_ERR_OBJ_PTR_NULL;
        return;
    }
    switch (opt) {                                              /* 验证 'opt' */
        case OS_OPT_POST_FIFO:
        case OS_OPT_POST_LIFO:
        case OS_OPT_POST_FIFO | OS_OPT_POST_ALL:
        case OS_OPT_POST_LIFO | OS_OPT_POST_ALL:
        case OS_OPT_POST_FIFO | OS_OPT_POST_NO_SCHED:
        case OS_OPT_POST_LIFO | OS_OPT_POST_NO_SCHED:
        case OS_OPT_POST_FIFO | (OS_OPT)(OS_OPT_POST_ALL | OS_OPT_POST_NO_SCHED):
        case OS_OPT_POST_LIFO | (OS_OPT)(OS_OPT_POST_ALL | OS_OPT_POST_NO_SCHED):
            break;

        default:
            OS_TRACE_Q_POST_FAILED(p_q);
            OS_TRACE_Q_POST_EXIT(OS_ERR_OPT_INVALID);
            *p_err = OS_ERR_OPT_INVALID;
            return;
    }
#endif

#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
    if (p_q->Type != OS_OBJ_TYPE_Q) {                           /* 确保消息队列已创建 */
        OS_TRACE_Q_POST_FAILED(p_q);
        OS_TRACE_Q_POST_EXIT(OS_ERR_OBJ_TYPE);
        *p_err = OS_ERR_OBJ_TYPE;
        return;
    }
#endif

#if (OS_CFG_TS_EN > 0u)
    ts = OS_TS_GET();                                           /* 获取时间戳 */
#else
    ts = 0u;
#endif

    OS_TRACE_Q_POST(p_q);

    CPU_CRITICAL_ENTER();
    p_pend_list = &p_q->PendList;
    if (p_pend_list->HeadPtr == (OS_TCB *)0) {                  /* 有任何任务在消息队列上等待吗? */
        if ((opt & OS_OPT_POST_LIFO) == 0u) {                   /* 确定我们是 FIFO 还是 LIFO 发布 */
            post_type = OS_OPT_POST_FIFO;
        } else {
            post_type = OS_OPT_POST_LIFO;
        }
        OS_MsgQPut(&p_q->MsgQ,                                  /* 将消息放入消息队列 */
                   p_void,
                   msg_size,
                   post_type,
                   ts,
                   p_err);
        CPU_CRITICAL_EXIT();
        OS_TRACE_Q_POST_EXIT(*p_err);
        return;
    }

    p_tcb = p_pend_list->HeadPtr;
    while (p_tcb != (OS_TCB *)0) {
        p_tcb_next = p_tcb->PendNextPtr;
        OS_Post((OS_PEND_OBJ *)((void *)p_q),
                p_tcb,
                p_void,
                msg_size,
                ts);
        if ((opt & OS_OPT_POST_ALL) == 0u) {                    /* 是否向所有等待的任务发布消息? */
            break;                                              /* 否 */
        }
        p_tcb = p_tcb_next;
    }

    CPU_CRITICAL_EXIT();

    if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
        OSSched();                                              /* 运行调度器 */
    }

    *p_err = OS_ERR_NONE;
    OS_TRACE_Q_POST_EXIT(*p_err);
}


/*
************************************************************************************************************************
*                                        清空消息队列的内容
*
* 描述: 此函数由 OSQDel() 调用,用于清空消息队列的内容
*
* 参数: p_q      是一个指向要清空的队列的指针
*              ---
*
* 返回值: 无
*
* 注意: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/

void OS_QClr(OS_Q *p_q)
{
    (void)OS_MsgQFreeAll(&p_q->MsgQ);                           /* 将所有 OS_MSG 返回到空闲列表 */
#if (OS_OBJ_TYPE_REQ > 0u)
    p_q->Type = OS_OBJ_TYPE_NONE;                               /* 将数据结构标记为 NONE */
#endif
#if (OS_CFG_DBG_EN > 0u)
    p_q->NamePtr = (CPU_CHAR *)((void *)"?Q");                  /* 设置名称指针为 "?Q" 以便调试 */
#endif
    OS_MsgQInit(&p_q->MsgQ,                                     /* 初始化 OS_MSG 列表 */
                0u);
    OS_PendListInit(&p_q->PendList);                            /* 初始化等待列表 */
}


/*
************************************************************************************************************************
*                                      添加/移除消息队列到/从调试列表
*
* 描述: 这些函数由 uC/OS-III 调用,用于将消息队列添加到或从消息队列调试列表中移除。
*
* 参数: p_q     是一个指向要添加或移除的消息队列的指针
*
* 返回值: 无
*
* 注意: 这些函数是 uC/OS-III 的内部函数,您的应用程序不应调用它们。
************************************************************************************************************************
*/

#if (OS_CFG_DBG_EN > 0u)
void  OS_QDbgListAdd (OS_Q  *p_q)
{
    p_q->DbgNamePtr               = (CPU_CHAR *)((void *)" ");
    p_q->DbgPrevPtr               = (OS_Q *)0;
    if (OSQDbgListPtr == (OS_Q *)0) {
        p_q->DbgNextPtr           = (OS_Q *)0;
    } else {
        p_q->DbgNextPtr           =  OSQDbgListPtr;
        OSQDbgListPtr->DbgPrevPtr =  p_q;
    }
    OSQDbgListPtr                 =  p_q;
}


void  OS_QDbgListRemove (OS_Q  *p_q)
{
    OS_Q  *p_q_next;
    OS_Q  *p_q_prev;


    p_q_prev = p_q->DbgPrevPtr;
    p_q_next = p_q->DbgNextPtr;

    if (p_q_prev == (OS_Q *)0) {
        OSQDbgListPtr = p_q_next;
        if (p_q_next != (OS_Q *)0) {
            p_q_next->DbgPrevPtr = (OS_Q *)0;
        }
        p_q->DbgNextPtr = (OS_Q *)0;

    } else if (p_q_next == (OS_Q *)0) {
        p_q_prev->DbgNextPtr = (OS_Q *)0;
        p_q->DbgPrevPtr      = (OS_Q *)0;

    } else {
        p_q_prev->DbgNextPtr =  p_q_next;
        p_q_next->DbgPrevPtr =  p_q_prev;
        p_q->DbgNextPtr      = (OS_Q *)0;
        p_q->DbgPrevPtr      = (OS_Q *)0;
    }
}
#endif
#endif

posted @   炽杨  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示