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。
转载于:https://www.cnblogs.com/vivohan/p/8470680.html
STM32库函数 断言机制 宏定义assert_param(expr)和assert_failed的使用方法相关推荐
- c语言assert_param,STM32断言机制assert_param()宏定义
我们在学STM32的时候函数assert_param出现的几率非常大,上网搜索一下,网上一般解释断言机制,做为程序开发调试阶段时使用. 下面我就谈一下我对这些应用的看法,学习东西抱着知其然也要知其所以 ...
- 单片机\程序中的那些宏定义
1.字符串的数据长度,用sizeof求数据长度并用宏定义表示 uint8_t buf[]="Hello,I am STM32\r\n"; 求字符串数组buf的数据长度: uint ...
- VC预处理指令与宏定义的妙用
VC中预处理指令与宏定义的妙用 刚接触到MFC编程的人往往会被MFC 向导生成的各种宏定义和预处理指令所吓倒,但是预处理和宏定义又是C语言的一个强大工具.使用它们可以进行简单的源代码控制,版本控制,预 ...
- STM32: startup_**.s、Core_cm3.c、宏定义、HAL库
.s 启动文件选择 给STM32写程序时,我们需要在工程文件中加入厂家提供的启动文件(这里以STMf10x系列为例),里面包含的是启动代码,启动代码是一段和硬件相关的汇编代码.是必不可少的!这代码主要 ...
- Linux宏定义实现类成员函数,全面解析Linux内核的同步与互斥机制
http://blog.csdn.net/sailor_8318/archive/2008/06/30/2599357.aspx [摘 要]本文分析了内核的同步及互斥的几种机制:原子运算符(atomi ...
- c++ 带参数的宏定义实现反射机制
lua 这种脚本语言用久了,总觉得反射机制就应该理所当然的嵌入在语言特性里. 比如希望根据自己传的类型名变量,动态去 new 一些实例.在 lua ,js 里做起来就非常简单,然而在 c++里面做起来 ...
- process调用protothread机制的相关宏定义——用HelloWorld进程诠释
一.HelloWorld例子 #include "contiki.h"#include <stdio.h> /* For printf() */ /*--------- ...
- STM32的C语言重点知识(1.C语言数据类型+2.C语言宏定义+3.C语言typedef+4.C语言结构体+5.C语言枚举)
1.C语言数据类型: 注:目的是看到stdint,如看到int8_t;uint16_t能够瞬间知道表示的是char,8字节:unsigned short,16字节. ST关键字意思是在老版本的引脚说明 ...
- C++ 标准库函数与宏定义的名字冲突
转至 http://blog.csdn.net/rnamatrix/article/details/5765462 今天在使用limits中的numeric_limits类模板函数max和min时, ...
- STM32 理解宏定义的重要性
今天做STM32F107系列单片机与ADM2587E的开发时,遇到了一个很郁闷的问题,通过串口发送数据后,单片机接收端的指示灯亮,但没我有返回值(通过接收标志发送数据).代码反反复复检查好几遍就是找不 ...
最新文章
- @程序员:Python 3.8正式发布,重要新功能都在这里
- 基于SSL的mysql(MariaDB)主从复制
- Reveal使用心法
- Exchange与ADFS单点登录 PART 3:部署和配置WAP
- 制作模块-安装模块压缩包
- pdf.js 利用HTML5技术显示pdf内容
- 查看 rabbitmq 启动websocket 提示404_RabbitMQ在windows下安装(笔记)
- Open-Falcon 监控系统监控 MySQL/Redis/MongoDB 状态监控
- 【报告分享】2020年中国企业直播服务市场研究报告.pdf(附下载链接)
- 年薪30W前端程序员,需要吃透的前端书籍推荐
- cairo在Gecko上实现的路线图
- 【转载】通过SQL获取MSSQL的数据库相关信息收藏
- nodejs基础 -- 全局对象
- 美团外卖返利小程序-饿了么外卖返利公众号系统– 程序侠
- 爬虫项目十六:用 Python 三十行代码采集QQ群成员信息,很简单
- matlab图例使用技巧
- cd rom是计算机的,CD-ROM是什么意思,CD-ROM是什么意思
- 距离最短原则的离散点连接 Python实现
- python 爬取微信朋友圈的一些信息
- 黑马程序员顺义校区php_PHP面向对象开发视频教程[黑马程序员]
热门文章
- Oracle SQL注入常用语句
- C#编程(四十)----------运算符重载
- 带checkbox的ListView实现(一)——数据与渲染完全分离的传统实现方式
- WPF 中依赖属性的继承(Inherits)
- 稳扎稳打Silverlight(18) - 2.0视频之详解MediaElement, 开发一个简易版的全功能播放器...
- 波形分析--SPI数据
- keepalived+mysql双主高可用配置
- 推荐iOS模拟器截图工具iOS-Simulator Cropper
- 去掉字符串不需要的HTML标记(正则表达式)
- DWM1000 收发RXLED TXLED控制代码修改