TI-RTOS的佇列提供具有執行緒安全性(thread-safe)的單向訊息傳輸模組,
通常用於高優先級的執行緒向低優先級的執行緒傳遞訊息,
這訊息可能只是某個指示,
等到低優先級的執行緒取得處理器的執行權時,
再依照指示來進行相對應的處理。
util 模組包含了一組抽象的TI-RTOS 佇列函式,如下
接著我們來看範例程式中如何使用佇列。
上一篇TI-RTOS 定時器(Clock)我們看到定時器的建立和啟動,
等定時器到期後,會在軟體中斷的執行緒中執行SimplePeripheral_clockHandler(),
為了避免花費太多執行時間,
所以我們在SimplePeripheral_clockHandler()裡面只有透過SimplePeripheral_enqueueMsg()做訊息的傳送。
首先再看一下定時器的流程圖,
之前講到流程圖的數字1和2的上半段,
現在從數字2 的SimplePeripheral_clockHandler()開始看,
// 參考流程圖中的數字2
// 參考剛剛建立定時器時候的第二個和第五個參數
// 當時間到的時候,就會呼叫SimplePeripheral_clockHandler這個函式
// 並且把 SP_PERIODIC_EVT 當作參數代入
static void SimplePeripheral_clockHandler(UArg arg)
{
spClockEventData_t *pData = (spClockEventData_t *)arg;
// 這邊會取出參數,判斷是否為定時器的事件
if (pData->event == SP_PERIODIC_EVT)
{
// 因為現在這段程式是執行在軟體中斷的執行緒,
// 我們不可以在這邊做太多事情,
// 所以只把"定時器到期"這個訊息放到佇列(Queue)中。
SimplePeripheral_enqueueMsg(SP_PERIODIC_EVT, NULL);
}
}
static status_t SimplePeripheral_enqueueMsg(uint8_t event, void *pData)
{
// 從heap中取得記憶體空間,拿來存放訊息的內容
spEvt_t *pMsg = ICall_malloc(sizeof(spEvt_t));
// 呼叫Util的API
success = Util_enqueueMsg(appMsgQueueHandle, syncEvent, (uint8_t *)pMsg);
// 為了保持訊息內容,記憶體空間不會馬上被釋放
}
// 順便看一下Util_enqueueMsg()裡面做了什麼事情
uint8_t Util_enqueueMsg(Queue_Handle msgQueue,
Event_Handle event,
uint8_t *pMsg)
{
...
// This is an atomic operation,簡單理解就是這動作很快就處理完不會影響到其他事情
Queue_put(msgQueue, &pRec->_elem);
// 將訊息放入佇列之後,
// 會透過Event_post()發送一個事件,
// 告訴對方Queue中已經被放入訊息,
Event_post(event, UTIL_QUEUE_EVENT_ID);
...
}
到這邊就是流程圖中數字2的結束
剛剛上面說的"對方"是誰呢?
Event_post()和Event_pend()一定是成對出現,
而這個"對方"就是指正在Event_pend()的那的對象,
所以接著我們就來看是誰在使用Event_pend()。
最後我們回到Simple Peripheral的任務,也就是流程圖中的數字3所在的紅框
static void SimplePeripheral_taskFxn(UArg a0, UArg a1)
{
// 初始化任務
SimplePeripheral_init();
// 任務的主迴圈
for (;;)
{
// SimplePeripheral在初始化完畢後,
// 就會在這邊等待事件,
// 一有事件就往下處理,
// 否則卡在這邊等事件發生,
// 所以當定時器發出"訊息已被放入Queue"的事件,
// 這邊就會收到該事件。
events = Event_pend(syncEvent, Event_Id_NONE, SP_ALL_EVENTS,
ICALL_TIMEOUT_FOREVER);
if (events)
{
...
// 判斷是否為佇列的事件
if (events & SP_QUEUE_EVT)
{
// 從佇列中取出訊息,直到佇列為空
// 判斷佇列是否為空
while (!Queue_empty(appMsgQueueHandle))
{
// 從佇列中取出訊息
spEvt_t *pMsg = (spEvt_t *)Util_dequeueMsg(appMsgQueueHandle);
if (pMsg)
{
// 處理訊息,假設為定時器所發出的訊息,
// 這邊取出的訊息就是:SP_PERIODIC_EVT
// 接著會直接把訊息當作參數代入SimplePeripheral_processAppMsg()函式,
SimplePeripheral_processAppMsg(pMsg);
// 等任務完成後,才將訊息的記憶體是放回heap
ICall_free(pMsg);
}
}
}
}
}
// 最後就是執行真正要做的事情,
// 因為現在是在SimplePeripheral的任務執行緒中,
// 所以可以穩穩地做好要做的事情,
// 但如果執行期間有硬體中斷或軟體中斷的發生,
// 動作就會先被暫停,直到中斷事件處理完畢,再繼續之前的動作。
static void SimplePeripheral_processAppMsg(spEvt_t *pMsg)
{
switch (pMsg->event)
{
...
case SP_PERIODIC_EVT:
SimplePeripheral_performPeriodicTask();
break;
...
}