這一次我們繼續講調試方法。調試是排查程序Bug的有效方法,同時也對嵌入式軟件設計的可靠性、穩定性而言至關重要。之前講的調試方法能夠打印出變量值、系統狀態,或用互動的方式去調試程序,都不能動態的在系統運行時由程序判斷變量、參數是否出錯。
而我們今天要講的斷言(assert)函數則能做到在運行時判斷參數是否超出預設值、狀態是否出錯,然后打印出出錯數據所在的源文件和行號。
那么,什么是斷言函數呢?百度百科給的定義是:“斷言(assertion)是一種在程序中的一階邏輯(如:一個結果為真或假的邏輯判斷式),目的為了表示與驗證軟件開發者預期的結果——當程序執行到斷言的位置時,對應的斷言應該為真。若斷言不為真時,程序會中止執行,并給出錯誤信息。“
接下來,我們繼續采用上一次實時跟蹤調試的例子,加入斷言函數對運行過程的參數進行判斷,看看斷言函數如何應用,有什么效果。
1. CubeMX設置
我們可以在CubeMX中打開例子工程中的.ioc文件,按下圖進行設置。
除此之外,可以直接在CubeIDE的工程屬性里定義一個宏USE_FULL_ASSERT,也可以在工程任意頭文件中定義這個宏,效果是一樣的。其實采用CubeMX配置之后,就是在工程的stm2f7xx_hal_conf.h頭文件中定義了這個宏。
2. 修改代碼
當定義了宏USE_FULL_ASSERT之后,assert_failed函數就能參與編譯了,這個函數在main.c的最下邊。這個函數的代碼如下:
void assert_failed(uint8_t *file, uint32_t line)
{
printf("Wrong parameters value: file %s on line %drn", file, (uint16_t)line);
}
斷言失敗的話則會執行這個函數,利用printf打印一條消息,這里我們用的是CubeIDE的ITM模塊向外打印,打印的消息里包含斷言失敗語句所在的源文件及行數。
要注意的是,參數line本來是無符號長整形,printf函數用%d對應長整形的話會給警告,所以做了一個強制類型轉換,變為無符號短整型。我想應該不會有一個源文件超過65535行吧,那是要挨打的。
接下來在main.h里定義一個宏IS_PARA_COUNTER_OK,當然名字可以自己任意取。
#define IS_PARA_COUNTER_OK(para) (para < 5)
這個宏的其實是個表達式,用以對para參數的值進行判斷,這里假設para的值小于5是正常的。為了防止出錯,表達式用小括號括起來了。
在main函數while循環開始的地方,我們加上一條語句,用來對我們設置的一個用來計數的變量counter進行參數斷言。
assert_param(IS_PARA_COUNTER_OK(counter));
其中,assert_param是在stm2f7xx_hal_conf.h中定義的一個宏。
#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *) FILE , LINE ))
意思是當expr表達式的值為真的時候,不執行任何操作,為假時,斷言失敗,執行assert_failed函數,并向該函數傳遞斷言失敗語句所在的源文件和行。__FILE__和__LINE__都是C語言定義的宏,分別代表當前源文件和所在行。
我們在main函數中寫的斷言語句可以完全展開如下:
(((counter < 5)) ? (void)0U : assert_failed((uint8_t *)"D:workspaceSTM32F7example2_ITMCoreSrcmain.c", 101))
是的,這條語句位于main.c的101行。
3. 調試結果
代碼修改好后,連接好開發板,構建工程,進入調試模式并開始運行,我們可以在SWV ITM Data Console窗口看到如下信息。
這里要說明一下,代碼里counter值是在打印之后加1的,也就是說在打印出4之后,其值已經變為5,導致參數斷言出錯,打印出預設消息。另外我們也可以在assert_failed函數里加入一個死循環,斷言失敗后程序就不會繼續往下執行了。
-
C語言
+關注
關注
180文章
7604瀏覽量
136713 -
狀態機
+關注
關注
2文章
492瀏覽量
27533 -
HAL庫
+關注
關注
1文章
121瀏覽量
6220
發布評論請先 登錄
相關推薦
評論