//作用:包含用於管理訊息佇列的程式碼
/*
*********************************************************************************************************
* 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