2014年12月5日

2014/12/05 進階微控制器應用

critical section(緊要區):當程式進入緊要區時有兩種可能(該區程式不可太長),目的:希望緊要區內的程式不被其他程式打斷

  1. 所有中斷被除能(disable)
  2. 部份中斷被除能(需包含系統滴答中斷的除能,則不會文本切換),保留部份硬體中斷仍可致能,被致能之中斷需能被控制到不切割緊要區之程式,程式進入緊要區後,執行緊要區程式後,要離開緊要區時,需重新致能被除能之中斷
哪些要寫在緊要區?
  1. Accessing Peripherals 
  2. 讀改寫操作(不可間斷)
  3. .Non-atomic Access to Variables (可切割存取變數)atomic原子/不可切割
  4. Function Reentrancy函式重入性
MACRO巨集指令:目的為了讓程式設計者閱讀程式碼更為清晰容易
#define taskENTER_CRITICAL() _asm_("disint")

taskENTER_CRITICAL();//進入緊要區之API,_asm_("disint")不易閱讀,taskENTER_CRITICAL();易讀

taskEXIT_CRITICAL();//離開緊要區之API

例:避免讀改寫過程被切換
taskENTER_CRITICAL();
PORTA 1=1;//被緊要區保護的程式
taskEXIT_CRITICAL();

例:列印訊息之保護
void printString(char *str)
{
    taskENTER_CRITICAL();


    printf("%s\n",str);
    taskEXIT_CRITICAL();
}//效果不好,因若列印字串過長,會導致緊要區過九,則即時作業系統的成效,大打則扣

另外利用工作擱置的方式亦可達到類似除能中斷之效果(可以保護資料不被損壞工作不會被切換,且仍可以中斷,有即時效果)
,此時,中斷仍有作用,但一工作之內容可不被其他工作損毀

問taskENTER_CRITICAL();會除能中斷?

擱置工作之API
vTaskSuspendALL()  相當於→ taskENTER_CRITICAL();
重啟工作之API
xTaskResumeALL() 相當於→ taskEXIT_CRITICAL();



void printString(char *str)
{
    vTaskSuspendALL();


    printf("%s\n",str);
    xTaskResumeALL();
}
需要注意的是xTaskResumeALL();需花較多時間重新排程

要保護ㄧ段程式不被其他工作或中斷破壞其完整性時,可以使用緊要區之方式或擱置工作之方式

保護記憶體資料完整之方式主要使用互斥(Mutex),Mutex之資料型態為xSemaaphoreHandle,基本上Mutex即為二元信號(binary semaphore)
不同處為取用信號後,需歸還釋放資源


低優先權被ㄧ嚴重阻斷,可使用優先權繼承來解決
當工作A取用資源Mutex時,工作A就可以存取資源,此時工作B無法取得資源Mutex,故不能存取資源,如此可保護資源資料與工作間之一致性與完整性,當工作A使用資源後需歸還資源Mutex,歸還後工作B即可取用資源Mutex以存取資源

使用Mutex可能造成兩種狀況
  1. 優先權反轉(priority inversion):在強佔式排程中,優先權低的工作被執行,而優先權高的工作反而不被執行之現象。當低優先權工作取得一資源Mutex後,工作切換到高優先權工作,而高優先權工作也要取用同一個資源Mutex時,由於資源尚未被釋放,故高優先權工作會進入阻斷狀態。

     
  2. 鎖死(deadlock):當兩個工作分別取用兩個不同資源Mutex,可能兩個工作需等待資源Mutex之取得,造成兩個工作都進入阻斷狀態而無法工作,如工作A、B取用資源1、2,工作A取得資源1之Mutex,工作B取得資源2之Mutex均位歸還前工作A又要取用資源1之Mutex,結果工作A、B均進入阻斷狀態。
要解決優先權反轉的問題,可以使用優先權繼承(priority inheritance)
優先權繼承:當低優先權工作取得資源Mutex時,將自身工作之優先權提高到與會取用同一個資源之工作的最高優先權之數值,如有工作A優先權數值=1,工作B優先權數值=3,工作A、工作B會有同時存取一資源,當工作A取得資源Mutex時,需同時將自身優先權提高為3,當資源使用後,歸還資源Mutex時需將優先權降為原先之優先權數值。

要避免鎖死可使用守門員工作(Gatekeeper Task),守門員負責總管所有資源,任何工作要使用資源時均需透過守門員工作來存取資源。

建立Mutex之API
xSemaphoreCreateMutex()會回傳Mutex Handle
例:xSemaphoreHandle mutex1;
mutex1=xSemaphoreCreateMutex();

char data[100];
void TaskA(void *ptr)
{
//取用mutex1
    xSemaphoreTake(mutex1,portMAX_DELAY);//若mutex1為空則等待,否則往下執行
    for(i=0;i<100;i++) {//使用mutex來保護該程式
        data[i]=i;
    }
//歸還mutex1
     xSemaphoreGive(mutex1);
}
void TaskB(void *ptr)
{
//取用mutex1
    xSemaphoreTake(mutex1,portMAX_DELAY);//若mutex1為空則等待,否則往下執行
    for(i=0;i<100;i++) {//使用mutex來保護該程式
        data[i]=i*i;
    }
//歸還mutex1
     xSemaphoreGive(mutex1);
}

使用守門員保護列印訊息
守門員工作
void GatekeeperTask(void *ptr)
{
    char *msgStr;
    while(1){
        xQueueReceve( xPrintQueue, &msgStr, portMAX_DELAY );
        printf( "%s", msgStr ); 
    }
}

送訊息工作
void sendMsgTask(void *ptr) {
    char *msgToSend;
    msgToSend=(char *)ptr;
    while(1) {
        xQueueSendToBack( xPrintQueue, &msgToSend,0);
        vTaskDelay(20);
    }
}
xQueueHandle xPrintQueue;
int main(void) {
    xPrintQueue=xQueueCreate(5,sizeof(char *));
     if( xPrintQueue != NULL ) {
         xTaskCreate( sendMsgTask, "Task1",1000,"Hello World!",1, NULL ); 
         xTaskCreate( sendMsgTask, "Task2",1000, "Abort,Retry",2, NULL ); 
         xTaskCreate( GatekeeperTask, "Gatekeeper", 1000, NULL, 1, NULL ); 
         vTaskStartScheduler();
    }
    while(1);
    return 0;
}

沒有留言:

張貼留言

文章有誤或有問題麻煩您留言告知! 謝謝您~~