我想嘗試使用 printf()
調試基於微控制器的項目是一件壞事。
我知道您沒有預定義的輸出位置,並且它可能會消耗寶貴的密碼。同時,我已經看到人們使用一個自定義 DEBUG_PRINT()
宏來消耗UART TX引腳輸出到IDE終端。
我想嘗試使用 printf()
調試基於微控制器的項目是一件壞事。
我知道您沒有預定義的輸出位置,並且它可能會消耗寶貴的密碼。同時,我已經看到人們使用一個自定義 DEBUG_PRINT()
宏來消耗UART TX引腳輸出到IDE終端。
使用printf()可能會帶來一些缺點。請記住,“嵌入式系統”的範圍可以從具有數百字節程序存儲器的東西到具有千兆字節RAM和TB級非易失性存儲器的成熟的機架安裝QNX RTOS驅動的系統。
它需要某個地方來發送數據。也許您已經在系統上具有adebug或編程端口,也許您沒有。如果您不這樣做(或您所擁有的那個人沒有工作),那麼它就不是很方便。
這並不是在所有情況下的輕量級功能。如果您的微控制器只有幾個K的內存,這可能是一件大事,因為在printf中進行鏈接可能會自己吞噬4K。如果您擁有32K或256K微控制器,那可能不是問題,更不用說擁有大型嵌入式系統了。
查找與內存分配或中斷有關的某些類型的問題很少或沒有用,並且可以在不包含語句的情況下更改程序的行為。
它對於處理對時間敏感的東西是毫無用處的。最好使用邏輯分析儀和示波器,協議分析儀甚至是模擬器。
如果您的程序很大,則必須重新編譯很多次更改printf語句並對其進行更改時,可能會浪費大量時間。
這有什麼好處-這是一種以預格式化的方式輸出數據的快速方法,每個C程序員都知道如何使用-零學習曲線。如果您需要為正在調試的Kalman濾波器吐出一個矩陣,最好以MATLAB可以讀取的格式吐出它。這肯定比在調試器或仿真器中一次查看RAM位置要好。 。
我認為這不是箭袋中的無用箭頭,但應與gdb或其他調試器,仿真器,邏輯分析儀,示波器,靜態代碼分析工具,代碼覆蓋率工具和以此類推。
除了一些其他好的答案外,以串行波特率將數據發送到端口的行為相對於循環時間而言可能會非常緩慢,並且會影響其餘程序的功能(例如可以進行任何調試過程)。
正如其他人告訴您的那樣,使用這種技術沒有什麼“不好”的地方,但是與許多其他調試技術一樣,它也有其局限性。只要您知道並能夠解決這些局限性,它便可以非常方便地幫助您正確編寫代碼。
嵌入式系統具有一定的不透明性,通常使調試過程有些困難。一個問題。
嘗試在微控制器上使用 printf
時會遇到兩個主要問題。
首先,將輸出管道傳輸到正確的端口可能很麻煩。不總是。但是某些平台比其他平台困難。某些配置文件的文檔可能不完善,可能需要進行大量實驗。
第二個是內存。完整的 printf
庫可以是BIG。有時您不需要所有格式說明符,但可以使用專門的版本。例如,由AVR提供的 stdio.h
包含三種不同的大小和功能不同的 printf
。
由於所有提到的功能的完整實現都變得很大,因此可以使用鏈接器選項選擇
vfprintf()
的三種不同樣式。默認的vfprintf()
實現除浮點轉換之外的所有上述功能。提供了vfprintf()
的最小版本,該版本僅實現了非常基本的整數和字符串轉換功能,但是只能使用轉換標誌(這些標誌指定#
附加選項)可以從格式規範中正確解析,但隨後將其忽略)。
我有一個實例,其中沒有庫可用,並且內存最少。因此,我別無選擇,只能使用自定義宏。但是是否使用 printf
確實是滿足您要求的一種。
要補充Spehro Pefhany所說的“對時間敏感的東西”:讓我們舉個例子。假設您有一台陀螺儀,您的嵌入式系統每秒從該陀螺儀進行1000次測量。您想調試這些測量,所以需要將它們打印出來。問題:將它們打印出來會導致系統太忙,無法每秒讀取1,000個測量值,這會導致陀螺儀的緩衝區溢出,從而導致讀取(並打印)損壞的數據。因此,通過打印數據,您已損壞了數據,使您認為實際上可能沒有讀取數據時存在錯誤。所謂的heisenbug。
不使用printf()進行調試的更大原因是它通常效率低下,不足且不必要。
效率低下:相對於小型微控制器上可用的功能,printf()和kin使用大量閃存和RAM,但實際調試中效率較低。更改記錄的內容需要重新編譯和重新編程目標,這會減慢該過程。它還會用完一個UART,否則您可能會用它來做有用的工作。
不足:通過串行鏈接可以輸出的細節太多。如果程序掛起,則您不知道確切的位置,只有最後完成的輸出。
不必要:可以對許多微控制器進行遠程調試。 JTAG或專有協議可用於暫停處理器,查看寄存器和RAM,甚至更改正在運行的處理器的狀態而無需重新編譯。這就是為什麼即使在具有大量空間和功能的PC上,調試器通常也比打印語句更好的調試方法。
不幸的是,對於新手來說,最常見的微控制器平台Arduino沒有調試器。 AVR支持遠程調試,但是Atmel的debugWIRE協議是專有的,沒有記錄。您可以使用官方的開發板進行GDB調試,但是如果您有,那您可能就不必再擔心Arduino了。
printf()不能單獨工作。它調用了許多其他函數,如果堆棧空間很小,則可能根本無法使用它來調試接近堆棧限制的問題。取決於編譯器和微控制器,格式字符串也可以放置在內存中,而不是從閃存中引用。如果您在代碼中加上printf語句,則可能會加起來很大。在Arduino環境中,這是一個大問題-使用數十或數百個printf語句的初學者突然遇到了看似隨機的問題,因為他們正在用堆棧覆蓋其堆。
即使想要將數據吐到某種形式的日誌記錄控制台中, printf
函數通常也不是一種很好的方法,因為它需要檢查傳遞的格式字符串並進行解析它在運行時;即使代碼從未使用%04X
之外的任何格式說明符,控制器通常也將需要包含解析任意格式字符串所需的所有代碼。根據使用的確切控制器,使用類似以下代碼的代碼可能會更有效:
void log_string(const char * st){int ch;做{ch = * st ++;如果(ch == 0)中斷; log_char(ch); } while(1);} void log_hexdigit(unsigned char d){d& = 15;如果(d > 9)d + = 7; log_char(d +'0');} void log_hexbyte(unsigned char b){log_hexdigit(b >> 4); log_hexdigit(b); } void log_hexi16(uint16_t s){log_hexbyte(s >> 8); log_hexbyte(s); } void log_hexi32(uint32_t i){log_hexbyte(i >> 24); log_hexbyte(i >> 16); log_hexbyte(i >> 8); log_hexbyte(i); } void log_hexi32p(uint32_t * p)//在指針小於32位的平台上{log_hexi32(* p); }
在某些PIC單片機上, log_hexi32(l)
可能需要9條指令,並且可能需要17條指令(如果 l
位於第二行),而 log_hexi32p(&l)
將花費2。 log_hexi32p
函數本身的長度約為14條指令,因此,如果調用兩次,它將為自己付出代價
其他答案都沒有提到的一點:在基本的微型程序(IE中,只有main()循環,並且可能隨時運行幾個ISR,而不是多線程OS),如果它崩潰了/停止/陷入循環,您的打印功能將不發生。
此外,人們還說過“不要使用printf”或“ stdio.h”空間”,但沒有太多替代方法-Embedded.kyle提到了簡化的替代方法,而這正是在基本的嵌入式系統上理所當然應該做的事情。從UART中噴出幾個字符的基本例程可能是幾個字節的代碼。