# 2021單晶片課程:RTOS(下)
---
### Mutex
`Mutex` 是一種可以將執行緒進行同步處理的方式,使用 lock 和 unlock 的方式來共享資源使用。
:::warning
`Mutex`的方法無法被 ISR (interrupt service routines)所使用。
:::
以下為 `Mutex` 的示意圖 :

以下為 `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` 的示意圖 :

以下為 `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` 的示意圖 :

常用指令 :
```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` 的示意圖 :

以下為 `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() 之間的差別。
:::