淺談嵌入式軟件設計


淺談嵌入式軟件設計

本文在21IC的公眾號文章《多年嵌入式編程工程師經驗分享:換個角度來編程》基礎上結合自己理解而寫,部分圖片以及文字說明均來自互聯網。

前后台模型

模型介紹

當開發過程中不使用OS時,幾乎所有的嵌入式程序歸根結底都是一個由無法停止的循環為結構構成的,即常見的while(1)for(;;),用流程圖表示就是這樣:

graph TD stop[結束] start[查詢IO或外設狀態] --> section1[執行相關業務邏輯] section1 --> conditionA{退出無限循環?} conditionA -- YES --> stop conditionA -- NO --> section1 start_sub[業務邏輯]-->subconditonA{執行業務邏輯A?} subconditonA{執行業務邏輯A?}--YES-->subsection1[子功能A] subconditonA{執行業務邏輯A?}--NO-->subconditonB{執行業務邏輯B?} subconditonB--YES-->subsection2[子功能B] subconditonB--NO-->stop_b[結束] interp[觸發中斷]-->baoliu[保護現場] baoliu-->interp2[執行中斷服務函數] interp2-->tuichuzhongduan[退出中斷] tuichuzhongduan-->huifu[恢復現場]

這個過程中,軟件處在兩種狀態下:

  • 在無外設或IO請求時處於邏輯工作模式,順序並循環執行相關邏輯程序
  • 在有中斷觸發時中斷當前業務流程,執行中斷服務程序。

這種軟件設計即前后台設計,用圖像表示即:

這種軟件設計下可以將設備需要的功能根據實時性進行分類,將實時性要求不高但需要持續刷新的功能作為一個子函數在主循環中順序執行,而將需要快速響應的功能作為中斷函數進行處理。

存在的問題

這種方法存在幾個問題:

  • 當高實時性任務的工作量過大時,中斷占用時間過久,使后台需要刷新的任務無法得到及時更新,即中斷函數過於臃腫,影響了業務邏輯。
  • 后台任務是順序執行的,在上一個任務無法結束前下一個任務無法開啟,這使的所有任務的更新時間均一致,邏輯代碼越多,更新時間越久,進而影響某些任務的實現。

解決方法

上面的問題當然有一些解決的辦法,但是都不是十分完美:

中斷臃腫問題

將中斷任務作為一個任務標記函數,並將中斷任務進行拆分,中斷來臨時僅進行任務工作狀態標記,如按鍵等,在主程序中對每個子任務執行前進行狀態標記檢查:


int main()
{
    while (1)
    {
        if (interflag == 1)
        {
            subinterfuncs(); //執行中斷子程序
        }
        else
        {
            backgroundfunc1(); //后台任務1
        }
        if (interflag == 2)
        {
            subinterfuncs(); //執行中斷子程序
        }
        else
        {
            backgroundfunc2(); //后台任務1
        }
        //后續其他任務等等
    }
    return 0;
}

subinterfuncs()
{
    switch (subinterflag)
    {
    case 1:
        interfunc1(); //中斷子任務1
        break;
    case 2:
        interfunc2(); //中斷子任務2
        break;
    default:
        break;
    }
    return;
}

這個方法雖然解決了中斷邏輯臃腫的問題,但是只適合於那些觸發動作式的場景,例如當按鍵按下時可以先快速的在屏幕或其他人機交互界面上響應觸發的動作,但是該動作並未完全執行,比如在液晶屏上顯示“執行中”等,可以先解決響應速度問題,但是對於需要顯示完整動作結果的場景依然無法解決。這種情況下可以設計交互流程,提高用戶體驗。

后台臃腫問題

解決方法跟上面類似,將子任務繼續拆分,將長耗時任務拆分,將周期性的時間要求高的任務穿插在長耗時任務中,即在一個循環內高耗時任務總體執行一次,時間要求高的任務執行多次。這種解決方法增加了程序維護成本,並且不是所有的任務都可以被有效拆分,高時效任務的執行周期不固定,因此並不是一個很有效的解決手段。

時間片模型

模型介紹

由於前后台模型在周期性任務上存在上面提到的問題,因此出現了時間片模型來解決這個問題,時間片模型簡單地說就是通過時間分割調整不同任務的執行順序,保證周期性任務時間上的滿足:

程序上只需要在循環中執行調度器即可:

/*--------------------主函數-----------------------*/
void main(void)
{
    SCH_Init();//設置調度器
    SCH_Add_Task(任務函數名,任務調度延遲,任務調度周期);//將任務加入調度器的任務隊列
    SCH_Start();//刷新任務隊列
    while(1)
    {
        SCH_Dispatch_Tasks();  //執行任務調度器
    }
}
/*-------------------定時中斷函數---------------------*/
Void SCH_Update(void)   interrupt
{
    //刷新任務隊列
}

例如5ms一個周期的任務和100ms一個周期的任務可以通過一個5ms的定時器,通過計數的周期進行判斷應該執行哪個任務,這里類似RTOS的任務設計,可以解決前后台模型中任務的時間要求問題。

存在的問題

時間片模型的問題十分明顯:

  • 一個任務只有被執行完后才可以執行下一個,這意味着當前任務的執行時間不能超過分配的時間片段,如果超過了時間范圍還未退出,則會被定時器中斷所打斷,並執行下一個任務,同時當前任務被中斷,直到下次釋放,執行周期出現混亂

在這種情況下只有將任務進行合理的拆分,設計好程序的執行周期,才可以解決這個問題。

一種通用的處理模型

從任務的時效性上可以將所有可執行的任務分為以下三種:

  • 及時型任務:當輸入來臨時必須快速響應,例如交互界面
  • 定時型任務:一般無外部輸入,但是對時間的周期性較為敏感,例如數據采集等
  • 后台型任務:對時間周期性不敏感,能夠定期刷新即可,如日志記錄,狀態上報等

這三類任務中,可以將及時型任務處理放在中斷中,並賦予最高的中斷優先級,即當及時型任務觸發時,其他任務必須暫停。

這里存在一個優先級的過程,如果外部響應的優先級沒有定時任務的優先級高,則也可以考慮在執行定時任務時將中斷關閉,有限保證定時任務。總之根據實際場景決定優先級

定時型和后台任務由時間片進行調度,定時型任務自己單獨享用一個定時器,由該定時器觸發時執行定時任務,在執行定時任務時關閉后台型任務的中斷,不響應外界輸入,當定時任務執行完畢后開啟中斷,即及時型任務可打斷后台型任務。

后台任務由另一個定時器控制時間調度,中斷優先級最低,執行過程中中斷並不關閉,即可被及時型任務和定時型任務搶斷,搶斷的優先級取決於場景,如及時任務的優先級高則可在及時任務中將總中斷關閉,退出時打開總中斷。而當定時任務執行時則將定時器中斷關閉,避免后台任務搶占定時任務。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM