古老、經過驗證且真正的調試技術是在整個嵌入式軟件中散布printf語句,以期獲得對系統行為的洞察力。嵌入式開發人員使用printf并不總是可取的,并且可能會對系統產生不可預見的實時影響。讓我們檢查一下printf的基本問題,然后是一些可用于從中獲得最大性能的技術。
了解printf的問題
使用printf會帶來一些開發人員經常忽略的問題。首先,開發人員必須引入一個標準的C庫,這無疑會增加ROM和RAM的使用。其次,每次使用printf stamen 時,系統都會阻塞,直到所有字符都已傳輸完畢,這會導致實時性能顯著下降。舉個例子,輸出一個簡單的字符串,例如“Hello World!”在9600處打印出UART(仍然很常見)。例如在STM32上執行了一個簡單的時序測量,如圖1所示,格式化字符串并打印到終端需要12.5毫秒。
圖 1 – 打印“Hello World!”
添加任何字符串格式會使情況變得更糟!使用 printf(“The system state is %d”, State) 將系統狀態打印到終端會導致21毫秒的應用程序延遲,因為字符串被格式化和傳輸。有人可能會爭辯說,以9600波特運行是荒謬的,但即使增加到 115200仍然會分別導致傳輸這兩條消息的時間分別為1.05和1.75毫秒。大量處理器帶寬和潛在的實時性能會影響最少的有用信息。
性能技巧 1 – 創建非阻塞printf
如果printf 版本是阻塞類型,嵌入式開發人員一旦調用 printf,應用程序就會停止執行,直到每個字符都被成功傳輸,效率低得驚人!另一種方法是創建一個非阻塞版本,非阻塞printf版本將
格式化字符串
將格式化的字符串填充到傳輸緩沖區中
啟動第一個字符的傳輸
讓中斷服務程序處理發送緩沖區中的剩余字符
繼續執行代碼
非阻塞printf的最大亮點是設置時間,在9600波特的STM32上它在0.8到1.8毫秒之間變化。在初始設置時間之后,發送中斷大約每毫秒發生一次,需要35微秒將下一個字符填充到UART發送寄存器中,然后再返回執行有用的工作。圖 2 顯示了周期性中斷以及中斷執行時間。請記住,執行時間不包括在這種情況下少于25個時鐘周期的中斷開銷。
圖 2 – 非阻塞 printf 性能
性能技巧 2 – 提高波特率
許多嵌入式開發人員仍將他們的UART默認設置為9600,今天的串行硬件可以處理1 Mbps 或更高的波特率!有些足夠大膽的人將波特率設置為115200。除非運行時鐘存在潛在的電氣或硬件相關問題,否則將波特率設置為1 Mbps并將調試消息輸出為盡可能快,以盡量減少實時性能問題。 “Hello World!”的原始阻塞 printf只會阻塞120微秒,遠比12.5毫秒更可接受。
性能技巧3 – 使用SWD
現代微控制器在開發芯片時考慮到了printf性能問題。例如,利用ARM Cortex-M 部件的調試功能的開發人員可以跳過UART并使用內部調試模塊將printf消息通過調試器傳輸回IDE。以這種方式跳過UART不僅可以節省設置,而且內部硬件機制可以最大限度地減少軟件開銷!內部緩沖區充滿消息,調試硬件自動處理傳輸到調試探針的傳輸,從而將對應用程序實時性能的影響降至最低。
結論
很少有嵌入式開發人員會放棄他們最喜歡的、嘗試過的、真正的printf調試技術。在當今的現代微控制器硬件中,有多種選項可以提高printf的性能和效率,從而最大限度地減少對實時性能的影響。