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
分类:
操作系统 / UcOs-III
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具