| 對于嵌入式軟件的開發(fā)人員而言,“printf調試(printf-debugging)”這個術語描述了將調試字符串從嵌入式目標空閑的串口壓出,并在運行于宿主工作站的終端模擬器上顯示結果的常見方法。 出于這個目的,許多程序員更喜歡使用有名的printf() C語言庫函數,因為它在將文本輸出和數據組合成單個函數調用上具有靈活性。但是,不在嵌入式系統(tǒng)中使用printf()無外乎兩個常見的原因:不是因為printf()太慢了,就是因為它太大了。 事實上,很容易就會碰到這些局限性。如果你看一下標準C語言庫的規(guī)范,原因就很明顯了。Printf()必須處理大量的數據格式,包括字符串、字符、(各種長度的有符號和無符號)數字,以及浮點值。此外,格式字符串可以包括用于更改文本對齊、基數、間距、字段寬度和精度的調節(jié)器和指示器。很清楚的是,任何支持整個規(guī)范的代碼都會是冗長和繁重的。 嵌入式系統(tǒng)庫的提供商意識到了這個問題,于是提供了只使用整數的printf()實現。這就通過去掉不必要的浮點支持而稍稍改善了這種狀況。但是即使如此,這些實現對于許多缺乏內存的嵌入式系統(tǒng)來說仍然太大了。 在你每天的編程工作中,你到底需要多少printf()的規(guī)范?可能只需要“%s”、“%d”,以及“%x”?所以還有另一個選擇:編寫能夠滿足需要的你自己的最小printf()函數。 這會帶來相當大的好處。例如,在最近一個項目里,我替換掉了一個由制造商所提供的printf()庫,它需要超過20 KB的內存(這超過了可用內存的一半),而換上了一個小巧的只支持必要特性的自定義版本,它只需要不到500字節(jié)的內存空間。 小巧的printf() 下面是替換一個非;镜膒rintf()的例子: #include <stdarg.h> intprintf(const char *fmt, ...) { const char *s; int d; char buf[16]; va_listap; va_start(ap, fmt); while (*fmt) { if (*fmt != %) { putchar(*fmt++); continue; } switch (*++fmt) { case s: s = va_arg(ap, const char *); for ( ; *s; s++) { putchar(*s); } break; case d: d = va_arg(ap, int); itoa(d, buf, 10); for (s = buf; *s; s++) { putchar(*s); } break; /* Add other specifiers here... */ default: putchar(*fmt); break; } fmt++; } va_end(ap); return 1; /* Dummy return value */ } 下面是一些值得注意的事情: 為了節(jié)省空間,這個簡單的printf()只支持“%s”和“%d”格式的分類符,而不需要任何指示器或者其他調節(jié)器。擴展這個函數以支持其他分類符是相對較簡單的。 C語言的標準要求printf()應該返回輸出字符串的數量。這個被返回的值通常被忽略掉了,所以這個輕型的printf()就不會勞心去計算它了,而是返回一個偽值。 一般來說,putchar()這個函數會將字符發(fā)送到串口,更通行的方法是通過由中斷驅動的串口輸出緩沖區(qū)以避免延遲。 首標文件<stdarg.h>是一個標準的庫首標文件,它為訪問傳遞給printf()的變量自變量列表提供小巧的宏,通常是通過系統(tǒng)堆棧。 更進一步優(yōu)化 要注意,控制權在你手里,你可以在感覺需要的情況下做出任何優(yōu)化,并輕易地添加你所需要的特性。 你要注意,上面的代碼使用itoa()將整數轉換成字符串。這個函數(為了簡化的目的沒有列出來)涉及重復劃分(repeated division),而且在很多嵌入式處理器上運行得相對較慢。 下面是你可以考慮的幾種優(yōu)化方法: 添加一個#define,它會控制%d是否將整數顯示為十進制或者十六進制數。如果你使用簡單的位移(bit-shift)而不是長的劃分來實現到十六進制數(等同于使用“%x”分類符)的轉換,那么這一轉換在大多數處理器上會快得多。然后你就能夠在編譯的時候為“%x”取得輸出格式:快速的十六進制數或者緩慢的十進制。 將整數到文本冗長的轉換轉移到工作站上。例如,當碰到“%x”分類符的時候,嵌入式處理器能夠輕易地發(fā)送一個未使用的ASCII碼,比如說0x01,并跟有原始的整數字節(jié)。不幸的是,這種方法需要一個在你工作站上運行的自定義終端程序,以解碼它所獲得的字節(jié)流,并在顯示字符串之前實現必要的整數到文本的轉換。 |