寫程式的人對於 printf( )這個函式應該不陌生,用它印變數真是再方便不過了,除了印文字還可以印數字,還包含了各種格式,整數、浮點、十六進位等等一應俱全。但若遇到沒辦法使用 printf( )的情況時,該怎麼辦呢?
阿信助教就碰到了,因為微處理器(MCU, Micro Controller Unit)的flash memory很小,只有幾K而已,而我的程式碼已經寫到瀕臨memory的邊界,只要再多寫幾行code記憶體就會爆掉,尤其是 printf( )只要一加進去,記憶體馬上就爆了,這真的很傷腦筋。
printf 很佔空間
平常寫PC的程式,記憶體動輒以Giga Byte來計算,大家也許不會感覺到memory空間的限制;而MCU的程式,由於記憶體空間只有4K~16KByte等級,因此程式大小錙銖必計能省則省。每呼叫一次函式,底層的機械碼就會產生一堆的push/pop指令,如果參數很多則占用的記憶體數量就很可觀,不僅如此printf還用了動態參數,讓參數的數量可以任意變動(如下方範例),為了彈性便需要吃掉更多空間,在記憶體捉襟見肘的情況下,若沒有printf該如何來做debug呢?
//printf可使用不定個數的參數, 舉例如下 printf("%d",a); //1個參數可以使用 printf("%d, %x",a,b); //2個參數, 也可以
Simulator用不上
一定有人會想,若printf不能用那就改用模擬器Simulator的step run單步執行就好了,只是如果一切都如此美好就好了。因為開發環境內的模擬器它再厲害也只能模擬正常情況。
但我要處理的是偶發性的異常事件,必須要抓到問題才能避免MCU當機。例如故意將USB或I2C短路,造成程式異常,這可是模擬器沒辦法模擬的。
那為何我要故意將他們短路,因為實際在run的情況之下,這些周邊通訊電路就是有可能因為EMI而有機會發生異常,由於不是每次EMI都會發生干擾,我沒那麼多時間在那守株待兔,所以直接將I2C短路讓它異常,效果直接而且狀況跟被EMI干擾的結果相同。
GPIO要好好利用
既然我們需要某種output來做為debug的工具,若不能用printf那還有甚麼方法可以做輸出呢? 還好微處理器的IO(Input/Output)很多,只要設定得當,就可以利用GPIO(General Purpose Input Output)來輸出電壓當作debug訊號,之後再接上示波器就能看到電壓的變化,所以你可以預先自行定義GPIO的high與low各是甚麼意義,這就可以當作debug的輸出。
由於GPIO只有High與Low兩種狀態,若想要在程式碼內超過兩個不同地方安插debug訊號,可以塞進數個由high/low組合而成的脈衝,請參考下面的寫法,用來觀察程式是否有跑到預期的地方。這裡是以CY7C68013A為例,我利用GPIO PortC的PC.0的腳位來輸出電壓變化。
如下範例,我寫了三種脈衝,分別會產生1根pulse、2根pulse以及3根pulse,將它們放在程式中的不同位置,來代表程式中的不同階段,萬一有function當掉,看示波器的波形就馬上能知道是哪個函式出問題。
void test(void) { func1(); //產生一個脈衝 IOC=(IOC&0xFE)| 0; IOC=(IOC&0xFE)| 1; //只有這裡是1, 其他都是0 IOC=(IOC&0xFE)| 0; func2(); //產生2個脈衝 IOC=(IOC&0xFE)| 0; IOC=(IOC&0xFE)| 1; //這裡是1 IOC=(IOC&0xFE)| 0; IOC=(IOC&0xFE)| 1; //這裡是1 IOC=(IOC&0xFE)| 0; func3(); //產生3個脈衝 IOC=(IOC&0xFE)| 0; IOC=(IOC&0xFE)| 1; //這裡是1 IOC=(IOC&0xFE)| 0; IOC=(IOC&0xFE)| 1; //這裡是1 IOC=(IOC&0xFE)| 0; IOC=(IOC&0xFE)| 1; //這裡是1 IOC=(IOC&0xFE)| 0; }
光看程式可能很難想像結果,實際在示波器上看到的GPIO波形會是甚麼樣子呢?
就是如下圖這個樣子,框起來的地方分別是三種脈衝訊號的波形。當然如果你擔心脈衝訊號彼此太靠近,會容易搞不清楚到底誰跟誰是一組的話,就在脈衝前後多加幾個0(也就是low),就能把距離拉開了。
那也許你說為何不用for loop來寫脈衝? 這樣程式不是更省? 其實並沒有,我確實有試過用for loop來取代hard coding,但我發現只要用了for這個關鍵字,程式就會變胖,但我的code size已經在臨界點了,這個for會變成壓垮駱駝的最後一根稻草,因此我全部用hard codeing。
想當年只靠一顆LED除錯
依稀記得多年前正在研究一個MCU的時候,由於它體積很小pin腳也少,少到連UART都不見了,這下可好,我變成瞎子了。所以當時我只能先用一根GPIO接上LED確認程式會動之後,再慢慢接出其他的GPIO來輔助debug,於是就變成了用一顆LED來除錯的工程師,現在想起來都覺得很瘋狂。但是面對時程的壓力,身為工程師當然是要想盡辦法達標,才能擦亮自己的招牌啊。
一顆LED除錯的故事不只發生在你身上…(拍拍
原來我有同伴