//作用:用于资源管理和同步的信号量代码;
/*
*********************************************************************************************************
* 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.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* SEMAPHORE MANAGEMENT
*
* File : os_sem.c
* Version : V3.08.02
*********************************************************************************************************
*/
#define MICRIUM_SOURCE
#include "os.h"
#ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
const CPU_CHAR *os_sem__c = "$Id: $";
#endif
#if (OS_CFG_SEM_EN > 0u)
/*
************************************************************************************************************************
* 创建信号量
*
* 描述: 此函数创建一个信号量。
*
* 参数: p_sem 指向要初始化的信号量的指针。您的应用程序负责为信号量分配存储空间。
*
* p_name 是一个指向您希望给信号量命名的字符串的指针。
*
* cnt 是信号量的初始值。
* 如果用于共享资源,应初始化为可用资源的数量。
* 如果用于信号事件的发生,则应初始化为 0。
*
* p_err 是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*
* OS_ERR_NONE 如果调用成功
* OS_ERR_CREATE_ISR 如果从 ISR 中调用此函数
* OS_ERR_ILLEGAL_CREATE_RUN_TIME 如果在调用 OSSafetyCriticalStart() 之后尝试创建信号量
* OS_ERR_OBJ_PTR_NULL 如果 'p_sem' 是空指针
* OS_ERR_OBJ_CREATED 如果信号量已创建
*
* 返回: 无
*
* 注意: 无
************************************************************************************************************************
*/
// 创建一个信号量并初始化其参数
//
// @param p_sem 指向信号量控制块的指针
// @param p_name 信号量的名称
// @param cnt 信号量的初始计数
// @param p_err 指向错误代码的指针
void OSSemCreate(OS_SEM *p_sem, CPU_CHAR *p_name, OS_SEM_CTR cnt, 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_sem == (OS_SEM *)0)
{
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif
CPU_CRITICAL_ENTER(); // 进入临界区
#if (OS_OBJ_TYPE_REQ > 0u)
#if (OS_CFG_OBJ_CREATED_CHK_EN > 0u)
// 检查信号量是否已经创建,如果是则返回错误
if (p_sem->Type == OS_OBJ_TYPE_SEM)
{
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OBJ_CREATED;
return;
}
#endif
p_sem->Type = OS_OBJ_TYPE_SEM; // 标记数据结构为信号量
#endif
p_sem->Ctr = cnt; // 设置信号量值
#if (OS_CFG_TS_EN > 0u)
p_sem->TS = 0u; // 如果配置了时间戳,则初始化时间戳
#endif
#if (OS_CFG_DBG_EN > 0u)
p_sem->NamePtr = p_name; // 如果配置了调试,则保存信号量的名称
#else
(void)p_name; // 避免编译器警告
#endif
OS_PendListInit(&p_sem->PendList); // 初始化等待列表
#if (OS_CFG_DBG_EN > 0u)
OS_SemDbgListAdd(p_sem); // 如果配置了调试,则将信号量添加到调试列表中
OSSemQty++; // 增加信号量数量计数
#endif
OS_TRACE_SEM_CREATE(p_sem, p_name); // 跟踪信号量创建
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE; // 设置无错误
}
/*
************************************************************************************************************************
* 删除信号量
*
* 描述: 此函数删除一个信号量。
*
* 参数: p_sem 指向要删除的信号量的指针
*
* 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_sem' 是空指针
* OS_ERR_OBJ_TYPE 如果 'p_sem' 不是指向信号量的指针
* OS_ERR_OPT_INVALID 指定了无效的选项
* OS_ERR_OS_NOT_RUNNING 如果 uC/OS-III 尚未运行
* OS_ERR_TASK_WAITING 一个或多个任务在等待信号量
*
* 返回: == 0 如果没有任务在等待信号量,或者发生错误。
* > 0 如果一个或多个等待信号量的任务现在已被就绪并被告知。
*
* 注意: 1) 使用此函数时必须小心。通常期望信号量存在的任务必须检查 OSSemPend() 的返回代码。
* 2) 因为所有等待信号量的任务都将被就绪,所以在信号量用于互斥的应用程序中必须特别小心,因为资源将不再受信号量保护。
************************************************************************************************************************
*/
#if (OS_CFG_SEM_DEL_EN > 0u)
// 删除一个信号量,并处理相关等待任务
OS_OBJ_QTY OSSemDel(OS_SEM *p_sem,
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();
// 安全关键代码检查:确保p_err参数不为空
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0)
{
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
// 进入删除信号量的跟踪
OS_TRACE_SEM_DEL_ENTER(p_sem, opt);
// 安全关键代码检查:在运行时不允许删除信号量
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == OS_TRUE)
{
OS_TRACE_SEM_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)
{
OS_TRACE_SEM_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_SEM_DEL_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
// 参数有效性检查:确保p_sem不为空
#if (OS_CFG_ARG_CHK_EN > 0u)
if (p_sem == (OS_SEM *)0)
{
OS_TRACE_SEM_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_sem->Type != OS_OBJ_TYPE_SEM)
{
OS_TRACE_SEM_DEL_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
// 进入临界区,防止多任务环境下的竞态条件
CPU_CRITICAL_ENTER();
p_pend_list = &p_sem->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_SemDbgListRemove(p_sem);
OSSemQty--;
#endif
OS_TRACE_SEM_DEL(p_sem);
OS_SemClr(p_sem);
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_SemDbgListRemove(p_sem);
OSSemQty--;
#endif
OS_TRACE_SEM_DEL(p_sem);
OS_SemClr(p_sem);
CPU_CRITICAL_EXIT();
OSSched();
*p_err = OS_ERR_NONE;
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OPT_INVALID;
break;
}
// 删除信号量操作结束的跟踪
OS_TRACE_SEM_DEL_EXIT(*p_err);
return (nbr_tasks);
}
#endif
/*
************************************************************************************************************************
* 等待信号量
*
* 描述: 此函数等待一个信号量。
*
* 参数: p_sem 指向信号量的指针
*
* timeout 是一个可选的超时时间(以时钟滴答为单位)。如果非零,您的任务将在指定的时间(以“滴答”为单位)内等待资源。如果您指定 0,则您的任务将在指定的信号量上无限期等待,直到资源可用(或事件发生)。
*
* opt 确定用户是否希望在信号量不可用时阻塞:
*
* OS_OPT_PEND_BLOCKING
* OS_OPT_PEND_NON_BLOCKING
*
* p_ts 是一个指向变量的指针,该变量将接收信号量被发送、等待被终止或信号量被删除的时间戳。如果您传递一个空指针(即 (CPU_TS*)0),则不会获取时间戳。换句话说,传递空指针是有效的,表示您不需要时间戳。
*
* p_err 是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*
* OS_ERR_NONE 调用成功且您的任务拥有资源,或您等待的事件已发生
* OS_ERR_OBJ_DEL 如果 'p_sem' 被删除
* OS_ERR_OBJ_PTR_NULL 如果 'p_sem' 是空指针
* OS_ERR_OBJ_TYPE 如果 'p_sem' 不是指向信号量的指针
* OS_ERR_OPT_INVALID 如果指定了无效的 'opt' 值
* OS_ERR_OS_NOT_RUNNING 如果 uC/OS-III 尚未运行
* OS_ERR_PEND_ABORT 如果等待被其他任务终止
* OS_ERR_PEND_ISR 如果从 ISR 中调用此函数且结果会导致挂起
* OS_ERR_PEND_WOULD_BLOCK 如果指定了非阻塞但信号量不可用
* OS_ERR_SCHED_LOCKED 如果在调度器锁定时调用此函数
* OS_ERR_STATUS_INVALID 等待状态无效
* OS_ERR_TIMEOUT 在指定的超时时间内未接收到信号量
* OS_ERR_TICK_DISABLED 如果内核滴答被禁用且指定了超时时间
*
* 返回: 信号量计数器的当前值,如果不可用则返回 0。
*
* 注意: 此 API “不得” 从定时器回调函数中调用。
************************************************************************************************************************
*/
/**
* @brief 挂起任务直到信号量可用
*
* 此函数使当前任务挂起,直到信号量变为可用状态或达到指定的超时时间。如果信号量在指定时间内未变为可用状态,
* 则根据提供的选项参数决定是否继续等待或返回错误。这是嵌入式实时操作系统(RTOS)中用于任务间同步和资源管理的基本机制。
*
* @param p_sem 指向信号量的指针,该信号量用于任务同步
* @param timeout 任务等待信号量变为可用状态的最大时间,如果设置为0,则无限期等待
* @param opt 控制函数行为的选项,例如是否在等待期间允许任务调度
* @param p_ts 指向用于存储当前时间戳的变量的指针,如果不需要时间戳,则可以设置为NULL
* @param p_err 指向用于存储函数执行结果的错误代码的指针
* @return OS_SEM_CTR 返回当前信号量的计数器值,表示还有多少次获取操作可以使信号量变为不可用状态
*/
OS_SEM_CTR OSSemPend(OS_SEM *p_sem,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err)
{
OS_SEM_CTR ctr;
CPU_SR_ALLOC();
#if (OS_CFG_TS_EN == 0u)
(void)p_ts; // 防止编译器警告未使用 'ts'
#endif
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0)
{
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
OS_TRACE_SEM_PEND_ENTER(p_sem, timeout, opt, p_ts); // 进入信号量等待跟踪
#if (OS_CFG_TICK_EN == 0u)
if (timeout != 0u)
{
*p_err = OS_ERR_TICK_DISABLED;
OS_TRACE_SEM_PEND_FAILED(p_sem);
OS_TRACE_SEM_PEND_EXIT(OS_ERR_TICK_DISABLED);
return (0u);
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
if (OSIntNestingCtr > 0u)
{ // 不允许从ISR调用
if ((opt & OS_OPT_PEND_NON_BLOCKING) != OS_OPT_PEND_NON_BLOCKING)
{
OS_TRACE_SEM_PEND_FAILED(p_sem);
OS_TRACE_SEM_PEND_EXIT(OS_ERR_PEND_ISR);
*p_err = OS_ERR_PEND_ISR;
return (0u);
}
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
if (OSRunning != OS_STATE_OS_RUNNING)
{ // 检查内核是否正在运行
OS_TRACE_SEM_PEND_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_sem == (OS_SEM *)0)
{ // 验证 'p_sem'
OS_TRACE_SEM_PEND_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
switch (opt)
{ // 验证 'opt'
case OS_OPT_PEND_BLOCKING:
case OS_OPT_PEND_NON_BLOCKING:
break;
default:
OS_TRACE_SEM_PEND_FAILED(p_sem);
OS_TRACE_SEM_PEND_EXIT(OS_ERR_OPT_INVALID);
*p_err = OS_ERR_OPT_INVALID;
return (0u);
}
#endif
#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
if (p_sem->Type != OS_OBJ_TYPE_SEM)
{ // 确保信号量已创建
OS_TRACE_SEM_PEND_FAILED(p_sem);
OS_TRACE_SEM_PEND_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
CPU_CRITICAL_ENTER(); /* 进入临界区,保护共享资源 */
if (p_sem->Ctr > 0u)
{ /* 检查信号量是否可用 */
p_sem->Ctr--; /* 信号量可用,减少计数器 */
#if (OS_CFG_TS_EN > 0u)
if (p_ts != (CPU_TS *)0)
{
*p_ts = p_sem->TS; /* 获取最后一次发布的时间戳 */
}
#endif
ctr = p_sem->Ctr;
OS_TRACE_SEM_PEND(p_sem);
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_EXIT(OS_ERR_NONE);
*p_err = OS_ERR_NONE;
return (ctr);
}
if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u)
{ /* 检查是否非阻塞模式 */
#if (OS_CFG_TS_EN > 0u)
if (p_ts != (CPU_TS *)0)
{
*p_ts = 0u;
}
#endif
ctr = p_sem->Ctr; /* 信号量不可用,返回当前计数 */
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_FAILED(p_sem);
OS_TRACE_SEM_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
*p_err = OS_ERR_PEND_WOULD_BLOCK;
return (ctr);
}
else
{
if (OSSchedLockNestingCtr > 0u)
{ /* 检查调度程序是否锁定 */
#if (OS_CFG_TS_EN > 0u)
if (p_ts != (CPU_TS *)0)
{
*p_ts = 0u;
}
#endif
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_FAILED(p_sem);
OS_TRACE_SEM_PEND_EXIT(OS_ERR_SCHED_LOCKED);
*p_err = OS_ERR_SCHED_LOCKED;
return (0u);
}
}
OS_Pend((OS_PEND_OBJ *)((void *)p_sem), /* 任务阻塞,等待信号量 */
OSTCBCurPtr,
OS_TASK_PEND_ON_SEM,
timeout);
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_BLOCK(p_sem);
OSSched(); /* 调度程序寻找下一个最高优先级的任务 */
CPU_CRITICAL_ENTER();
switch (OSTCBCurPtr->PendStatus)
{
case OS_STATUS_PEND_OK: /* 获取到信号量 */
#if (OS_CFG_TS_EN > 0u)
if (p_ts != (CPU_TS *)0)
{
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_SEM_PEND(p_sem);
*p_err = OS_ERR_NONE;
break;
case OS_STATUS_PEND_ABORT: /* 中止等待 */
#if (OS_CFG_TS_EN > 0u)
if (p_ts != (CPU_TS *)0)
{
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_SEM_PEND_FAILED(p_sem);
*p_err = OS_ERR_PEND_ABORT;
break;
case OS_STATUS_PEND_TIMEOUT: /* 等待超时 */
#if (OS_CFG_TS_EN > 0u)
if (p_ts != (CPU_TS *)0)
{
*p_ts = 0u;
}
#endif
OS_TRACE_SEM_PEND_FAILED(p_sem);
*p_err = OS_ERR_TIMEOUT;
break;
case OS_STATUS_PEND_DEL: /* 信号量被删除 */
#if (OS_CFG_TS_EN > 0u)
if (p_ts != (CPU_TS *)0)
{
*p_ts = OSTCBCurPtr->TS;
}
#endif
OS_TRACE_SEM_PEND_FAILED(p_sem);
*p_err = OS_ERR_OBJ_DEL;
break;
default:
OS_TRACE_SEM_PEND_FAILED(p_sem);
*p_err = OS_ERR_STATUS_INVALID;
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_EXIT(*p_err);
return (0u);
}
ctr = p_sem->Ctr;
CPU_CRITICAL_EXIT();
OS_TRACE_SEM_PEND_EXIT(*p_err);
return (ctr);
}
/*
************************************************************************************************************************
* 终止对信号量的等待
*
* 描述: 此函数终止并使当前正在等待信号量的所有任务就绪。此函数应用于故障终止对信号量的等待,而不是通过 OSSemPost() 正常发送信号量。
*
* 参数: p_sem 指向信号量的指针
*
* 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_sem' 是空指针
* OS_ERR_OBJ_TYPE 如果 'p_sem' 不是指向信号量的指针
* 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_SEM_PEND_ABORT_EN > 0u)
// OSSemPendAbort函数用于中止在指定信号量上等待的任务。
// 该函数可以中止一个或所有在信号量上等待的任务,并且可以选择是否重新调度。
// 参数:
// p_sem: 指向信号量控制块的指针。
// opt: 指定中止等待的选项,可以是OS_OPT_PEND_ABORT_1(中止一个任务)或OS_OPT_PEND_ABORT_ALL(中止所有任务),
// 结合OS_OPT_POST_NO_SCHED选项使用时,中止等待后不进行重新调度。
// p_err: 指向错误代码的指针,用于返回函数执行的错误代码。
// 返回值:
// 返回成功中止等待的任务数量。
OS_OBJ_QTY OSSemPendAbort(OS_SEM *p_sem,
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)
{ /* Not allowed to Pend Abort from an 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)
{ /* Is the kernel running? */
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
// 参数有效性检查:检查信号量指针和选项参数
#if (OS_CFG_ARG_CHK_EN > 0u)
if (p_sem == (OS_SEM *)0)
{ /* Validate 'p_sem' */
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
switch (opt)
{ /* Validate '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_sem->Type != OS_OBJ_TYPE_SEM)
{ /* Make sure semaphore was created */
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
// 进入临界区,保护信号量等待列表
CPU_CRITICAL_ENTER();
p_pend_list = &p_sem->PendList;
if (p_pend_list->HeadPtr == (OS_TCB *)0)
{ /* Any task waiting on semaphore? */
CPU_CRITICAL_EXIT(); /* No */
*p_err = OS_ERR_PEND_ABORT_NONE;
return (0u);
}
nbr_tasks = 0u;
// 获取时间戳,为所有被中止的任务提供相同的时间
#if (OS_CFG_TS_EN > 0u)
ts = OS_TS_GET(); /* Get local time stamp so all tasks get the same time */
#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)
{ /* Pend abort all tasks waiting? */
break; /* No */
}
}
CPU_CRITICAL_EXIT();
// 根据选项决定是否进行重新调度
if ((opt & OS_OPT_POST_NO_SCHED) == 0u)
{
OSSched(); /* Run the scheduler */
}
*p_err = OS_ERR_NONE;
return (nbr_tasks);
}
#endif
/*
************************************************************************************************************************
* 向信号量发送信号
*
* 描述: 此函数向信号量发送信号。
*
* 参数: p_sem 指向信号量的指针
*
* opt 确定执行的发送类型:
*
* OS_OPT_POST_1 发送并仅使最高优先级的等待任务就绪(如果有任务在等待)。
* OS_OPT_POST_ALL 向所有等待信号量的任务发送信号。
*
* OS_OPT_POST_NO_SCHED 不调用调度器
*
* p_err 是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*
* OS_ERR_NONE 调用成功且信号量已发送信号
* OS_ERR_OBJ_PTR_NULL 如果 'p_sem' 是空指针
* OS_ERR_OBJ_TYPE 如果 'p_sem' 不是指向信号量的指针
* OS_ERR_OPT_INVALID 如果指定了无效的选项
* OS_ERR_OS_NOT_RUNNING 如果 uC/OS-III 尚未运行
* OS_ERR_SEM_OVF 如果发送会导致信号量计数溢出
*
* 返回: 信号量计数器的当前值,或在发生错误时返回 0。
*
* 注意: 1) OS_OPT_POST_NO_SCHED 可以与其他选项之一组合使用。
************************************************************************************************************************
*/
// 函数OSSemPost用于释放信号量,并可选地调度下一个任务。
// 参数p_sem指向信号量控制块,opt指定释放选项,p_err返回错误代码。
// 返回值是信号量计数器的当前值。
OS_SEM_CTR OSSemPost(OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err)
{
OS_SEM_CTR ctr;
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 (0u);
}
#endif
// 追踪:进入信号量发布
OS_TRACE_SEM_POST_ENTER(p_sem, opt);
// 确保内核正在运行
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
if (OSRunning != OS_STATE_OS_RUNNING)
{ /* Is the kernel running? */
OS_TRACE_SEM_POST_EXIT(OS_ERR_OS_NOT_RUNNING);
*p_err = OS_ERR_OS_NOT_RUNNING;
return (0u);
}
#endif
// 参数有效性检查:p_sem非空且opt为有效值
#if (OS_CFG_ARG_CHK_EN > 0u)
if (p_sem == (OS_SEM *)0)
{ /* Validate 'p_sem' */
OS_TRACE_SEM_POST_FAILED(p_sem);
OS_TRACE_SEM_POST_EXIT(OS_ERR_OBJ_PTR_NULL);
*p_err = OS_ERR_OBJ_PTR_NULL;
return (0u);
}
switch (opt)
{ /* Validate 'opt' */
case OS_OPT_POST_1:
case OS_OPT_POST_ALL:
case OS_OPT_POST_1 | OS_OPT_POST_NO_SCHED:
case OS_OPT_POST_ALL | OS_OPT_POST_NO_SCHED:
break;
default:
OS_TRACE_SEM_POST_FAILED(p_sem);
OS_TRACE_SEM_POST_EXIT(OS_ERR_OPT_INVALID);
*p_err = OS_ERR_OPT_INVALID;
return (0u);
}
#endif
// 对象类型检查:确保p_sem是信号量类型
#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
if (p_sem->Type != OS_OBJ_TYPE_SEM)
{ /* Make sure semaphore was created */
OS_TRACE_SEM_POST_FAILED(p_sem);
OS_TRACE_SEM_POST_EXIT(OS_ERR_OBJ_TYPE);
*p_err = OS_ERR_OBJ_TYPE;
return (0u);
}
#endif
// 获取时间戳(如果配置支持)
#if (OS_CFG_TS_EN > 0u)
ts = OS_TS_GET(); /* Get timestamp */
#else
ts = 0u;
#endif
// 追踪:信号量发布
OS_TRACE_SEM_POST(p_sem);
CPU_CRITICAL_ENTER();
p_pend_list = &p_sem->PendList;
if (p_pend_list->HeadPtr == (OS_TCB *)0)
{ /* Any task waiting on semaphore? */
if (p_sem->Ctr == (OS_SEM_CTR)-1)
{
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_SEM_OVF;
OS_TRACE_SEM_POST_EXIT(*p_err);
return (0u);
}
p_sem->Ctr++; /* No */
ctr = p_sem->Ctr;
#if (OS_CFG_TS_EN > 0u)
p_sem->TS = ts; /* Save timestamp in semaphore control block */
#endif
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
OS_TRACE_SEM_POST_EXIT(*p_err);
return (ctr);
}
// 遍历等待信号量的任务列表并解除阻塞
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_sem),
p_tcb,
(void *)0,
0u,
ts);
if ((opt & OS_OPT_POST_ALL) == 0u)
{ /* Post to all tasks waiting? */
break; /* No */
}
p_tcb = p_tcb_next;
}
CPU_CRITICAL_EXIT();
if ((opt & OS_OPT_POST_NO_SCHED) == 0u)
{
OSSched(); /* Run the scheduler */
}
*p_err = OS_ERR_NONE;
// 追踪:退出信号量发布
OS_TRACE_SEM_POST_EXIT(*p_err);
return (0u);
}
/*
************************************************************************************************************************
* 设置信号量
*
* 描述: 此函数将信号量计数设置为作为参数指定的值。通常,这个值为 0,但当然,我们可以将信号量设置为任何值。
*
* 当信号量用作信号机制时,您通常会使用此函数来重置计数值。
*
* 参数: p_sem 指向信号量的指针
*
* cnt 是信号量计数的新值。要重置信号量计数,可以传递 0。
*
* p_err 是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*
* OS_ERR_NONE 调用成功且信号量值已设置
* OS_ERR_OBJ_PTR_NULL 如果 'p_sem' 是空指针
* OS_ERR_OBJ_TYPE 如果 'p_sem' 不是指向信号量的指针
* OS_ERR_SET_ISR 如果从 ISR 中调用
* OS_ERR_TASK_WAITING 如果有任务在等待信号量
*
* 返回: 无
*
* 注意: 无
************************************************************************************************************************
*/
#if (OS_CFG_SEM_SET_EN > 0u)
// 设置信号量的值
// 参数 p_sem 指向信号量控制块
// 参数 cnt 是要设置的信号量计数器值
// 参数 p_err 指向错误代码变量
void OSSemSet(OS_SEM *p_sem,
OS_SEM_CTR cnt,
OS_ERR *p_err)
{
// 指向挂起列表的指针
OS_PEND_LIST *p_pend_list;
// 分配CPU状态寄存器
CPU_SR_ALLOC();
// 安全关键任务检查,确保p_err不为空
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0)
{
// 如果p_err为空,则调用安全关键异常处理函数
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
// 检查是否在中断服务程序中调用此函数
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
if (OSIntNestingCtr > 0u)
{ // 如果在中断服务程序中,则返回错误
*p_err = OS_ERR_SET_ISR;
return;
}
#endif
// 参数检查,确保p_sem不为空
#if (OS_CFG_ARG_CHK_EN > 0u)
if (p_sem == (OS_SEM *)0)
{ // 如果p_sem为空,则返回错误
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif
// 对象类型检查,确保信号量已创建
#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
if (p_sem->Type != OS_OBJ_TYPE_SEM)
{ // 如果信号量未创建,则返回错误
*p_err = OS_ERR_OBJ_TYPE;
return;
}
#endif
// 初始化错误代码为无错误
*p_err = OS_ERR_NONE;
// 进入临界区
CPU_CRITICAL_ENTER();
// 检查信号量计数器是否大于0
if (p_sem->Ctr > 0u)
{ // 如果信号量已经有计数,则直接设置为新的计数值
p_sem->Ctr = cnt;
}
else
{
// 获取挂起列表指针
p_pend_list = &p_sem->PendList;
// 检查是否有任务在等待此信号量
if (p_pend_list->HeadPtr == (OS_TCB *)0)
{ // 如果没有任务等待,则设置信号量值
p_sem->Ctr = cnt;
}
else
{
// 如果有任务在等待,则返回错误
*p_err = OS_ERR_TASK_WAITING;
}
}
// 退出临界区
CPU_CRITICAL_EXIT();
}
#endif
/*
************************************************************************************************************************
* 清除信号量的内容
*
* 描述: 此函数由OSSemDel()调用,用于清除信号量的内容
*
* 参数: p_sem 指向要清除的信号量的指针
* -----
*
* 返回: 无
*
* 注意: 1) 此函数是uC/OS-III的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
// 清除信号量函数
// 该函数用于初始化或清除一个信号量,将其计数器设置为0,并执行相关初始化操作。
// 参数: p_sem - 指向要清除的信号量的指针,不能为空。
void OS_SemClr(OS_SEM *p_sem)
{
// 根据配置可选地将数据结构类型设置为NONE,用于对象类型检查。
#if (OS_OBJ_TYPE_REQ > 0u)
p_sem->Type = OS_OBJ_TYPE_NONE;
#endif
// 将信号量的计数值设置为0。
p_sem->Ctr = 0u;
// 根据配置可选地清除时间戳。
#if (OS_CFG_TS_EN > 0u)
p_sem->TS = 0u;
#endif
// 根据配置可选地将信号量名称设置为默认值。
#if (OS_CFG_DBG_EN > 0u)
p_sem->NamePtr = (CPU_CHAR *)((void *)"?SEM");
#endif
// 初始化等待列表。
OS_PendListInit(&p_sem->PendList);
}
/*
************************************************************************************************************************
* 将信号量添加/移除到调试列表
*
* 描述: 这些函数由 uC/OS-III 调用,用于将信号量添加或移除到调试列表。
*
* 参数: p_sem 指向要添加或移除的信号量的指针
*
* 返回: 无
*
* 注意: 这些函数是 uC/OS-III 的内部函数,您的应用程序不应调用它们。
************************************************************************************************************************
*/
#if (OS_CFG_DBG_EN > 0u)
void OS_SemDbgListAdd(OS_SEM *p_sem)
{
p_sem->DbgNamePtr = (CPU_CHAR *)((void *)" ");
p_sem->DbgPrevPtr = (OS_SEM *)0;
if (OSSemDbgListPtr == (OS_SEM *)0)
{
p_sem->DbgNextPtr = (OS_SEM *)0;
}
else
{
p_sem->DbgNextPtr = OSSemDbgListPtr;
OSSemDbgListPtr->DbgPrevPtr = p_sem;
}
OSSemDbgListPtr = p_sem;
}
void OS_SemDbgListRemove(OS_SEM *p_sem)
{
OS_SEM *p_sem_next;
OS_SEM *p_sem_prev;
p_sem_prev = p_sem->DbgPrevPtr;
p_sem_next = p_sem->DbgNextPtr;
if (p_sem_prev == (OS_SEM *)0)
{
OSSemDbgListPtr = p_sem_next;
if (p_sem_next != (OS_SEM *)0)
{
p_sem_next->DbgPrevPtr = (OS_SEM *)0;
}
p_sem->DbgNextPtr = (OS_SEM *)0;
}
else if (p_sem_next == (OS_SEM *)0)
{
p_sem_prev->DbgNextPtr = (OS_SEM *)0;
p_sem->DbgPrevPtr = (OS_SEM *)0;
}
else
{
p_sem_prev->DbgNextPtr = p_sem_next;
p_sem_next->DbgPrevPtr = p_sem_prev;
p_sem->DbgNextPtr = (OS_SEM *)0;
p_sem->DbgPrevPtr = (OS_SEM *)0;
}
}
#endif
#endif