# 2021單晶片課程:RTOS(下) --- ### Mutex `Mutex` 是一種可以將執行緒進行同步處理的方式,使用 lock 和 unlock 的方式來共享資源使用。 :::warning `Mutex`的方法無法被 ISR (interrupt service routines)所使用。 ::: 以下為 `Mutex` 的示意圖 : ![](https://i.imgur.com/cKvXmC6.png) 以下為 `Mutex` 的範例程式 : ```javascript= #include "mbed.h" #include "rtos.h" Mutex stdio_mutex; void notify(const char* name, int state) { stdio_mutex.lock(); printf("%s: %d\n\r", name, state); stdio_mutex.unlock(); } void test_thread(void const *args) { while (true) { notify((const char*)args, 0); Thread::wait(1000); notify((const char*)args, 1); Thread::wait(1000); } } int main() { Thread t2; Thread t3; t2.start(callback(test_thread, (void *)"Th 2")); t3.start(callback(test_thread, (void *)"Th 3")); test_thread((void *)"Th 1"); } ``` 利用 lock() 來使 stdio 正常運作,並用 unlock()釋放資源。 :::info 在 ARM C 標準函式庫內已經存在 Mutex 。主要是可以讓 stdio 進行正常運作,因此以上的範例在實作時並不是必須,除非有些沒有以上保護機制的開發板才需要以上程式碼,如 ARM M0 系列。 ::: :::warning 正是因為 ARM C 標準函式庫在使用 stdio 時會使用到 Mutex,所以在 ISR 中變無法使用 printf, putc, getc, malloc 和 new 。 ::: ::: info Thread class 中的 wait() function 中間是以毫秒為單位,使用 Thread::wait(1000) 是該 funciotn 下 全部 thread 暫停一秒, t1.wait(500) 是 t1 該物件暫停 0.5 秒。 ::: 相關語法請參考 : [Mutex](https://os.mbed.com/docs/mbed-os/v6.15/apis/mutex.html)。 --- ### Semaphore `Semaphore` 可以用來管理多種特定線程使用共享資源 (a pool of shared resources) 的情況。 以下為 `Semaphore` 的示意圖 : ![](https://i.imgur.com/xeydSYc.png) 以下為 `Semaphore` 的範例程式 : ```javascript= #include "mbed.h" #include "rtos.h" Semaphore two_slots(2); //宣告兩執行緒的Semaphore void test_thread(void const *name) { while (true) { two_slots.wait(); printf("%s\n\r", (const char*)name); Thread::wait(1000); two_slots.release(); } } int main (void) { Thread t2; Thread t3; t2.start(callback(test_thread, (void *)"Th 2")); t3.start(callback(test_thread, (void *)"Th 3")); test_thread((void *)"Th 1"); } ``` 可管理同時要釋放多少資源來處理執行緒。 相關語法請參考 : [Semaphore](https://os.mbed.com/docs/mbed-os/v5.15/apis/semaphore.html)。 --- ### Queue 在資料結構中我們學過 `Queue`,即是 FIFO (Frist In Frist Out) 的結構,在實作執行緒排程時就會使用到此種結構方式。 以下為 `Queue` 的示意圖 : ![](https://i.imgur.com/gr3DlME.png) 常用指令 : ```javascript= Queue () //Create and initialise a message Queue . Queue< T, queue_sz > osStatus put (T *data, uint32_t millisec=0) //Put a message in a Queue ; T : templete osEvent get (uint32_t millisec=osWaitForever)//Get a message or Wait for a message from a Queue . ``` 相關語法請參考 : [Queue](https://os.mbed.com/docs/mbed-os/v5.15/apis/queue.html)。 --- ### MemoryPool `MemoryPool` 是用來定意並管理記憶體空間的程式。混合 `Queue` 的函式我們可以得到以下範例程式 : ```javascript= #include "mbed.h" #include "rtos.h" typedef struct { float voltage; /* AD result of measured voltage */ float current; /* AD result of measured current */ uint32_t counter; /* A counter value */ } message_t; MemoryPool<message_t, 16> mpool; Queue<message_t, 16> queue; /* Send Thread */ void send_thread (void) { uint32_t i = 0; while (true) { i++; // fake data update message_t *message = mpool.alloc(); message->voltage = (i * 0.1) * 33; message->current = (i * 0.1) * 11; message->counter = i; queue.put(message); Thread::wait(1000); } } int main (void) { Thread thread; thread.start(callback(send_thread)); while (true) { osEvent evt = queue.get(); if (evt.status == osEventMessage) { message_t *message = (message_t*)evt.value.p; printf("\nVoltage: %.2f V\n\r" , message->voltage); printf("Current: %.2f A\n\r" , message->current); printf("Number of cycles: %u\n\r", message->counter); mpool.free(message); } } } ``` 常用指令 : ```javascript= MemoryPool< T, pool_sz > //Create and Initialize a memory pool. MemoryPool () T* alloc (void) //Allocate a memory block of type T from a memory pool. T* calloc (void) //Allocate a memory block of type T from a memory pool and set memory block to zero. osStatus free (T *block) //Return an allocated memory block back to a specific memory pool. ``` 相關語法請參考 : [MemoryPool](https://os.mbed.com/docs/mbed-os/v5.15/apis/memorypool.html)。 --- ### Mail `Mail`的功能即是將上述的 `Queue` 和 `Memory pool` 功能整合起來。 以下為 `Mail` 的示意圖 : ![](https://i.imgur.com/EURq6iV.png) 以下為 `Mail` 的範例程式 : ```javascript= #include "mbed.h" #include "rtos.h" /* Mail */ typedef struct { float voltage; /* AD result of measured voltage */ float current; /* AD result of measured current */ uint32_t counter; /* A counter value */ } mail_t; Mail<mail_t, 16> mail_box; void send_thread (void) { uint32_t i = 0; while (true) { i++; // fake data update mail_t *mail = mail_box.alloc(); mail->voltage = (i * 0.1) * 33; mail->current = (i * 0.1) * 11; mail->counter = i; mail_box.put(mail); Thread::wait(1000); } } int main (void) { Thread thread; thread.start(callback(send_thread)); while (true) { osEvent evt = mail_box.get(); if (evt.status == osEventMail) { mail_t *mail = (mail_t*)evt.value.p; printf("\nVoltage: %.2f V\n\r" , mail->voltage); printf("Current: %.2f A\n\r" , mail->current); printf("Number of cycles: %u\n\r", mail->counter); mail_box.free(mail); } } } ``` 常用指令 : ```javascript= Mail< T, queue_sz > //Create and Initialise Mail queue. Mail () T *alloc (uint32_t millisec=0) //Allocate a memory block of type T. T *calloc (uint32_t millisec=0) //Allocate a memory block of type T and set memory block to zero. osStatus put (T *mptr) //Put a mail in the queue. osEvent get (uint32_t millisec=osWaitForever) //Get a mail from a queue. osStatus free (T *mptr) //Free a memory block from a mai ``` 相關語法請參考 : [Mail](https://os.mbed.com/docs/mbed-os/v5.15/apis/mail.html)。 --- ### Status and Error Codes The Status and Error Codes section lists all the return values that the `CMSIS-RTOS functions` will return: :::info CMSIS-RTOS 是 ARM 公司為統一操作系統、降低嵌入式門檻而發佈的操作系統標準軟件接口。通俗講,CMSIS-RTOS 將操作系統(不管是 FREE-RTOS 還是 RTX 等)屏蔽起來,然後提供 CMSIS-RTOS 接口函數給最終用户調用。 ::: + `osOK` : function completed ; no event occurred. + `osEventSignal` : function completed; signal event occurred. + `osEventMessage` : function completed; message event occurred. + `osEventMail` : function completed; mail event occurred. + `osEventTimeout` : function completed; timeout occurred. + `osErrorParameter` : parameter error: a mandatory parameter was missing or specified an incorrect object. + `osErrorResource` : resource not available: a specified resource was not available. + `osErrorTimeoutResource` : resource not available within given time : a specified resource was not available within the timeout period. + `osErrorISR` : not allowed in ISR context: the function cannot be called from interrupt service routines. + `osErrorISRRecursive` : function called multiple times from ISR with same object. + `osErrorPriority` : system cannot determine priority or thread has illegal priority. + `osErrorNoMemory` : system is out of memory: it was impossible to allocate or reserve memory for the operation. + `osErrorValue` : value of a parameter is out of range. + `osErrorOS` : unspecified RTOS error: run-time error but no other error message fits. --- <font color = "red">**※ 使用thread.terminate();使thread停止**</font> --- ### Lab 1 請利用上述 RTOS 中的 <font color = "red">**Thread**</font> & <font color = "red">**Semaphore**</font> & <font color = "red">**Mutex**</font>來完成以下功能: 1. 每隔1秒亮一顆開發板上之LED,總共3顆輪流。 2. 當開發板上 Button 按下後,改變為 <font color = "red">**Semaphore**</font>的狀態,Semaphore 觸發內容為每隔1秒亮兩顆開發版上之LED。 3. 再按一次開發板上的 Button 後,改變為 <font color = "red">**Mutex**</font>的狀態,每隔1秒閃爍開發版上所有LED。 4. 最後再按一次Button回歸每隔1秒亮一顆LED的狀態。 {%youtube Aj4gx4QwFIk %} --- ### Lab 2 請利用上述 RTOS 中的 <font color = "red">**Mail**</font> 實現可繼續計時的碼表。 <font color = "red">**※使用Thread::wait()計時**</font> 1. 輸入"s" : 開始計時(最小單位為1/100秒)。 2. 輸入"p" : 顯示計時的秒數。 3. 輸入" r ":將計時至今的秒數清空 {%youtube oDnveLfayaw %} ## 課後問題 :::info * Q1.請詳述 Thread 在 Mbed 平台中是如何運作的。 * Q2.請嘗試解釋 Mbed 平台 API 中, wait() 與 Thread::wait() 之間的差別。 :::