微控制器裸編思考之消除軟體延時

無痕幽雨發表於2015-09-16

6月份聽了一次培訓課-高質量C程式設計,受益匪淺。聽過那次培訓,我就在想,怎麼形成一種自己的程式設計風格,怎麼有一個自己的裸編架構?

通過自己查閱書籍、資料、相關帖子,有一些收穫,現記錄如下,以便查閱。

微控制器的程式設計風格,我不想做過多的談論,只要程式碼清晰,便於閱讀,適合自己即可。推薦一本書-----程式設計匠藝之編寫卓越的程式碼,很不錯。

微控制器裸編架構,這個是新手往往最容易忽略的問題。因為新手剛開始只是注重C語法,微控制器模組使用,等等。無可厚非,這些都是大廈的基石,沒有這些,也無從討論架構。

所有的新手都是從如下架構開始,其中的函式都是阻塞式方式:

main()

{

/****初始化函式*****/

................................

while(1)

{

task1();

task2();

......

}
}

當然這種架構是最簡單,也是最容易理解的。因為對於入門的學員來說,無非是點亮一個LED,閃爍,點亮數碼管,等簡單的任務,任務不多,實時要求又很低,所以這種架構基本都能應付。

假如我有三個任務,task1(),task2(),task3(),task3()是對於上位機的迴應,對於時間上有要求。task1()是LED燈的閃爍,task2()是按鍵檢測。我們一般都是利用軟體延時函式delay_ms來做燈閃爍的延時和按鍵消抖,這中就是阻塞式方式。由於task3()是對於上位機的應答,什麼時候上位機來資料,不能確定,還要及時作出迴應,最壞的情況是開開始執行task1(),上位機資料就來了,作出迴應的時間為燈閃爍延時+按鍵消抖延時,都是ms級(程式碼執行時間可忽略),我們不能忍受。怎麼辦呢?我認為最好的辦法就是在執行一些無用的延時指令的時候把控制權釋放掉,讓其他任務執行。下面是我思考的方法:

對於按鍵:

方案1:把按鍵採集函式放到定時器中斷裡,這樣就不需要延時了。

方案2:先做一個軟體定時器或者叫軟體延時器,當然是基於硬體定期實現的,按鍵函式在延時的時候不斷查詢標誌位。如果這樣做,按鍵函式必須經過改造,它要具有如下功能:能夠主動退出,並在下次呼叫時間能夠從上次的退出點執行。有人說這不就是作業系統才能時間的功能嗎,時間任務排程?我們可用協程的方法來模擬作業系統的任務排程。具體實現方法用C語言的switch語法或者GNU的&&語法。虛擬碼歷程如下:



unsigend char state=0;

unsigend chari=0;


unsigend charkey_value_read_last=常態; //上次值,常態及無動作值

unsigend charkey_value_read_current=常態; //當前值


unsigend charkey_value_user_last=常態; //上次值

unsigend charkey_value_user_current=常態; //當前值



void KEY(void)

{

#deifne KEY_READ_TIMES10

switch(state)

{

case 0:

key_value_read_current=KEY_IO;

i++;

state++;

啟動軟體定時器;


if(key_value_read_last != key_value_read_current)//消抖

{

key_value_read_last = key_value_read_current;

i=0;

break;

}


if(i>=KEY_READ_TIMES)//獲取按鍵值

{

i=0;

if(key_value_read_current != key_value_user_last)

{

key_value_user_last=key_value_read_current;//防止使用者未鬆開按鍵,按鍵一直有效(無須做按鍵鬆開檢測)


if(key_value_user_current !=常態) key_value_user_current=key_value_read_current;//需使用者清除標誌位

}

}

break;

case 1:

if(軟體定時器有效)

{

軟體定時器標誌清零;

state=0;
}

break;

}

}

void KEY_USER(void)

{

if(key_value_user_current != 常態)

{

key_value_user_current= 常態; //清除標誌位

//使用者程式碼

}

}

上面的例子,就是一個按鍵函式改後的例子,由於沒有使用者棧的概念,所以函式不能儲存區域性變數,要麼定義為static,要麼用全域性的,根據物件導向的思路,把按鍵函式的資料封裝為一個struct。

LDE函式也一樣。通過上面的例子我們發現,在裸編的時候,我們主要任務是消除任務裡的延時等待,或者一個任務分解為幾個小的任務,每當完成一小步,就主動釋放控制權,等待下載呼叫,直到完成這個任務。

對於協程的感念,可以檢視PT協程例子,其實現思路和上面的討論是一樣的。可以檢視我的部落格:PT協程簡介


相關文章