STM32库函数 断言机制 宏定义assert_param(expr)和assert_failed的使用方法

Easter79
• 阅读 726

首先我们先了解一下,什么是断言?断言都有什么特点?

断言就是我们人为定义的一个宏,用于判断一些输入参数的布尔表达式是否为预设的值的范围内,如果是就为真,否则就为假。断言就是用于检查一些函数的输入参数的合法性。一般默认情况下,断言的功能是关闭的,在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。代码如下:

STM32库函数  断言机制 宏定义assert_param(expr)和assert_failed的使用方法

第416行,由于我们传进来的GPIO_Pin的值为0,所以这行的断言代码将会被代码assert_failed((uint8_t *)__FILE__, __LINE__)替换,并且将会在串口输出警告。

重新编译工程,无错误无警告。上电,打开串口调试助手,如下图所示:

STM32库函数  断言机制 宏定义assert_param(expr)和assert_failed的使用方法

跟我们预想的文件名和行号是一致的。

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

如有疑问,或者附件链接失效,请联系我个人邮箱:vivohan@163.com,个人微信:vivohan。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
C++系统学习之C库assert
C库之<cassertassert.h定义了一个作为标准调试工具的宏宏函数函数说明assertEvaluateassertion(macro)assert当使用assert()里,给它一个参数,即一个表示断言为真的表达式。预处理器产生测试该断言的代码。如果断言不为真,则发出一
Stella981 Stella981
3年前
JUnit的各种断言
JUnit为我们提供了一些辅助函数,他们用来帮助我们确定被测试的方法是否按照预期的效果正常工作,通常,把这些辅助函数称为断言。下面我们来介绍一下JUnit的各种断言。   1、assertEquals   函数原型1:assertEquals(\Stringmessage\,expected,actual) 参数说
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
3年前
STM32库函数 断言机制 宏定义assert_param(expr)和assert_failed的使用方法
首先我们先了解一下,什么是断言?断言都有什么特点?断言就是我们人为定义的一个宏,用于判断一些输入参数的布尔表达式是否为预设的值的范围内,如果是就为真,否则就为假。断言就是用于检查一些函数的输入参数的合法性。一般默认情况下,断言的功能是关闭的,在debug调试模式下,将断言功能打开;在release发布模式下,将断言功能关闭,断言打开的话,会在一定程度
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k