聽了一次培訓課-高質量C編程,受益匪淺。聽過那次培訓,我就在想,怎么形成一種自己的編程風格,怎么有一個自己的裸編架構?
通過自己查閱書籍、資料、相關帖子,有一些收獲,現(xiàn)記錄如下,以便查閱。
單片機的編程風格,我不想做過多的談論,只要代碼清晰,便于閱讀,適合自己即可。推薦一本書-----編程匠藝之編寫卓越的代碼,很不錯。
單片機裸編架構,這個是新手往往最容易忽略的問題。因為新手剛開始只是注重C語法,單片機模塊使用,等等。無可厚非,這些都是大廈的基石,沒有這些,也無從討論架構。
所有的新手都是從如下架構開始,其中的函數(shù)都是阻塞式方式:
main()
{
/****初始化函數(shù)*****/
................................
while(1)
{
task1();
task2();
......
}
}
當然這種架構是最簡單,也是最容易理解的。因為對于入門的學員來說,無非是點亮一個LED,閃爍,點亮數(shù)碼管,等簡單的任務,任務不多,實時要求又很低,所以這種架構基本都能應付。
假如我有三個任務,task1(),task2(),task3(),task3()是對于上位機的回應,對于時間上有要求。task1()是LED燈的閃爍,task2()是按鍵檢測。我們一般都是利用軟件延時函數(shù)delay_ms來做燈閃爍的延時和按鍵消抖,這中就是阻塞式方式。由于task3()是對于上位機的應答,什么時候上位機來數(shù)據(jù),不能確定,還要及時作出回應,最壞的情況是開開始執(zhí)行task1(),上位機數(shù)據(jù)就來了,作出回應的時間為燈閃爍延時+按鍵消抖延時,都是ms級(代碼執(zhí)行時間可忽略),我們不能忍受。怎么辦呢?我認為最好的辦法就是在執(zhí)行一些無用的延時指令的時候把控制權釋放掉,讓其他任務執(zhí)行。下面是我思考的方法:
對于按鍵:
方案1:把按鍵采集函數(shù)放到定時器中斷里,這樣就不需要延時了。
方案2:先做一個軟件定時器或者叫軟件延時器,當然是基于硬件定期實現(xiàn)的,按鍵函數(shù)在延時的時候不斷查詢標志位。如果這樣做,按鍵函數(shù)必須經(jīng)過改造,它要具有如下功能:能夠主動退出,并在下次調用時間能夠從上次的退出點執(zhí)行。有人說這不就是操作系統(tǒng)才能時間的功能嗎,時間任務調度?我們可用協(xié)程的方法來模擬操作系統(tǒng)的任務調度。具體實現(xiàn)方法用C語言的switch語法或者GNU的&&語法。偽代碼歷程如下:
unsigend char state=0;
unsigend chari=0;
unsigend charkey_value_read_last=常態(tài);//上次值,常態(tài)及無動作值
unsigend charkey_value_read_current=常態(tài);//當前值
unsigend charkey_value_user_last=常態(tài);//上次值
unsigend charkey_value_user_current=常態(tài);//當前值
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 !=常態(tài))key_value_user_current=key_value_read_current;//需用戶清除標志位
}
}
break;
case 1:
if(軟件定時器有效)
{
軟件定時器標志清零;
state=0;
}
break;
}
}
void KEY_USER(void)
{
if(key_value_user_current != 常態(tài))
{
key_value_user_current= 常態(tài);//清除標志位
//用戶代碼
}
}
上面的例子,就是一個按鍵函數(shù)改后的例子,由于沒有用戶棧的概念,所以函數(shù)不能保存局部變量,要么定義為static,要么用全局的,根據(jù)面向對象的思路,把按鍵函數(shù)的數(shù)據(jù)封裝為一個struct。
LDE函數(shù)也一樣。通過上面的例子我們發(fā)現(xiàn),在裸編的時候,我們主要任務是消除任務里的延時等待,或者一個任務分解為幾個小的任務,每當完成一小步,就主動釋放控制權,等待下載調用,直到完成這個任務。