不知不覺已經出來工作一年,經過一年的工作,使我學了不少東西,從而使我認識到以前很多不足之處,也想把自己的程序調試經驗跟大家分享下。
首先是軟件的編寫環境,選擇一個編譯平臺對于編寫代碼效率是至關重要的,在實際開發中,基本上使用Source Insight這個軟件上編寫和根改程序代碼,本人現在使用的的是Source Insight3.5,這個軟件功能非常強大,可以幫你快速定位代碼、查看代碼變量使用及各位置調用情況等信息。顏色管理也非常出色,根據顏色可以判斷各個信息,比如變量是否定義、是局部變量還是全局變量等,可以說只要你使用過了就決不會再想在KEIL環境下編寫代碼了,當然程序很小有點感覺不出來,而對于程序量較大,且充分使用程序模塊化,就是頭文件的使用,定位程序的速度可以達到想到哪里就定位到哪里。熟悉后基本是使用各個快捷鍵進行操作,到那時KEIL就是當作一個編譯器使用了。
KEIL建工程還是很有講究的,假如代碼比較大時,而工程全部又放在同一個文件里,那這個文件里的東西就像雜貨間,找一個東西都難。如下建立一個工程簡單keil工程,這樣建的工程,在keil設置相關的頭文件調用路徑,清單與工程存放文件等設置后,整個工程的程序代碼就跟keil工程文件獨立了,這樣就對代碼的移植及備份帶來很大的方便,當然還有分得更細的工程了。這樣一來Source Insight工程里添加文件,SVN版本管理代碼就方便了。
變量及函數的定義在一個工程里命名習慣是非常重要的,做到看到函數或變量名就可以知道變量和函數的意義及作用。我一般命令根據函數功能相關的關鍵字進行命名,而關鍵字與關鍵字之間用大小寫區分,我英語也很差,但語法可以不懂,但專業英語的單詞還是要會使用的。而變量定義能用結構體的盡量使用,比如時間就可以構建一個時間結構體變量,里面包含年、月、日、時、分、秒、星期,這樣定義了一個時間就非常容易操作而且直觀。
程序的調試是寫程序最重要的環節,好的調試方法可以快速完成程序調試。以前調試就是要在板上設置一盞燈,不然程序跑到哪里出了問題都不知道,所以以前沒燈都不知道怎么樣去調試,但即使有燈調試,這樣效率也是很低的而且實際中也不太現實。使用串口打印信息來調試程序,可以完全跟TUBRO C 2.0下的printf函數格式進行信息打印,如使用%d,%s、%f、%c打印各類型變量值,而這個功能函數就是debug.c和debug.h模塊文件,在這里預編譯發揮著重要的作用,可以根據自己配置的定義去控制打印的信息,當程序調試結束后就關閉打印信息,這樣編譯器就不會編譯調試信息,這有就不用一個一個把調試代碼刪掉了,當要更改程序時,可以重新打開編譯信息,這就充分使用了C語言里的預編譯和DUBUG的使用,這就是為什么程序里存在調試版和釋放版,而調試版程序運行時,往往在串口可以看到相關的信息。斷言(assert (條件))也是檢測程序里關鍵參數一個重要調試方式,但條件不成立時打印出錯所在的文件下的第幾行和錯誤條件信息。串口調試環境構建當使能調試時要開銷一部分資源,不過現在單片機基本上有外擴RAM,從而使串口調試在51單片機上調試成為可能。
附:本人調試keil51時遇到打印char 類型出現了一些問題,比如char a=0X01,用printf(“a=%c\n”,a),串口會打印出a=0100, 用printf(“a=%d\n”,a),串口會打印出a=512,而keilARM里沒出現,應該是51keil標準庫的問題。
程序的屏蔽,以前最常用的是 “ // ”和”/**/”來注釋掉程序,//是屏蔽掉一行的代碼,當要屏蔽一段代碼時就會使用/**/來屏蔽,但往往一些注釋也用/**/來注釋,如果在屏蔽段代碼中剛好用/**/的注釋,那是問題就出現了,以前就會把代碼段里/**/該成//注釋。現在就我們可以使用#if <條件> 一段代碼 #endif來屏蔽一段代碼,當條件為真時編譯器編譯代碼,如為0則不編譯代碼,這樣就容易多了。
前后臺系統,寫程序中處理單個任務在難的功能只算是一個功能,代碼多容易寫,當任務多個給且任務看似實時的,如數碼管顯示,按鍵操作、流水燈、點陣顯示燈這樣的任務放在一起的時候,就存在時間調配問題。這些我們按鍵按下時要馬上反應、數碼顯示、點陣顯示、流水燈都是要實時處理的,比如按鍵按下不能及時反應,要麻處理按鍵時顯示停在某個狀態,而這些現象在實際中是完全可以同時出現的,而你是看不的上面的現象,而處理這樣的事件往往是*前后臺系統運行起來的,而前后臺系統就是看定時器來構建這個程序的軟中斷一樣,比如單片機里的中斷就是一個機器周期去查詢中斷的狀態位,如符合中斷,程序就會放下當前代碼去執行中斷代碼,但按鍵、數碼顯示、點陣這些對人來說是時間很短的,但對于單片機來說是很長的,有了這樣的思維后我們就可以構建一個由我們自己定的模擬軟件中斷查詢周期,這個周期就是*定時器來設置中斷間隔時間,而這個在定時器里處理的事件最好是占用時間短,比如計數、少數個賦值、狀態判斷及標記,然后后臺(main()函數里的代碼)就根據前臺(TimerTnterrupt()函數里的代碼)返回的標記狀態進行處理相應事件。這就有點像操作系統的系統節拍,簡稱為系統的心臟。