iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 20
0
IoT

玩玩 BLE系列 第 20

[Day 20] TI-RTOS 佇列(Queue)

  • 分享至 

  • xImage
  •  

佇列(Queue)

Queue Messageing Process
TI-RTOS的佇列提供具有執行緒安全性(thread-safe)的單向訊息傳輸模組,
通常用於高優先級的執行緒向低優先級的執行緒傳遞訊息,
這訊息可能只是某個指示,
等到低優先級的執行緒取得處理器的執行權時,
再依照指示來進行相對應的處理。

util 模組包含了一組抽象的TI-RTOS 佇列函式,如下

接著我們來看範例程式中如何使用佇列。

上一篇TI-RTOS 定時器(Clock)我們看到定時器的建立和啟動,
等定時器到期後,會在軟體中斷的執行緒中執行SimplePeripheral_clockHandler(),
為了避免花費太多執行時間,
所以我們在SimplePeripheral_clockHandler()裡面只有透過SimplePeripheral_enqueueMsg()做訊息的傳送。

首先再看一下定時器的流程圖,
之前講到流程圖的數字1和2的上半段,
現在從數字2 的SimplePeripheral_clockHandler()開始看,
Triggering Clock objects

// 參考流程圖中的數字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;
    ...
}

上一篇
[Day 19] TI-RTOS 定時器(Clock)
下一篇
[Day 21] TI-RTOS 執行緒的同步(Thread Synchronization)
系列文
玩玩 BLE27
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言