STM32庫函數 斷言機制 宏定義assert_param(expr)和assert_failed的使用方法


  首先我們先了解一下,什么是斷言?斷言都有什么特點?

  斷言就是我們人為定義的一個宏,用於判斷一些輸入參數的布爾表達式是否為預設的值的范圍內,如果是就為真,否則就為假。斷言就是用於檢查一些函數的輸入參數的合法性。一般默認情況下,斷言的功能是關閉的,在debug調試模式下,將斷言功能打開;在release發布模式下,將斷言功能關閉,斷言打開的話,會在一定程度上影響函數的執行效率。

  使用斷言,可以創建更加穩定,不易出錯的代碼。如果在單元測試過程中,使用斷言,將會非常方便。使用斷言得區別於代碼錯誤,代碼錯誤編譯就會不通過,但是斷言有問題,代碼編譯是OK的。斷言檢查的就是在代碼執行過程中,一些輸入參數的合法性是否為真。

  斷言就是在debug模式下,代碼運行過程中,對函數中輸入的參數進行檢查。如果輸入的參數違規,將進行某些操作,輸出一些信息提醒,或者控制代碼進入一個死循環使得代碼無法繼續執行下去。在release版本,是不用斷言功能的。

  下面我們用STM32F407ZGT6的工程來解釋斷言的用法。我們使用的是STM32的固件庫版本是3.5,使用的編譯環境是keil MDK V5.24A,斷言檢測出異常的文件名和行號會通過串口輸出,並將終止代碼執行進入一個死循環。

  如下的代碼摘自文件“stm32f4xx_conf.h”

 1 /* #define USE_FULL_ASSERT    1 */
 2 
 3 /* Exported macro ------------------------------------------------------------*/
 4 #ifdef  USE_FULL_ASSERT
 5 
 6 /**
 7   * @brief  The assert_param macro is used for function's parameters check.
 8   * @param  expr: If expr is false, it calls assert_failed function
 9   *   which reports the name of the source file and the source
10   *   line number of the call that failed. 
11   *   If expr is true, it returns no value.
12   * @retval None
13   */
14   #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
15 /* Exported functions ------------------------------------------------------- */
16   void assert_failed(uint8_t* file, uint32_t line);
17 #else
18   #define assert_param(expr) ((void)0)
19 #endif /* USE_FULL_ASSERT */

第1行代碼 ,默認情況下斷言是關閉的,已經把“#define USE_FULL_ASSERT 1 ”注釋掉,說明USE_FULL_ASSERT未被定義。

 

如果需要打開斷言的功能,需要將“#define USE_FULL_ASSERT 1 ”注釋去掉,如下代碼所示

 1 #define USE_FULL_ASSERT    1 
 2 
 3 /* Exported macro ------------------------------------------------------------*/
 4 #ifdef  USE_FULL_ASSERT
 5 
 6 /**
 7   * @brief  The assert_param macro is used for function's parameters check.
 8   * @param  expr: If expr is false, it calls assert_failed function
 9   *   which reports the name of the source file and the source
10   *   line number of the call that failed. 
11   *   If expr is true, it returns no value.
12   * @retval None
13   */
14   #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
15 /* Exported functions ------------------------------------------------------- */
16   void assert_failed(uint8_t* file, uint32_t line);
17 #else
18   #define assert_param(expr) ((void)0)
19 #endif /* USE_FULL_ASSERT */

第1行,宏定義常量USE_FULL_ASSERT的值為1

第14行,是一個宏定義assert_param(expr),通過一個條件判斷語句,如果表達式expr的值為真,則assert_param(expr)返回(void)0,如果表達式expr的值為假,則assert_param(expr)返回assert_failed((uint8_t *)__FILE__, __LINE__)。

第16行,函數聲明void assert_failed(uint8_t* file, uint32_t line);這個函數的作用就是返回調用這個函數的文件名和行數。

斷言打開之后,編譯工程出現一個錯誤如下:

..\OBJ\Template.axf: Error: L6218E: Undefined symbol assert_failed (referred from misc.o).
Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
Finished: 2 information, 0 warning and 1 error messages.
"..\OBJ\Template.axf" - 1 Error(s), 0 Warning(s).
Target not created.
Build Time Elapsed: 00:00:00

 

