STM32库函数断言机制宏定义assert_param(expr)和assert_fail。。。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
STM32库函数断⾔机制宏定义assert_param(expr)和
assert_fail。
⾸先我们先了解⼀下,什么是断⾔?断⾔都有什么特点?
断⾔就是我们⼈为定义的⼀个宏,⽤于判断⼀些输⼊参数的布尔表达式是否为预设的值的范围内,如果是就为真,否则就为假。
断⾔就是⽤于检查⼀些函数的输⼊参数的合法性。
⼀般默认情况下,断⾔的功能是关闭的,在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 ------------------------------------------------------- */
16void 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 ------------------------------------------------------- */
16void 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,如下所⽰:
1void assert_failed(uint8_t* file, uint32_t line)
2 {
3 printf("Wrong parameters value: file %s on line %d\r\n", file, line);
4while(1);
5 }
注意printf是通过串⼝1输出的,因为我们已经将输出重定位到串⼝1了。
再编译⼯程,⽆错误⽆警告。
现在我们⼈为修改⼀些不合法的参数,⽐如⽂件“led.c”中,函数LED_Init的第15⾏代码屏蔽掉,增加16⾏代码,然后将GPIO_Pin_9 | GPIO_Pin_10⽤0x0替代,请参考如下代码:
1void 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__)替换,并且将会在串⼝输出警告。
重新编译⼯程,⽆错误⽆警告。
上电,打开串⼝调试助⼿,如下图所⽰:
跟我们预想的⽂件名和⾏号是⼀致的。
如有疑问,或者附件链接失效,请联系我个⼈邮箱:vivohan@,个⼈微信:vivohan。