/*
*********************************************************************************************************
* 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_core.c
* 版本 : V3.08.02
*********************************************************************************************************
*/
#define MICRIUM_SOURCE
#include "os.h"
#ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
const CPU_CHAR *os_core__c = "$Id: $";
#endif
/*
************************************************************************************************************************
* 初始化
*
* 描述: 此函数用于初始化 uC/OS-III 的内部,并且必须在创建任何 uC/OS-III 对象之前以及调用 OSStart() 之前调用。
*
* 参数: p_err 是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*
* OS_ERR_NONE 初始化成功
* 其他 其他 OS_ERR_xxx,具体取决于 OSInit() 调用的子函数。
* 返回值: 无
************************************************************************************************************************
*/
void OSInit (OS_ERR *p_err)
{
#if (OS_CFG_ISR_STK_SIZE > 0u)
CPU_STK *p_stk;
CPU_STK_SIZE size;
#endif
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) { // 检查错误指针是否为 NULL
OS_SAFETY_CRITICAL_EXCEPTION(); // 安全关键异常处理
return;
}
#endif
OSInitHook(); // 调用特定于端口的初始化代码
OSIntNestingCtr = 0u; // 清除中断嵌套计数器
OSRunning = OS_STATE_OS_STOPPED; // 表示多任务尚未开始
OSSchedLockNestingCtr = 0u; // 清除调度锁定计数器
OSTCBCurPtr = (OS_TCB *)0; // 初始化 OS_TCB 指针到已知状态
OSTCBHighRdyPtr = (OS_TCB *)0;
OSPrioCur = 0u; // 初始化优先级变量到已知状态
OSPrioHighRdy = 0u;
#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)
OSSchedLockTimeBegin = 0u;
OSSchedLockTimeMax = 0u;
OSSchedLockTimeMaxCur = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
OSSafetyCriticalStartFlag = OS_FALSE;
#endif
#if (OS_CFG_SCHED_ROUND_ROBIN_EN > 0u)
OSSchedRoundRobinEn = OS_FALSE;
OSSchedRoundRobinDfltTimeQuanta = OSCfg_TickRate_Hz / 10u;
#endif
#if (OS_CFG_ISR_STK_SIZE > 0u)
p_stk = OSCfg_ISRStkBasePtr; // 清除异常堆栈以进行堆栈检查
if (p_stk != (CPU_STK *)0) {
size = OSCfg_ISRStkSize;
while (size > 0u) {
size--;
*p_stk = 0u;
p_stk++;
}
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u) // 初始化带红区的 ISR 堆栈
OS_TaskStkRedzoneInit(OSCfg_ISRStkBasePtr, OSCfg_ISRStkSize);
#endif
}
#endif
#if (OS_CFG_APP_HOOKS_EN > 0u) // 清除应用钩子指针
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
OS_AppRedzoneHitHookPtr = (OS_APP_HOOK_TCB)0;
#endif
OS_AppTaskCreateHookPtr = (OS_APP_HOOK_TCB)0;
OS_AppTaskDelHookPtr = (OS_APP_HOOK_TCB)0;
OS_AppTaskReturnHookPtr = (OS_APP_HOOK_TCB)0;
OS_AppIdleTaskHookPtr = (OS_APP_HOOK_VOID)0;
OS_AppStatTaskHookPtr = (OS_APP_HOOK_VOID)0;
OS_AppTaskSwHookPtr = (OS_APP_HOOK_VOID)0;
OS_AppTimeTickHookPtr = (OS_APP_HOOK_VOID)0;
#endif
#if (OS_CFG_TASK_REG_TBL_SIZE > 0u)
OSTaskRegNextAvailID = 0u;
#endif
OS_PrioInit(); // 初始化优先级位图表
OS_RdyListInit(); // 初始化就绪列表
#if (OS_CFG_FLAG_EN > 0u) // 初始化事件标志模块
#if (OS_CFG_DBG_EN > 0u)
OSFlagDbgListPtr = (OS_FLAG_GRP *)0;
OSFlagQty = 0u;
#endif
#endif
#if (OS_CFG_MEM_EN > 0u) // 初始化内存管理模块
OS_MemInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if (OS_MSG_EN > 0u) // 初始化 OS_MSG 的空闲列表
OS_MsgPoolInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if (OS_CFG_MUTEX_EN > 0u) // 初始化互斥锁管理模块
#if (OS_CFG_DBG_EN > 0u)
OSMutexDbgListPtr = (OS_MUTEX *)0;
OSMutexQty = 0u;
#endif
#endif
#if (OS_CFG_Q_EN > 0u) // 初始化消息队列管理模块
#if (OS_CFG_DBG_EN > 0u)
OSQDbgListPtr = (OS_Q *)0;
OSQQty = 0u;
#endif
#endif
#if (OS_CFG_SEM_EN > 0u) // 初始化信号量管理模块
#if (OS_CFG_DBG_EN > 0u)
OSSemDbgListPtr = (OS_SEM *)0;
OSSemQty = 0u;
#endif
#endif
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS_Init(p_err); // 初始化任务本地存储,创建任务前
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
OS_TaskInit(p_err); // 初始化任务管理器
if (*p_err != OS_ERR_NONE) {
return;
}
#if (OS_CFG_TASK_IDLE_EN > 0u)
OS_IdleTaskInit(p_err); // 初始化空闲任务
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if (OS_CFG_TICK_EN > 0u)
OS_TickInit(p_err); // 初始化系统节拍
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if (OS_CFG_STAT_TASK_EN > 0u) // 初始化统计任务
OS_StatTaskInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if (OS_CFG_TMR_EN > 0u) // 初始化定时器管理模块
OS_TmrInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if (OS_CFG_DBG_EN > 0u)
OS_Dbg_Init();
#endif
OSCfg_Init();
OSInitialized = OS_TRUE; // 内核已初始化
}
/*
************************************************************************************************************************
* 进入中断服务例程
*
* 描述: 此函数用于通知 uC/OS-III 即将处理一个中断服务例程 (ISR)。这允许 uC/OS-III 跟踪中断嵌套,从而仅在最后一个嵌套的 ISR 中执行重新调度。
*
* 参数: 无
*
* 返回值: 无
*
* 注意事项:
* 1) 必须在已经禁用中断的情况下调用此函数。
*
* 2) 你的 ISR 可以直接递增 'OSIntNestingCtr' 而不调用此函数,因为 OSIntNestingCtr 已被声明为全局变量,端口实际上被视为操作系统的一部分,因此可以访问 uC/OS-III 变量。
*
* 3) 即使你直接递增 'OSIntNestingCtr',也必须调用 OSIntExit()。
*
* 4) 必须成对调用 OSIntEnter() 和 OSIntExit()。换句话说,对于 ISR 开始时的每次 OSIntEnter() 调用(或直接递增 OSIntNestingCtr),在 ISR 结束时必须有一个 OSIntExit() 调用。
*
* 5) 允许中断嵌套最多 250 层。
************************************************************************************************************************
*/
void OSIntEnter (void)
{
OS_TRACE_ISR_ENTER(); // 记录 ISR 进入事件
if (OSRunning != OS_STATE_OS_RUNNING) { // 操作系统是否正在运行?
return; // 否
}
if (OSIntNestingCtr >= 250u) { // 是否已经嵌套超过 250 层?
return; // 是
}
OSIntNestingCtr++; // 增加 ISR 嵌套级别
}
/*
************************************************************************************************************************
* 退出中断服务例程
*
* 描述: 此函数用于通知 uC/OS-III 已经完成了一个中断服务例程 (ISR) 的处理。当最后一个嵌套的 ISR 完成时,uC/OS-III 将调用调度器来确定是否有新的高优先级任务准备运行。
*
* 参数: 无
*
* 返回值: 无
*
* 注意事项:
* 1) 必须成对调用 OSIntEnter() 和 OSIntExit()。换句话说,对于 ISR 开始时的每次 OSIntEnter() 调用(或直接递增 OSIntNestingCtr),在 ISR 结束时必须有一个 OSIntExit() 调用。
* 2) 当调度器被锁定时(参见 OSSchedLock()),防止重新调度。
************************************************************************************************************************
*/
void OSIntExit (void)
{
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
CPU_BOOLEAN stk_status;
#endif
CPU_SR_ALLOC();
if (OSRunning != OS_STATE_OS_RUNNING) { // 操作系统是否已启动?
OS_TRACE_ISR_EXIT(); // 记录 ISR 退出事件
return; // 否
}
CPU_INT_DIS(); // 禁用中断
if (OSIntNestingCtr == 0u) { // 防止 OSIntNestingCtr 溢出
OS_TRACE_ISR_EXIT(); // 记录 ISR 退出事件
CPU_INT_EN(); // 启用中断
return;
}
OSIntNestingCtr--; // 减少 ISR 嵌套级别
if (OSIntNestingCtr > 0u) { // 中断是否仍然嵌套?
OS_TRACE_ISR_EXIT(); // 记录 ISR 退出事件
CPU_INT_EN(); // 启用中断
return;
}
if (OSSchedLockNestingCtr > 0u) { // 调度器是否仍被锁定?
OS_TRACE_ISR_EXIT(); // 记录 ISR 退出事件
CPU_INT_EN(); // 启用中断
return;
}
// 验证 ISR 栈
#if (OS_CFG_ISR_STK_SIZE > 0u)
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
stk_status = OS_TaskStkRedzoneChk(OSCfg_ISRStkBasePtr, OSCfg_ISRStkSize); // 检查 ISR 栈红区
if (stk_status != OS_TRUE) {
OSRedzoneHitHook((OS_TCB *)0); // 触发红区命中钩子
}
#endif
#endif
OSPrioHighRdy = OS_PrioGetHighest(); // 获取最高优先级
#if (OS_CFG_TASK_IDLE_EN > 0u)
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; // 获取最高优先级就绪任务
if (OSTCBHighRdyPtr == OSTCBCurPtr) { // 当前任务是否仍然是最高优先级?
// 是
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
stk_status = OSTaskStkRedzoneChk((OS_TCB *)0); // 检查当前任务栈红区
if (stk_status != OS_TRUE) {
OSRedzoneHitHook(OSTCBCurPtr); // 触发红区命中钩子
}
#endif
OS_TRACE_ISR_EXIT(); // 记录 ISR 退出事件
CPU_INT_EN(); // 启用中断
OS_TRACE_TASK_SWITCHED_IN(OSTCBHighRdyPtr); // 记录任务切换
return;
}
#else
if (OSPrioHighRdy != (OS_CFG_PRIO_MAX - 1u)) { // 是否返回空闲任务?
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; // 获取最高优先级就绪任务
if (OSTCBHighRdyPtr == OSTCBCurPtr) { // 当前任务是否仍然是最高优先级?
// 是
OS_TRACE_ISR_EXIT(); // 记录 ISR 退出事件
CPU_INT_EN(); // 启用中断
OS_TRACE_TASK_SWITCHED_IN(OSTCBHighRdyPtr); // 记录任务切换
return;
}
}
#endif
#if (OS_CFG_TASK_PROFILE_EN > 0u)
OSTCBHighRdyPtr->CtxSwCtr++; // 增加新任务的上下文切换计数
#endif
#if ((OS_CFG_TASK_PROFILE_EN > 0u) || (OS_CFG_DBG_EN > 0u))
OSTaskCtxSwCtr++; // 跟踪总的上下文切换次数
#endif
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS_TaskSw(); // 切换线程局部存储
#endif
OS_TRACE_ISR_EXIT_TO_SCHEDULER(); // 记录 ISR 退出到调度器事件
OSIntCtxSw(); // 执行中断级别的上下文切换
CPU_INT_EN(); // 启用中断
}
/*
************************************************************************************************************************
* 表示不再安全创建对象
*
* 描述: 此函数由应用程序代码调用,表示所有初始化已完成,不再允许创建内核对象。
*
* 参数: 无
*
* 返回值: 无
*
* 注意事项: 无
************************************************************************************************************************
*/
#ifdef OS_SAFETY_CRITICAL_IEC61508
void OSSafetyCriticalStart (void)
{
OSSafetyCriticalStartFlag = OS_TRUE;
}
#endif
/*
************************************************************************************************************************
* 调度器
*
* 描述: 此函数由其他 uC/OS-III 服务调用,用于确定是否有新的高优先级任务已准备好运行。此函数由任务级代码调用,不用于从 ISR 中重新调度任务(ISR 重新调度请参见 OSIntExit())。
*
* 参数: 无
*
* 返回值: 无
*
* 注意事项:
* 1) 当调度器被锁定时(参见 OSSchedLock()),防止重新调度。
************************************************************************************************************************
*/
void OSSched (void)
{
CPU_SR_ALLOC();
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u) // 当操作系统未运行时不能调度
if (OSRunning != OS_STATE_OS_RUNNING) {
return;
}
#endif
if (OSIntNestingCtr > 0u) { // 中断是否仍然嵌套?
return; // 是 ... 只有在没有嵌套中断时才进行调度
}
if (OSSchedLockNestingCtr > 0u) { // 调度器是否被锁定?
return; // 是
}
CPU_INT_DIS(); // 禁用中断
OSPrioHighRdy = OS_PrioGetHighest(); // 获取最高优先级就绪任务
#if (OS_CFG_TASK_IDLE_EN > 0u)
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; // 获取最高优先级就绪任务
if (OSTCBHighRdyPtr == OSTCBCurPtr) { // 当前任务是否仍然是最高优先级?
CPU_INT_EN(); // 是
return;
}
#else
if (OSPrioHighRdy != (OS_CFG_PRIO_MAX - 1u)) { // 是否返回空闲任务?
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; // 获取最高优先级就绪任务
if (OSTCBHighRdyPtr == OSTCBCurPtr) { // 当前任务是否仍然是最高优先级?
CPU_INT_EN(); // 是
return;
}
}
#endif
OS_TRACE_TASK_PREEMPT(OSTCBCurPtr); // 记录任务抢占
#if (OS_CFG_TASK_PROFILE_EN > 0u)
OSTCBHighRdyPtr->CtxSwCtr++; // 增加新任务的上下文切换计数
#endif
#if ((OS_CFG_TASK_PROFILE_EN > 0u) || (OS_CFG_DBG_EN > 0u))
OSTaskCtxSwCtr++; // 增加上下文切换计数器
#endif
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS_TaskSw(); // 切换线程局部存储
#endif
#if (OS_CFG_TASK_IDLE_EN > 0u)
OS_TASK_SW(); // 执行任务级别的上下文切换
CPU_INT_EN(); // 启用中断
#else
if ((OSPrioHighRdy != (OS_CFG_PRIO_MAX - 1u))) {
OS_TASK_SW(); // 执行任务级别的上下文切换
CPU_INT_EN(); // 启用中断
} else {
OSTCBHighRdyPtr = OSTCBCurPtr;
CPU_INT_EN(); // 启用中断
for (;;) {
#if ((OS_CFG_DBG_EN > 0u) || (OS_CFG_STAT_TASK_EN > 0u))
CPU_CRITICAL_ENTER(); // 进入临界区
#if (OS_CFG_DBG_EN > 0u)
OSIdleTaskCtr++; // 增加空闲任务计数器
#endif
#if (OS_CFG_STAT_TASK_EN > 0u)
OSStatTaskCtr++; // 增加统计任务计数器
#endif
CPU_CRITICAL_EXIT(); // 退出临界区
#endif
#if (OS_CFG_APP_HOOKS_EN > 0u)
OSIdleTaskHook(); // 调用用户定义的空闲任务钩子
#endif
if ((*((volatile OS_PRIO *)&OSPrioHighRdy) != (OS_CFG_PRIO_MAX - 1u))) {
break; // 如果有更高优先级的任务就绪,跳出循环
}
}
}
#endif
#ifdef OS_TASK_SW_SYNC
OS_TASK_SW_SYNC(); // 同步任务切换
#endif
}
/*
************************************************************************************************************************
* 防止调度
*
* 描述: 此函数用于防止重新调度的发生。这允许您的应用程序在准备好允许上下文切换之前防止上下文切换。
*
* 参数:
* p_err 是一个指向变量的指针,该变量将接收错误代码:
*
* OS_ERR_NONE 调度器已锁定
* OS_ERR_LOCK_NESTING_OVF 如果尝试嵌套调用此函数超过 250 层
* OS_ERR_OS_NOT_RUNNING 如果 uC/OS-III 尚未运行
* OS_ERR_SCHED_LOCK_ISR 如果从 ISR 中调用此函数
*
* 返回值: 无
*
* 注意事项:
* 1) 您必须成对调用 OSSchedLock() 和 OSSchedUnlock()。换句话说,对于每次调用 OSSchedLock(),您必须有一次调用 OSSchedUnlock()。
************************************************************************************************************************
*/
void OSSchedLock (OS_ERR *p_err)
{
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
if (OSIntNestingCtr > 0u) { // 不允许从 ISR 中调用
*p_err = OS_ERR_SCHED_LOCK_ISR;
return;
}
#endif
if (OSRunning != OS_STATE_OS_RUNNING) { // 确保多任务正在运行
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
if (OSSchedLockNestingCtr >= 250u) { // 防止 OSSchedLockNestingCtr 溢出
*p_err = OS_ERR_LOCK_NESTING_OVF;
return;
}
CPU_CRITICAL_ENTER(); // 进入临界区
OSSchedLockNestingCtr++; // 增加锁嵌套级别
#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)
OS_SchedLockTimeMeasStart(); // 开始测量调度锁时间
#endif
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE; // 设置错误代码为无错误
}
/*
************************************************************************************************************************
* 启用调度
*
* 描述: 此函数用于重新允许重新调度。
*
* 参数:
* p_err 是一个指向变量的指针,该变量将接收此函数返回的错误代码:
*
* OS_ERR_NONE 调度器已启用
* OS_ERR_OS_NOT_RUNNING 如果 uC/OS-III 尚未运行
* OS_ERR_SCHED_LOCKED 调度器仍被锁定,嵌套层数未减少到零
* OS_ERR_SCHED_NOT_LOCKED 调度器未被锁定
* OS_ERR_SCHED_UNLOCK_ISR 如果从 ISR 中调用此函数
*
* 返回值: 无
*
* 注意事项:
* 1) 您必须成对调用 OSSchedLock() 和 OSSchedUnlock()。换句话说,对于每次调用 OSSchedLock(),您必须有一次调用 OSSchedUnlock()。
************************************************************************************************************************
*/
void OSSchedUnlock (OS_ERR *p_err)
{
CPU_SR_ALLOC(); // 分配CPU状态寄存器
// 安全关键代码检查
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION(); // 触发安全关键异常
return;
}
#endif
// 检查是否从中断服务例程中调用
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
if (OSIntNestingCtr > 0u) { // 不允许从中断服务例程中调用
*p_err = OS_ERR_SCHED_UNLOCK_ISR;
return;
}
#endif
// 确保多任务正在运行
if (OSRunning != OS_STATE_OS_RUNNING) {
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
// 检查调度器是否已锁定
if (OSSchedLockNestingCtr == 0u) {
*p_err = OS_ERR_SCHED_NOT_LOCKED;
return;
}
CPU_CRITICAL_ENTER(); // 进入临界区
OSSchedLockNestingCtr--; // 递减锁定嵌套级别
if (OSSchedLockNestingCtr > 0u) {
CPU_CRITICAL_EXIT(); // 调度器仍处于锁定状态
*p_err = OS_ERR_SCHED_LOCKED;
return;
}
// 停止调度器锁定时间测量
#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)
OS_SchedLockTimeMeasStop();
#endif
CPU_CRITICAL_EXIT(); // 退出临界区,重新启用调度器
OSSched(); // 运行调度器
*p_err = OS_ERR_NONE; // 设置无错误
}
/*
************************************************************************************************************************
* 配置轮询调度参数
*
* 描述: 此函数用于更改轮询调度参数。
*
* 参数 : en 确定是否启用轮询调度(当为 OS_TRUE 时启用,当为 OS_FALSE 时不启用)
*
* dflt_time_quanta 默认的时间片间隔,以滴答数表示。0 表示 OSCfg_TickRate_Hz / 10。
*
* p_err 指向一个变量的指针,该变量将包含此函数返回的错误代码。
*
* OS_ERR_NONE 调用成功
*
* 返回值: 无
*
* 注意事项: 无
************************************************************************************************************************
*/
#if (OS_CFG_SCHED_ROUND_ROBIN_EN > 0u)
void OSSchedRoundRobinCfg (CPU_BOOLEAN en, OS_TICK dflt_time_quanta, OS_ERR *p_err)
{
CPU_SR_ALLOC(); // 分配CPU状态寄存器
// 安全关键代码检查
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION(); // 触发安全关键异常
return;
}
#endif
CPU_CRITICAL_ENTER(); // 进入临界区
if (en == 0u) {
OSSchedRoundRobinEn = OS_FALSE; // 禁用轮询调度
} else {
OSSchedRoundRobinEn = OS_TRUE; // 启用轮询调度
}
if (dflt_time_quanta > 0u) {
OSSchedRoundRobinDfltTimeQuanta = dflt_time_quanta; // 设置默认时间片间隔
} else {
OSSchedRoundRobinDfltTimeQuanta = (OS_TICK)(OSCfg_TickRate_Hz / 10u); // 默认时间片间隔为 OSCfg_TickRate_Hz / 10
}
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE; // 设置无错误
}
#endif
/*
************************************************************************************************************************
* 当任务不再需要时间片时让出CPU
*
* 描述: 此函数在任务在其时间片结束前完成执行时调用,以让出CPU。
*
* 参数: p_err 指向一个变量的指针,该变量将包含此函数返回的错误代码。
*
* OS_ERR_NONE 调用成功
* OS_ERR_ROUND_ROBIN_1 在此优先级下只有一个任务,没有可让出的对象
* OS_ERR_ROUND_ROBIN_DISABLED 轮询调度未启用
* OS_ERR_SCHED_LOCKED 调度器已被锁定
* OS_ERR_YIELD_ISR 不能从中断服务例程中调用
*
* 返回值: 无
*
* 注意事项: 1) 此函数必须从任务中调用。
************************************************************************************************************************
*/
#if (OS_CFG_SCHED_ROUND_ROBIN_EN > 0u)
void OSSchedRoundRobinYield (OS_ERR *p_err)
{
OS_RDY_LIST *p_rdy_list;
OS_TCB *p_tcb;
CPU_SR_ALLOC(); // 分配CPU状态寄存器
// 安全关键代码检查
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION(); // 触发安全关键异常
return;
}
#endif
// 检查是否从中断服务例程中调用
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
if (OSIntNestingCtr > 0u) { // 不能从中断服务例程中调用
*p_err = OS_ERR_YIELD_ISR;
return;
}
#endif
// 检查调度器是否被锁定
if (OSSchedLockNestingCtr > 0u) { // 如果调度器被锁定,则不能让出
*p_err = OS_ERR_SCHED_LOCKED;
return;
}
// 确保轮询调度已启用
if (OSSchedRoundRobinEn != OS_TRUE) { // 确保轮询调度已启用
*p_err = OS_ERR_ROUND_ROBIN_DISABLED;
return;
}
CPU_CRITICAL_ENTER(); // 进入临界区
p_rdy_list = &OSRdyList[OSPrioCur]; // 获取当前优先级的就绪列表
if (p_rdy_list->HeadPtr == p_rdy_list->TailPtr) { // 如果在此优先级下只有一个任务,则不能让出
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_ROUND_ROBIN_1;
return;
}
OS_RdyListMoveHeadToTail(p_rdy_list); // 将当前任务移到就绪列表的末尾
p_tcb = p_rdy_list->HeadPtr; // 获取新的头部任务
if (p_tcb->TimeQuanta == 0u) { // 如果需要使用默认时间片
p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;
} else {
p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; // 加载新的时间片计数器
}
CPU_CRITICAL_EXIT(); // 退出临界区
OSSched(); // 运行新任务
*p_err = OS_ERR_NONE; // 设置无错误
}
#endif
/*
************************************************************************************************************************
* 启动多任务
*
* 描述: 此函数用于启动多任务处理过程,使 uC/OS-III 管理你创建的任务。在调用 OSStart() 之前,你必须已经调用了 OSInit() 并且至少创建了一个应用程序任务。
*
* 参数: p_err 指向一个变量的指针,该变量将包含此函数返回的错误代码。
*
* OS_ERR_FATAL_RETURN 操作系统正在运行且 OSStart() 返回
* OS_ERR_OS_NOT_INIT 操作系统未初始化,OSStart() 无效
* OS_ERR_OS_NO_APP_TASK 未创建应用程序任务,OSStart() 无效
* OS_ERR_OS_RUNNING 操作系统已经在运行,OSStart() 无效
*
* 返回值: 无
*
* 注意事项: 1) OSStartHighRdy() 必须:
* a) 调用 OSTaskSwHook(),然后,
* b) 加载由 OSTCBHighRdyPtr 指向的任务的上下文。
* c) 执行任务。
*
* 2) OSStart() 不应该返回。如果它返回了,这将被视为致命错误。
************************************************************************************************************************
*/
void OSStart(OS_ERR *p_err)
{
OS_OBJ_QTY kernel_task_cnt; // 内核任务数量
// 安全关键模式下,检查错误指针是否为空
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
// 检查操作系统是否已初始化
if (OSInitialized != OS_TRUE) {
*p_err = OS_ERR_OS_NOT_INIT;
return;
}
// 计算内核任务的数量
kernel_task_cnt = 0u;
#if (OS_CFG_STAT_TASK_EN > 0u)
kernel_task_cnt++;
#endif
#if (OS_CFG_TMR_EN > 0u)
kernel_task_cnt++;
#endif
#if (OS_CFG_TASK_IDLE_EN > 0u)
kernel_task_cnt++;
#endif
// 检查是否有应用任务被创建
if (OSTaskQty <= kernel_task_cnt) {
*p_err = OS_ERR_OS_NO_APP_TASK;
return;
}
// 检查操作系统是否处于停止状态
if (OSRunning == OS_STATE_OS_STOPPED) {
OSPrioHighRdy = OS_PrioGetHighest(); // 找到最高优先级
OSPrioCur = OSPrioHighRdy;
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;
OSTCBCurPtr = OSTCBHighRdyPtr;
OSRunning = OS_STATE_OS_RUNNING;
OSStartHighRdy(); // 执行特定于目标的代码以启动任务
*p_err = OS_ERR_FATAL_RETURN; // OSStart() 不应该返回
} else {
*p_err = OS_ERR_OS_RUNNING; // 操作系统已经在运行
}
}
/*
************************************************************************************************************************
* 获取版本号
*
* 描述: 此函数用于返回 uC/OS-III 的版本号。返回的值对应于 uC/OS-III 版本号乘以 10000。换句话说,版本 3.01.02 将返回为 30102。
*
* 参数: p_err 是一个指向变量的指针,该变量将接收错误代码。然而,OSVersion() 函数将此变量设置为
*
* OS_ERR_NONE
*
* 返回值: uC/OS-III 的版本号乘以 10000。
*
* 注意: 无
************************************************************************************************************************
*/
CPU_INT16U OSVersion (OS_ERR *p_err)
{
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
*p_err = OS_ERR_NONE;
return (OS_VERSION);
}
/*
************************************************************************************************************************
* 空闲任务
*
* 描述: 此任务是 uC/OS-III 的内部任务,当没有其他更高优先级的任务执行时(因为它们都在等待事件发生)时,此任务将执行。
*
* 参数: p_arg 是在任务创建时传递给任务的参数。
*
* 返回值: 无
*
* 注意:
* 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
*
* 2) OSIdleTaskHook() 在临界区之后调用,以确保中断至少在几条指令中是启用的。在某些处理器上(例如 Philips XA),启用和禁用中断的时间不足以让处理器在再次禁用之前真正启用中断。因此,uC/OS-III 可能无法识别中断。
*
* 3) 添加此钩子是为了允许您执行诸如停止 CPU 以节省电力的操作。
************************************************************************************************************************
*/
#if (OS_CFG_TASK_IDLE_EN > 0u)
void OS_IdleTask(void *p_arg)
{
#if ((OS_CFG_DBG_EN > 0u) || (OS_CFG_STAT_TASK_EN > 0u))
CPU_SR_ALLOC(); // 分配 CPU 状态寄存器
#endif
(void)p_arg; // 防止编译器警告未使用 'p_arg'
for (;;) {
#if ((OS_CFG_DBG_EN > 0u) || (OS_CFG_STAT_TASK_EN > 0u))
CPU_CRITICAL_ENTER(); // 进入临界区
#if (OS_CFG_DBG_EN > 0u)
OSIdleTaskCtr++; // 增加空闲任务计数器
#endif
#if (OS_CFG_STAT_TASK_EN > 0u)
OSStatTaskCtr++; // 增加统计任务计数器
#endif
CPU_CRITICAL_EXIT(); // 退出临界区
#endif
#if (OS_CFG_APP_HOOKS_EN > 0u)
OSIdleTaskHook(); // 调用用户定义的钩子函数
#endif
}
}
#endif
/*
************************************************************************************************************************
* 初始化空闲任务
*
* 描述: 此函数用于初始化空闲任务。
*
* 参数: p_err 是一个指向变量的指针,该变量将包含此函数返回的错误代码。
*
* 返回值: 无
*
* 注意:
* 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
#if (OS_CFG_TASK_IDLE_EN > 0u)
void OS_IdleTaskInit(OS_ERR *p_err)
{
#if (OS_CFG_DBG_EN > 0u)
OSIdleTaskCtr = 0u; // 初始化空闲任务计数器
#endif
// 创建空闲任务
OSTaskCreate(&OSIdleTaskTCB,
#if (OS_CFG_DBG_EN == 0u)
(CPU_CHAR *)0,
#else
(CPU_CHAR *)"uC/OS-III Idle Task", // 任务名称
#endif
OS_IdleTask, // 任务入口函数
(void *)0, // 任务参数
(OS_PRIO)(OS_CFG_PRIO_MAX - 1u), // 任务优先级
OSCfg_IdleTaskStkBasePtr, // 任务栈基地址
OSCfg_IdleTaskStkLimit, // 任务栈限制
OSCfg_IdleTaskStkSize, // 任务栈大小
0u, // 任务选项1
0u, // 任务选项2
(void *)0, // 任务控制块扩展
(OS_OPT_TASK_STK_CHK | (OS_OPT)(OS_OPT_TASK_STK_CLR | OS_OPT_TASK_NO_TLS)), // 任务创建选项
p_err); // 错误代码指针
}
#endif
/*
************************************************************************************************************************
* 阻塞等待事件的任务
*
* 描述: 此函数用于将任务置于阻塞状态,等待某个事件的发生。此函数存在于多个 OSxxxPend() 服务中,因为它们有共同的需求。
*
* 参数 : p_obj 是指向待阻塞对象的指针。如果没有对象用于阻塞,则
* ----- 调用者必须传递一个 NULL 指针。
*
* p_tcb 是将被阻塞的任务。
*
* pending_on 指定任务将等待的内容:
*
* OS_TASK_PEND_ON_FLAG
* OS_TASK_PEND_ON_TASK_Q <- 无对象(等待发送给任务的消息)
* OS_TASK_PEND_ON_MUTEX
* OS_TASK_PEND_ON_COND
* OS_TASK_PEND_ON_Q
* OS_TASK_PEND_ON_SEM
* OS_TASK_PEND_ON_TASK_SEM <- 无对象(等待发送给任务的信号)
*
* timeout 是任务等待事件发生的时间。
*
* 返回值: 无
*
* 注意: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void OS_Pend(OS_PEND_OBJ *p_obj,
OS_TCB *p_tcb,
OS_STATE pending_on,
OS_TICK timeout)
{
OS_PEND_LIST *p_pend_list;
p_tcb->PendOn = pending_on; /* 资源不可用,等待直到可用 */
p_tcb->PendStatus = OS_STATUS_PEND_OK;
OS_TaskBlock(p_tcb, /* 阻塞任务并根据需要将其添加到计时器列表 */
timeout);
if (p_obj != (OS_PEND_OBJ *)0) { /* 如果有待阻塞的对象,将当前任务添加到阻塞列表 */
p_pend_list = &p_obj->PendList; /* 如果有待阻塞的对象,将任务添加到阻塞列表 */
p_tcb->PendObjPtr = p_obj; /* 保存指向待阻塞对象的指针 */
OS_PendListInsertPrio(p_pend_list, /* 按优先级顺序插入阻塞列表 */
p_tcb);
} else {
p_tcb->PendObjPtr = (OS_PEND_OBJ *)0; /* 如果没有待阻塞的对象,清除阻塞对象指针 */
}
#if (OS_CFG_DBG_EN > 0u)
OS_PendDbgNameAdd(p_obj, /* 在调试模式下,添加阻塞对象和任务的调试信息 */
p_tcb);
#endif
}
/*
************************************************************************************************************************
* 取消阻塞
*
* 描述: 此函数由 OSxxxPendAbort() 和 OSxxxDel() 函数调用,用于取消对事件的阻塞。
*
* 参数 : p_tcb 是指向将要取消阻塞的任务的 OS_TCB 指针
* -----
*
* ts 是取消阻塞的时间戳
*
* reason 指示任务是如何被唤醒的:
*
* OS_STATUS_PEND_DEL 阻塞的对象已被删除。
* OS_STATUS_PEND_ABORT 阻塞被取消。
*
* 返回值: 无
*
* 注意: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void OS_PendAbort(OS_TCB *p_tcb,
CPU_TS ts,
OS_STATUS reason)
{
#if (OS_CFG_TS_EN == 0u)
(void)ts; /* 防止编译器警告未使用 'ts' */
#endif
switch (p_tcb->TaskState) {
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_TIMEOUT:
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = (void *)0;
p_tcb->MsgSize = 0u;
#endif
#if (OS_CFG_TS_EN > 0u)
p_tcb->TS = ts;
#endif
OS_PendListRemove(p_tcb); /* 从阻塞列表中移除任务 */
#if (OS_CFG_TICK_EN > 0u)
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT) {
OS_TickListRemove(p_tcb); /* 取消超时 */
}
#endif
OS_RdyListInsert(p_tcb); /* 将任务插入就绪列表 */
p_tcb->TaskState = OS_TASK_STATE_RDY; /* 任务将变为就绪状态 */
p_tcb->PendStatus = reason; /* 指示任务如何变为就绪 */
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* 指示不再阻塞 */
break;
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = (void *)0;
p_tcb->MsgSize = 0u;
#endif
#if (OS_CFG_TS_EN > 0u)
p_tcb->TS = ts;
#endif
OS_PendListRemove(p_tcb); /* 从阻塞列表中移除任务 */
#if (OS_CFG_TICK_EN > 0u)
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED) {
OS_TickListRemove(p_tcb); /* 取消超时 */
}
#endif
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; /* 任务需要保持挂起状态 */
p_tcb->PendStatus = reason; /* 指示任务如何变为就绪 */
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* 指示不再阻塞 */
break;
case OS_TASK_STATE_RDY: /* 当任务处于这些状态时,不能取消阻塞 */
case OS_TASK_STATE_DLY:
case OS_TASK_STATE_SUSPENDED:
case OS_TASK_STATE_DLY_SUSPENDED:
default:
/* 默认情况 */
break;
}
}
/*
************************************************************************************************************************
* 添加/移除阻塞对象和 OS_TCB 的调试名称
*
* 描述: 这些函数用于添加指向对象的 ASCII 名称的指针,以便使用内核感知工具轻松显示。
*
* 参数 : p_obj 是指向待阻塞对象的指针
*
* p_tcb 是指向待阻塞任务的 OS_TCB 指针
*
* 返回值: 无
*
* 注意: 1) 这些函数是 uC/OS-III 的内部函数,您的应用程序不得调用它们。
************************************************************************************************************************
*/
#if (OS_CFG_DBG_EN > 0u)
void OS_PendDbgNameAdd (OS_PEND_OBJ *p_obj,
OS_TCB *p_tcb)
{
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb1;
if (p_obj != (OS_PEND_OBJ *)0) {
p_tcb->DbgNamePtr = p_obj->NamePtr; /* Task pending on this object ... save name in TCB */
p_pend_list = &p_obj->PendList; /* Find name of HP task pending on this object ... */
p_tcb1 = p_pend_list->HeadPtr;
p_obj->DbgNamePtr = p_tcb1->NamePtr; /* ... Save in object */
} else {
switch (p_tcb->PendOn) {
case OS_TASK_PEND_ON_TASK_Q:
p_tcb->DbgNamePtr = (CPU_CHAR *)((void *)"Task Q");
break;
case OS_TASK_PEND_ON_TASK_SEM:
p_tcb->DbgNamePtr = (CPU_CHAR *)((void *)"Task Sem");
break;
default:
p_tcb->DbgNamePtr = (CPU_CHAR *)((void *)" ");
break;
}
}
}
void OS_PendDbgNameRemove (OS_PEND_OBJ *p_obj,
OS_TCB *p_tcb)
{
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb1;
p_tcb->DbgNamePtr = (CPU_CHAR *)((void *)" "); /* Remove name of object pended on for readied task */
if (p_obj != (OS_PEND_OBJ *)0) {
p_pend_list = &p_obj->PendList;
p_tcb1 = p_pend_list->HeadPtr;
if (p_tcb1 != (OS_TCB *)0) { /* Find name of HP task pending on this object ... */
p_obj->DbgNamePtr = p_tcb1->NamePtr; /* ... Save in object */
} else {
p_obj->DbgNamePtr = (CPU_CHAR *)((void *)" "); /* Or no other task is pending on object */
}
}
}
#endif
/*
************************************************************************************************************************
* 更改阻塞列表中等待任务的优先级
*
* 描述: 此函数用于更改阻塞列表中等待任务的位置。采用的策略是将任务从阻塞列表中移除,然后使用其更改后的优先级重新添加。
*
* 参数 : p_tcb 是指向要移动的任务的 TCB 指针
* -----
*
* 返回值: 无
*
* 注意: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
*
* 2) 假设 TCB 的 .Prio 字段中包含新的优先级。
************************************************************************************************************************
*/
void OS_PendListChangePrio(OS_TCB *p_tcb)
{
OS_PEND_LIST *p_pend_list;
OS_PEND_OBJ *p_obj;
p_obj = p_tcb->PendObjPtr; /* 获取指向阻塞列表的指针 */
p_pend_list = &p_obj->PendList;
if (p_pend_list->HeadPtr->PendNextPtr != (OS_TCB *)0) { /* 仅在列表中有多个条目时移动 */
OS_PendListRemove(p_tcb); /* 从当前位置移除条目 */
p_tcb->PendObjPtr = p_obj;
OS_PendListInsertPrio(p_pend_list, /* 将其重新插入列表 */
p_tcb);
}
}
/*
************************************************************************************************************************
* 初始化等待列表
*
* 描述: 此函数用于初始化 OS_PEND_LIST 的字段。
*
* 参数 : p_pend_list 是指向 OS_PEND_LIST 的指针
* -----------
*
* 返回值: 无
*
* 注意: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void OS_PendListInit (OS_PEND_LIST *p_pend_list)
{
p_pend_list->HeadPtr = (OS_TCB *)0;
p_pend_list->TailPtr = (OS_TCB *)0;
#if (OS_CFG_DBG_EN > 0u)
p_pend_list->NbrEntries = 0u;
#endif
}
/*
************************************************************************************************************************
* 在阻塞列表中基于优先级插入任务
*
* 描述: 此函数用于将一个 OS_TCB 条目基于其优先级插入到链表中。最高优先级的任务将被放置在链表的头部。假设 TCB 的 .Prio 字段中包含任务的优先级。
*
* 情况 0: 插入到空列表中。
*
* OS_PEND_LIST
* +---------------+
* | TailPtr |-> 0
* +---------------+
* | HeadPtr |-> 0
* +---------------+
* | NbrEntries=0 |
* +---------------+
*
*
* 情况 1: 在一个 OS_TCB 之前或之后插入
*
* OS_PEND_LIST
* +--------------+ OS_TCB
* | TailPtr |--+---> +--------------+
* +--------------+ | | PendNextPtr |->0
* | HeadPtr |--/ +--------------+
* +--------------+ 0<-| PendPrevPtr |
* | NbrEntries=1 | +--------------+
* +--------------+ | |
* +--------------+
* | |
* +--------------+
*
*
* OS_PEND_LIST
* +--------------+
* | TailPtr |---------------------------------------------------+
* +--------------+ OS_TCB OS_TCB | OS_TCB
* | HeadPtr |------> +--------------+ +--------------+ +-> +--------------+
* +--------------+ | PendNextPtr |<------| PendNextPtr | ...... | PendNextPtr |->0
* | NbrEntries=N | +--------------+ +--------------+ +--------------+
* +--------------+ 0<-| PendPrevPtr |<------| PendPrevPtr | ...... | PendPrevPtr |
* +--------------+ +--------------+ +--------------+
* | | | | | |
* +--------------+ +--------------+ +--------------+
* | | | | | |
* +--------------+ +--------------+ +--------------+
*
* 参数 : p_pend_list 是指向将要插入 OS_TCB 条目的 OS_PEND_LIST 的指针
* -----------
*
* p_tcb 是要插入到列表中的 OS_TCB
* -----
*
* 返回值: 无
*
* 注意: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void OS_PendListInsertPrio(OS_PEND_LIST *p_pend_list, OS_TCB *p_tcb)
{
OS_PRIO prio;
OS_TCB *p_tcb_next;
prio = p_tcb->Prio; /* 获取要插入任务的优先级 */
if (p_pend_list->HeadPtr == (OS_TCB *)0) { /* 情况 0: 列表为空时插入 */
#if (OS_CFG_DBG_EN > 0u)
p_pend_list->NbrEntries = 1u; /* 这是第一个条目 */
#endif
p_tcb->PendNextPtr = (OS_TCB *)0; /* 列表中没有其他 OS_TCB */
p_tcb->PendPrevPtr = (OS_TCB *)0;
p_pend_list->HeadPtr = p_tcb;
p_pend_list->TailPtr = p_tcb;
} else {
#if (OS_CFG_DBG_EN > 0u)
p_pend_list->NbrEntries++; /* 情况 1: 列表中增加一个 OS_TCB */
#endif
p_tcb_next = p_pend_list->HeadPtr;
while (p_tcb_next != (OS_TCB *)0) { /* 查找插入位置 */
if (prio < p_tcb_next->Prio) {
break; /* 找到!... 在当前条目前插入 */
} else {
p_tcb_next = p_tcb_next->PendNextPtr; /* 未找到,继续遍历列表 */
}
}
if (p_tcb_next == (OS_TCB *)0) { /* 要插入的 TCB 优先级最低 */
p_tcb->PendNextPtr = (OS_TCB *)0; /* ... 插入到尾部 */
p_tcb->PendPrevPtr = p_pend_list->TailPtr;
p_tcb->PendPrevPtr->PendNextPtr = p_tcb;
p_pend_list->TailPtr = p_tcb;
} else {
if (p_tcb_next->PendPrevPtr == (OS_TCB *)0) { /* 新 TCB 是否具有最高优先级? */
p_tcb->PendNextPtr = p_tcb_next; /* 是,作为新头部插入 */
p_tcb->PendPrevPtr = (OS_TCB *)0;
p_tcb_next->PendPrevPtr = p_tcb;
p_pend_list->HeadPtr = p_tcb;
} else { /* 否,插入到两个条目之间 */
p_tcb->PendNextPtr = p_tcb_next;
p_tcb->PendPrevPtr = p_tcb_next->PendPrevPtr;
p_tcb->PendPrevPtr->PendNextPtr = p_tcb;
p_tcb_next->PendPrevPtr = p_tcb;
}
}
}
}
/*
************************************************************************************************************************
* 从阻塞列表中移除任务,仅知道要移除的 TCB
*
* 描述: 此函数用于从阻塞列表中移除一个任务,仅知道要移除的任务的 TCB。
*
* 情况 0: OS_PEND_LIST 列表为空,无需操作。
*
* 情况 1: 列表中只有一个 OS_TCB。
*
* OS_PEND_LIST
* +--------------+ OS_TCB
* | TailPtr |--+---> +--------------+
* +--------------+ | | PendNextPtr |->0
* | HeadPtr |--/ +--------------+
* +--------------+ 0<-| PendPrevPtr |
* | NbrEntries=1 | +--------------+
* +--------------+ | |
* +--------------+
* | |
* +--------------+
*
* 情况 N: 列表中有两个或更多个 OS_TCB。
*
* OS_PEND_LIST
* +--------------+
* | TailPtr |---------------------------------------------------+
* +--------------+ OS_TCB OS_TCB | OS_TCB
* | HeadPtr |------> +--------------+ +--------------+ +-> +--------------+
* +--------------+ | PendNextPtr |<------| PendNextPtr | ...... | PendNextPtr |->0
* | NbrEntries=N | +--------------+ +--------------+ +--------------+
* +--------------+ 0<-| PendPrevPtr |<------| PendPrevPtr | ...... | PendPrevPtr |
* +--------------+ +--------------+ +--------------+
* | | | | | |
* +--------------+ +--------------+ +--------------+
* | | | | | |
* +--------------+ +--------------+ +--------------+
*
* 参数 : p_tcb 是指向要从阻塞列表中移除的任务的 TCB 的指针
* -----
*
* 返回值: 无
*
* 注意: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void OS_PendListRemove(OS_TCB *p_tcb)
{
OS_PEND_LIST *p_pend_list;
OS_TCB *p_next;
OS_TCB *p_prev;
if (p_tcb->PendObjPtr != (OS_PEND_OBJ *)0) { /* 仅当对象有阻塞列表时才移除 */
p_pend_list = &p_tcb->PendObjPtr->PendList; /* 获取阻塞列表的指针 */
/* 从阻塞列表中移除 TCB */
if (p_pend_list->HeadPtr->PendNextPtr == (OS_TCB *)0) {
p_pend_list->HeadPtr = (OS_TCB *)0; /* 列表中只有一个条目 */
p_pend_list->TailPtr = (OS_TCB *)0;
} else if (p_tcb->PendPrevPtr == (OS_TCB *)0) { /* 检查条目是否在列表头部 */
p_next = p_tcb->PendNextPtr; /* 是 */
p_next->PendPrevPtr = (OS_TCB *)0;
p_pend_list->HeadPtr = p_next;
} else if (p_tcb->PendNextPtr == (OS_TCB *)0) { /* 检查条目是否在列表尾部 */
p_prev = p_tcb->PendPrevPtr; /* 是 */
p_prev->PendNextPtr = (OS_TCB *)0;
p_pend_list->TailPtr = p_prev;
} else {
p_prev = p_tcb->PendPrevPtr; /* 从中间移除条目 */
p_next = p_tcb->PendNextPtr;
p_prev->PendNextPtr = p_next;
p_next->PendPrevPtr = p_prev;
}
#if (OS_CFG_DBG_EN > 0u)
p_pend_list->NbrEntries--; /* 列表中的条目减少一个 */
#endif
p_tcb->PendNextPtr = (OS_TCB *)0;
p_tcb->PendPrevPtr = (OS_TCB *)0;
p_tcb->PendObjPtr = (OS_PEND_OBJ *)0;
}
}
/*
************************************************************************************************************************
* 向任务发送消息
*
* 描述: 此函数用于向任务发送消息。此函数存在是因为它在多个 OSxxxPost() 服务中很常见。
*
* 参数 : p_obj 是指向被发送对象的指针,如果没有对象则为 NULL 指针
* -----
*
* p_tcb 是指向将接收“发送”的 OS_TCB 的指针
* -----
*
* p_void 如果我们向任务发送消息,这是任务将接收到的消息
*
* msg_size 如果我们向任务发送消息,这是消息的大小
*
* ts 发送发生的时间戳
*
* 返回值 : 无
*
* 注意 : 此函数是 uC/OS-III 的内部函数,您的应用程序不应调用它。
************************************************************************************************************************
*/
void OS_Post(OS_PEND_OBJ *p_obj,
OS_TCB *p_tcb,
void *p_void,
OS_MSG_SIZE msg_size,
CPU_TS ts)
{
#if (OS_CFG_TS_EN == 0u)
(void)ts; // 防止编译器警告未使用 'ts'
#endif
#if (OS_MSG_EN == 0u)
(void)p_void;
(void)msg_size;
#endif
switch (p_tcb->TaskState) {
case OS_TASK_STATE_RDY: // 不能向就绪的任务发送消息
case OS_TASK_STATE_DLY: // 不能向延迟的任务发送消息
case OS_TASK_STATE_SUSPENDED: // 不能向挂起的任务发送消息
case OS_TASK_STATE_DLY_SUSPENDED: // 不能向已延迟且挂起的任务发送消息
break;
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_TIMEOUT:
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = p_void; // 将消息存入等待任务的 OS_TCB 中
p_tcb->MsgSize = msg_size; // 假设正在发送消息
#endif
#if (OS_CFG_TS_EN > 0u)
p_tcb->TS = ts; // 设置时间戳
#endif
if (p_obj != (OS_PEND_OBJ *)0) {
OS_PendListRemove(p_tcb); // 从等待列表中移除任务
}
#if (OS_CFG_DBG_EN > 0u)
OS_PendDbgNameRemove(p_obj,
p_tcb); // 移除调试名称
#endif
#if (OS_CFG_TICK_EN > 0u)
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT) {
OS_TickListRemove(p_tcb); // 从计时器列表中移除任务
}
#endif
OS_RdyListInsert(p_tcb); // 将任务插入就绪列表
p_tcb->TaskState = OS_TASK_STATE_RDY; // 设置任务状态为就绪
p_tcb->PendStatus = OS_STATUS_PEND_OK; // 清除等待状态
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; // 表示不再等待
break;
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = p_void; // 将消息存入等待任务的 OS_TCB 中
p_tcb->MsgSize = msg_size; // 假设正在发送消息
#endif
#if (OS_CFG_TS_EN > 0u)
p_tcb->TS = ts; // 设置时间戳
#endif
if (p_obj != (OS_PEND_OBJ *)0) {
OS_PendListRemove(p_tcb); // 从等待列表中移除任务
}
#if (OS_CFG_DBG_EN > 0u)
OS_PendDbgNameRemove(p_obj,
p_tcb); // 移除调试名称
#endif
#if (OS_CFG_TICK_EN > 0u)
if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED) {
OS_TickListRemove(p_tcb); // 取消任何超时
}
#endif
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; // 设置任务状态为挂起
p_tcb->PendStatus = OS_STATUS_PEND_OK; // 清除等待状态
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; // 表示不再等待
break;
default:
// 默认情况
break;
}
}
/*
************************************************************************************************************************
* 初始化
* 就绪列表初始化
*
* 描述: 此函数由 OSInit() 调用以初始化就绪列表。就绪列表包含所有准备运行的任务列表。该列表实际上是一个 OS_RDY_LIST 数组。每个 OS_RDY_LIST 包含三个字段:列表中的 OS_TCB 数量(即 .NbrEntries)、指向 OS_RDY_LIST 中第一个 OS_TCB 的指针(即 .HeadPtr)和指向 OS_RDY_LIST 中最后一个 OS_TCB 的指针(即 .TailPtr)。
*
* OS_TCB 在 OS_RDY_LIST 中是双向链接的,每个 OS_TCB 都指向它所属的 OS_RDY_LIST。
*
* 'OS_RDY_LIST OSRdyTbl[OS_CFG_PRIO_MAX]' 初始化后看起来像这样:
*
* +---------------+--------------+
* | | TailPtr |-----> 0
* [0] | NbrEntries=0 +--------------+
* | | HeadPtr |-----> 0
* +---------------+--------------+
* | | TailPtr |-----> 0
* [1] | NbrEntries=0 +--------------+
* | | HeadPtr |-----> 0
* +---------------+--------------+
* : :
* : :
* : :
* +---------------+--------------+
* | | TailPtr |-----> 0
* [OS_CFG_PRIO_MAX-1] | NbrEntries=0 +--------------+
* | | HeadPtr |-----> 0
* +---------------+--------------+
*
* 参数 : 无
*
* 返回值: 无
*
* 注意 : 此函数是 uC/OS-III 的内部函数,您的应用程序不应调用它。
************************************************************************************************************************
*/
void OS_RdyListInit(void)
{
CPU_INT32U i;
OS_RDY_LIST *p_rdy_list;
for (i = 0u; i < OS_CFG_PRIO_MAX; i++) { // 初始化每个优先级的 OS_RDY_LIST 数组
p_rdy_list = &OSRdyList[i];
#if (OS_CFG_DBG_EN > 0u)
p_rdy_list->NbrEntries = 0u; // 初始化条目数量为 0
#endif
p_rdy_list->HeadPtr = (OS_TCB *)0; // 初始化头指针为 NULL
p_rdy_list->TailPtr = (OS_TCB *)0; // 初始化尾指针为 NULL
}
}
/*
************************************************************************************************************************
* 将TCB插入就绪列表
*
* 描述: 此函数用于将TCB插入就绪列表。
*
* 如果TCB的优先级与当前任务的优先级相同,则将TCB插入到列表的尾部。否则,将TCB插入到列表的头部。
*
* 参数: p_tcb 是要插入就绪列表的TCB的指针
* -----
*
* 返回: 无
*
* 注意: 此函数是uC/OS-III的内部函数,您的应用程序不应调用它。
************************************************************************************************************************
*/
void OS_RdyListInsert (OS_TCB *p_tcb)
{
OS_PrioInsert(p_tcb->Prio); // 将任务的优先级插入到优先级列表中
if (p_tcb->Prio == OSPrioCur) { // 任务的优先级是否与当前任务的优先级相同?
OS_RdyListInsertTail(p_tcb); // 是,将任务插入到就绪列表的尾部
} else {
OS_RdyListInsertHead(p_tcb); // 否,将任务插入到就绪列表的头部
}
OS_TRACE_TASK_READY(p_tcb); // 记录任务就绪的事件
}
/*
************************************************************************************************************************
* 在链表开头插入TCB
*
* 描述: 此函数用于将一个OS_TCB插入到链表的开头,具体如下:
*
* 情况 0: 在空链表中插入。
*
* OS_RDY_LIST
* +--------------+
* | TailPtr |-> 0
* +--------------+
* | HeadPtr |-> 0
* +--------------+
* | NbrEntries=0 |
* +--------------+
*
*
* 情况 1: 在当前链表头部之前插入
*
* OS_RDY_LIST
* +--------------+ OS_TCB
* | TailPtr |--+---> +------------+
* +--------------+ | | NextPtr |->0
* | HeadPtr |--/ +------------+
* +--------------+ 0<-| PrevPtr |
* | NbrEntries=1 | +------------+
* +--------------+ : :
* : :
* +------------+
*
*
* OS_RDY_LIST
* +--------------+
* | TailPtr |-----------------------------------------------+
* +--------------+ OS_TCB OS_TCB | OS_TCB
* | HeadPtr |------> +------------+ +------------+ +-> +------------+
* +--------------+ | NextPtr |------>| NextPtr | ...... | NextPtr |->0
* | NbrEntries=N | +------------+ +------------+ +------------+
* +--------------+ 0<-| PrevPtr |<------| PrevPtr | ...... | PrevPtr |
* +------------+ +------------+ +------------+
* : : : : : :
* : : : : : :
* +------------+ +------------+ +------------+
*
*
* 参数 : p_tcb 是要插入到链表中的OS_TCB
* -----
*
* 返回值: 无
*
* 注意事项: 1) 此函数是uC/OS-III的内部函数,您的应用程序必须不调用它。
************************************************************************************************************************
*/
void OS_RdyListInsertHead (OS_TCB *p_tcb)
{
OS_RDY_LIST *p_rdy_list;
OS_TCB *p_tcb2;
// 获取就绪列表的指针
p_rdy_list = &OSRdyList[p_tcb->Prio];
// 情况 0: 当链表为空时插入
if (p_rdy_list->HeadPtr == (OS_TCB *)0) {
#if (OS_CFG_DBG_EN > 0u)
// 这是第一个条目
p_rdy_list->NbrEntries = 1u;
#endif
// 链表中没有其他OS_TCB
p_tcb->NextPtr = (OS_TCB *)0;
p_tcb->PrevPtr = (OS_TCB *)0;
// 链表的头尾指针都指向这个OS_TCB
p_rdy_list->HeadPtr = p_tcb;
p_rdy_list->TailPtr = p_tcb;
} else {
// 情况 1: 在当前链表头部之前插入
#if (OS_CFG_DBG_EN > 0u)
// 链表中的OS_TCB数量加一
p_rdy_list->NbrEntries++;
#endif
// 调整新OS_TCB的链接
p_tcb->NextPtr = p_rdy_list->HeadPtr;
p_tcb->PrevPtr = (OS_TCB *)0;
// 调整旧链表头部的链接
p_tcb2 = p_rdy_list->HeadPtr;
p_tcb2->PrevPtr = p_tcb;
// 更新链表的头部指针
p_rdy_list->HeadPtr = p_tcb;
}
}
/*
************************************************************************************************************************
* 在链表末尾插入TCB
*
* 描述: 此函数用于将一个OS_TCB插入到链表的末尾,具体如下:
*
* 情况 0: 在空链表中插入。
*
* OS_RDY_LIST
* +--------------+
* | TailPtr |-> 0
* +--------------+
* | HeadPtr |-> 0
* +--------------+
* | NbrEntries=0 |
* +--------------+
*
*
* 情况 1: 在当前链表尾部之后插入
*
* OS_RDY_LIST
* +--------------+ OS_TCB
* | TailPtr |--+---> +------------+
* +--------------+ | | NextPtr |->0
* | HeadPtr |--/ +------------+
* +--------------+ 0<-| PrevPtr |
* | NbrEntries=1 | +------------+
* +--------------+ : :
* : :
* +------------+
*
*
* OS_RDY_LIST
* +--------------+
* | TailPtr |-----------------------------------------------+
* +--------------+ OS_TCB OS_TCB | OS_TCB
* | HeadPtr |------> +------------+ +------------+ +-> +------------+
* +--------------+ | NextPtr |------>| NextPtr | ...... | NextPtr |->0
* | NbrEntries=N | +------------+ +------------+ +------------+
* +--------------+ 0<-| PrevPtr |<------| PrevPtr | ...... | PrevPtr |
* +------------+ +------------+ +------------+
* : : : : : :
* : : : : : :
* +------------+ +------------+ +------------+
*
*
* 参数 : p_tcb 是要插入到链表中的OS_TCB
* -----
*
* 返回值: 无
*
* 注意事项: 1) 此函数是uC/OS-III的内部函数,您的应用程序必须不调用它。
************************************************************************************************************************
*/
void OS_RdyListInsertTail (OS_TCB *p_tcb)
{
OS_RDY_LIST *p_rdy_list;
OS_TCB *p_tcb2;
// 获取就绪列表的指针
p_rdy_list = &OSRdyList[p_tcb->Prio];
// 情况 0: 当链表为空时插入
if (p_rdy_list->HeadPtr == (OS_TCB *)0) {
#if (OS_CFG_DBG_EN > 0u)
// 这是第一个条目
p_rdy_list->NbrEntries = 1u;
#endif
// 链表中没有其他OS_TCB
p_tcb->NextPtr = (OS_TCB *)0;
p_tcb->PrevPtr = (OS_TCB *)0;
// 链表的头尾指针都指向这个OS_TCB
p_rdy_list->HeadPtr = p_tcb;
p_rdy_list->TailPtr = p_tcb;
} else {
// 情况 1: 在当前链表尾部之后插入
#if (OS_CFG_DBG_EN > 0u)
// 链表中的OS_TCB数量加一
p_rdy_list->NbrEntries++;
#endif
// 调整新OS_TCB的链接
p_tcb->NextPtr = (OS_TCB *)0;
p_tcb2 = p_rdy_list->TailPtr;
p_tcb->PrevPtr = p_tcb2;
// 调整旧链表尾部的链接
p_tcb2->NextPtr = p_tcb;
// 更新链表的尾部指针
p_rdy_list->TailPtr = p_tcb;
}
}
/*
************************************************************************************************************************
* 将TCB列表的头部移动到尾部
*
* 描述: 此函数用于将列表的当前头部移动到列表的尾部。
*
* 情况 0: TCB 列表为空,无需操作。
*
* 情况 1: 列表中只有一个 OS_TCB,无需操作。
*
* 情况 2: 列表中有两个 OS_TCB。
*
* OS_RDY_LIST
* +--------------+
* | TailPtr |--------------------------+
* +--------------+ OS_TCB | OS_TCB
* | HeadPtr |------> +------------+ +-> +------------+
* +--------------+ | NextPtr |------> | NextPtr |->0
* | NbrEntries=2 | +------------+ +------------+
* +--------------+ 0<-| PrevPtr | <------| PrevPtr |
* +------------+ +------------+
* : : : :
* : : : :
* +------------+ +------------+
*
*
* 情况 N: 列表中有超过两个 OS_TCB。
*
* OS_RDY_LIST
* +--------------+
* | TailPtr |-----------------------------------------------+
* +--------------+ OS_TCB OS_TCB | OS_TCB
* | HeadPtr |------> +------------+ +------------+ +-> +------------+
* +--------------+ | NextPtr |------>| NextPtr | ...... | NextPtr |->0
* | NbrEntries=N | +------------+ +------------+ +------------+
* +--------------+ 0<-| PrevPtr |<------| PrevPtr | ...... | PrevPtr |
* +------------+ +------------+ +------------+
* : : : : : :
* : : : : : :
* +------------+ +------------+ +------------+
*
*
* 参数 : p_rdy_list 是指向 OS_RDY_LIST 的指针,OS_TCB 将在此处插入
* ------
*
* 返回值: 无
*
* 注意事项: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void OS_RdyListMoveHeadToTail(OS_RDY_LIST *p_rdy_list)
{
OS_TCB *p_tcb1;
OS_TCB *p_tcb2;
OS_TCB *p_tcb3;
// 如果列表的头部和尾部不相同,说明列表中有多个 TCB
if (p_rdy_list->HeadPtr != p_rdy_list->TailPtr) {
// 如果列表中只有两个 TCB,则交换它们的位置
if (p_rdy_list->HeadPtr->NextPtr == p_rdy_list->TailPtr) {
p_tcb1 = p_rdy_list->HeadPtr; // 指向当前头部
p_tcb2 = p_rdy_list->TailPtr; // 指向当前尾部
p_tcb1->PrevPtr = p_tcb2; // 调整前向指针
p_tcb1->NextPtr = (OS_TCB *)0; // 调整后向指针
p_tcb2->PrevPtr = (OS_TCB *)0; // 调整前向指针
p_tcb2->NextPtr = p_tcb1; // 调整后向指针
p_rdy_list->HeadPtr = p_tcb2; // 更新头部指针
p_rdy_list->TailPtr = p_tcb1; // 更新尾部指针
} else {
p_tcb1 = p_rdy_list->HeadPtr; // 指向当前头部
p_tcb2 = p_rdy_list->TailPtr; // 指向当前尾部
p_tcb3 = p_tcb1->NextPtr; // 指向新的头部
p_tcb3->PrevPtr = (OS_TCB *)0; // 调整新头部的前向指针
p_tcb1->NextPtr = (OS_TCB *)0; // 调整新尾部的后向指针
p_tcb1->PrevPtr = p_tcb2; // 调整新尾部的前向指针
p_tcb2->NextPtr = p_tcb1; // 调整旧尾部的后向指针
p_rdy_list->HeadPtr = p_tcb3; // 更新头部指针
p_rdy_list->TailPtr = p_tcb1; // 更新尾部指针
}
}
}
/*
************************************************************************************************************************
* 从列表中移除已知地址的OS_TCB
*
* 描述: 此函数用于从 OS_RDY_LIST 中移除一个已知地址的 OS_TCB。
*
* 情况 0: TCB 列表为空,无需操作。
*
* 情况 1: 列表中只有一个 OS_TCB。
*
* OS_RDY_LIST
* +--------------+ OS_TCB
* | TailPtr |--+---> +------------+
* +--------------+ | | NextPtr |->0
* | HeadPtr |--/ +------------+
* +--------------+ 0<-| PrevPtr |
* | NbrEntries=1 | +------------+
* +--------------+ : :
* : :
* +------------+
*
* 情况 N: 列表中有两个或更多 OS_TCB。
*
* OS_RDY_LIST
* +--------------+
* | TailPtr |-----------------------------------------------+
* +--------------+ OS_TCB OS_TCB | OS_TCB
* | HeadPtr |------> +------------+ +------------+ +-> +------------+
* +--------------+ | NextPtr |------>| NextPtr | ...... | NextPtr |->0
* | NbrEntries=N | +------------+ +------------+ +------------+
* +--------------+ 0<-| PrevPtr |<------| PrevPtr | ...... | PrevPtr |
* +------------+ +------------+ +------------+
* : : : : : :
* : : : : : :
* +------------+ +------------+ +------------+
*
*
* 参数 : p_tcb 是指向要移除的 OS_TCB 的指针
* -----
*
* 返回值: 移除 OS_TCB 的 OS_RDY_LIST 的指针
*
* 注意事项: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void OS_RdyListRemove(OS_TCB *p_tcb)
{
OS_RDY_LIST *p_rdy_list;
OS_TCB *p_tcb1;
OS_TCB *p_tcb2;
p_rdy_list = &OSRdyList[p_tcb->Prio]; // 获取优先级对应的就绪列表
p_tcb1 = p_tcb->PrevPtr; // 获取要移除的 TCB 的前一个 TCB
p_tcb2 = p_tcb->NextPtr; // 获取要移除的 TCB 的后一个 TCB
// 检查要移除的 TCB 是否在列表的头部
if (p_tcb1 == (OS_TCB *)0) {
// 如果要移除的 TCB 也是列表的唯一 TCB
if (p_tcb2 == (OS_TCB *)0) {
#if (OS_CFG_DBG_EN > 0u)
p_rdy_list->NbrEntries = 0u; // 列表中没有更多的条目
#endif
p_rdy_list->HeadPtr = (OS_TCB *)0; // 更新头部指针
p_rdy_list->TailPtr = (OS_TCB *)0; // 更新尾部指针
OS_PrioRemove(p_tcb->Prio); // 从优先级列表中移除该优先级
} else {
#if (OS_CFG_DBG_EN > 0u)
p_rdy_list->NbrEntries--; // 列表中的条目减少一个
#endif
p_tcb2->PrevPtr = (OS_TCB *)0; // 调整新头部的前向指针
p_rdy_list->HeadPtr = p_tcb2; // 更新就绪列表的新头部
}
} else {
#if (OS_CFG_DBG_EN > 0u)
p_rdy_list->NbrEntries--; // 列表中的条目减少一个
#endif
p_tcb1->NextPtr = p_tcb2; // 调整前一个 TCB 的后向指针
if (p_tcb2 == (OS_TCB *)0) {
p_rdy_list->TailPtr = p_tcb1; // 如果移除的是尾部 TCB,更新尾部指针
} else {
p_tcb2->PrevPtr = p_tcb1; // 调整后一个 TCB 的前向指针
}
}
p_tcb->PrevPtr = (OS_TCB *)0; // 清除要移除的 TCB 的前向指针
p_tcb->NextPtr = (OS_TCB *)0; // 清除要移除的 TCB 的后向指针
OS_TRACE_TASK_SUSPENDED(p_tcb); // 记录任务被挂起的跟踪信息
}
/*
************************************************************************************************************************
* 调度器锁定时间测量
*
* 描述: 这些函数用于测量调度器锁定的最长时间。
*
* 参数 : 无
*
* 返回值: 无
*
* 注意事项: 1) 这些函数是 uC/OS-III 的内部函数,不得在应用程序代码中调用。
*
* 2) 假设这些函数在中断禁用时被调用。
*
* 3) 即使这是一个 16 位定时器,我们也直接通过 OS_TS_GET() 读取时间戳定时器。原因是不期望调度器锁定时间超过 65536 个计数,即使定时器以较高的频率更新。换句话说,在实时系统中,调度器锁定时间超过 65536 个计数不是一个好现象。
************************************************************************************************************************
*/
#if (OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u)
void OS_SchedLockTimeMeasStart (void)
{
if (OSSchedLockNestingCtr == 1u) {
OSSchedLockTimeBegin = OS_TS_GET();
}
}
void OS_SchedLockTimeMeasStop (void)
{
CPU_TS_TMR delta;
if (OSSchedLockNestingCtr == 0u) { /* Make sure we fully un-nested scheduler lock */
delta = OS_TS_GET() /* Compute the delta time between begin and end */
- OSSchedLockTimeBegin;
if (OSSchedLockTimeMax < delta) { /* Detect peak value */
OSSchedLockTimeMax = delta;
}
if (OSSchedLockTimeMaxCur < delta) { /* Detect peak value (for resettable value) */
OSSchedLockTimeMaxCur = delta;
}
}
}
#endif
/*
************************************************************************************************************************
* 运行轮转调度算法
*
* 描述: 此函数在每个时钟滴答时被调用,以确定是否需要执行当前优先级下的新任务。
*
* 参数 : p_rdy_list 是指向当前优先级就绪列表的 OS_RDY_LIST 条目的指针
* ----------
*
* 返回值: 无
*
* 注意事项: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
#if (OS_CFG_SCHED_ROUND_ROBIN_EN > 0u)
void OS_SchedRoundRobin (OS_RDY_LIST *p_rdy_list)
{
OS_TCB *p_tcb;
CPU_SR_ALLOC();
// 确保轮转调度已启用
if (OSSchedRoundRobinEn != OS_TRUE) {
return;
}
CPU_CRITICAL_ENTER(); // 进入临界区
p_tcb = p_rdy_list->HeadPtr; // 获取就绪列表的头指针
if (p_tcb == (OS_TCB *)0) { // 检查是否有任务在就绪列表中
CPU_CRITICAL_EXIT();
return;
}
#if (OS_CFG_TASK_IDLE_EN > 0u)
if (p_tcb == &OSIdleTaskTCB) { // 检查是否为空闲任务
CPU_CRITICAL_EXIT();
return;
}
#endif
if (p_tcb->TimeQuantaCtr > 0u) { // 减少时间片计数器
p_tcb->TimeQuantaCtr--;
}
if (p_tcb->TimeQuantaCtr > 0u) { // 如果任务的时间片未用完
CPU_CRITICAL_EXIT();
return;
}
if (p_rdy_list->HeadPtr == p_rdy_list->TailPtr) { // 检查是否只有一个任务在就绪列表中
CPU_CRITICAL_EXIT();
return;
}
if (OSSchedLockNestingCtr > 0u) { // 检查调度器是否被锁定
CPU_CRITICAL_EXIT();
return;
}
OS_RdyListMoveHeadToTail(p_rdy_list); // 将当前任务移到就绪列表的末尾
p_tcb = p_rdy_list->HeadPtr; // 获取新的头任务
if (p_tcb->TimeQuanta == 0u) { // 检查是否需要使用默认时间片
p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;
} else {
p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; // 加载新的时间片计数器
}
CPU_CRITICAL_EXIT(); // 退出临界区
}
#endif
/*
************************************************************************************************************************
* 阻塞任务
*
* 描述: 此函数用于将任务从就绪列表中移除,并在指定的超时时间非零时将其插入定时器滴答列表。
*
* 参数 : p_tcb 是指向要阻塞的任务的 OS_TCB 的指针
* -----
*
* timeout 是所需的超时时间
*
* 返回值: 无
*
* 注意事项: 1) 此函数是 uC/OS-III 的内部函数,您的应用程序不得调用它。
************************************************************************************************************************
*/
void OS_TaskBlock (OS_TCB *p_tcb,
OS_TICK timeout)
{
#if (OS_CFG_DYN_TICK_EN > 0u)
OS_TICK elapsed;
elapsed = OS_DynTickGet(); // 获取动态滴答计数
#endif
#if (OS_CFG_TICK_EN > 0u)
if (timeout > 0u) { // 如果超时时间非零,将任务添加到滴答列表
#if (OS_CFG_DYN_TICK_EN > 0u)
(void)OS_TickListInsert(p_tcb, elapsed, (OSTickCtr + elapsed), timeout); // 插入动态滴答计数
#else
(void)OS_TickListInsert(p_tcb, 0u, OSTickCtr, timeout); // 插入普通滴答计数
#endif
p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT; // 设置任务状态为等待超时
} else {
p_tcb->TaskState = OS_TASK_STATE_PEND; // 设置任务状态为等待
}
#else
(void)timeout; // 忽略超时参数
p_tcb->TaskState = OS_TASK_STATE_PEND; // 设置任务状态为等待
#endif
OS_RdyListRemove(p_tcb); // 从就绪列表中移除任务
}