通過編譯錯誤的說明提示,函數assert_failed沒有定義,我們在main.c文件中定義函數assert_failed,如下所示:

1 void assert_failed(uint8_t* file, uint32_t line)
2 {
3  printf("Wrong parameters value: file %s on line %d\r\n", file, line);
4  while(1);
5 }

注意printf是通過串口1輸出的,因為我們已經將輸出重定位到串口1了。

 

再編譯工程,無錯誤無警告。

 現在我們人為修改一些不合法的參數,比如文件“led.c”中,函數LED_Init的第15行代碼屏蔽掉,增加16行代碼,然后將GPIO_Pin_9 | GPIO_Pin_10用0x0替代,請參考如下代碼:

 1 void LED_Init(void)
 2 {         
 3   GPIO_InitTypeDef  GPIO_InitStructure;
 4 
 5   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF時鍾
 6 
 7   //GPIOF9,F10初始化設置
 8   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
 9   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通輸出模式
10   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽輸出
11   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
12   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
13   GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化
14     
15 //    GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10設置高,燈滅
16     GPIO_SetBits(GPIOF,0x0);
17 }

 

我們定位到GPIO_Pin_9宏定義的地方,如下代碼所示:

 1 #define GPIO_Pin_0                 ((uint16_t)0x0001)  /* Pin 0 selected */
 2 #define GPIO_Pin_1                 ((uint16_t)0x0002)  /* Pin 1 selected */
 3 #define GPIO_Pin_2                 ((uint16_t)0x0004)  /* Pin 2 selected */
 4 #define GPIO_Pin_3                 ((uint16_t)0x0008)  /* Pin 3 selected */
 5 #define GPIO_Pin_4                 ((uint16_t)0x0010)  /* Pin 4 selected */
 6 #define GPIO_Pin_5                 ((uint16_t)0x0020)  /* Pin 5 selected */
 7 #define GPIO_Pin_6                 ((uint16_t)0x0040)  /* Pin 6 selected */
 8 #define GPIO_Pin_7                 ((uint16_t)0x0080)  /* Pin 7 selected */
 9 #define GPIO_Pin_8                 ((uint16_t)0x0100)  /* Pin 8 selected */
10 #define GPIO_Pin_9                 ((uint16_t)0x0200)  /* Pin 9 selected */
11 #define GPIO_Pin_10                ((uint16_t)0x0400)  /* Pin 10 selected */
12 #define GPIO_Pin_11                ((uint16_t)0x0800)  /* Pin 11 selected */
13 #define GPIO_Pin_12                ((uint16_t)0x1000)  /* Pin 12 selected */
14 #define GPIO_Pin_13                ((uint16_t)0x2000)  /* Pin 13 selected */
15 #define GPIO_Pin_14                ((uint16_t)0x4000)  /* Pin 14 selected */
16 #define GPIO_Pin_15                ((uint16_t)0x8000)  /* Pin 15 selected */
17 #define GPIO_Pin_All               ((uint16_t)0xFFFF)  /* All pins selected */
18 
19 #define GPIO_PIN_MASK              ((uint32_t)0x0000FFFF) /* PIN mask for assert test */
20 #define IS_GPIO_PIN(PIN)           (((PIN) & GPIO_PIN_MASK ) != (uint32_t)0x00)

第20行,宏定義IS_GPIO_PIN(PIN),如果PIN為0,則IS_GPIO_PIN(PIN)的值就為0。

 

我們查看函數GPIO_SetBits定義的代碼,來自文件stm32f4xx_gpio.c。代碼如下:

第416行,由於我們傳進來的GPIO_Pin的值為0,所以這行的斷言代碼將會被代碼assert_failed((uint8_t *)__FILE__, __LINE__)替換,並且將會在串口輸出警告。

重新編譯工程,無錯誤無警告。上電,打開串口調試助手,如下圖所示:

跟我們預想的文件名和行號是一致的。

STM32代碼工程鏈接地址 : https://pan.baidu.com/s/1eTcdevw

如有疑問,或者附件鏈接失效,請聯系我個人郵箱:vivohan@163.com,個人微信:vivohan。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM