assert_param

我们在学STM32的时候函数assert_param出现的几率非常大,上网搜索一下,网上一般解释断言机制,做为程序开发调试阶段时使用。下面我就谈一下我对这些应用的看法,学习东西抱着知其然也要知其所以然。
4 断言机制函数assert_param
我们在分析库函数的时候,几乎每一个函数的原型有这个函数assert_param();下面以assert_param(IS_GPIO_ALL_PERIPH(GPIOx));为例说一下我的理解,函数的参数IS_GPIO_ALL_PERIPH(GPIOx),我们可以寻找到原型
#define IS_GPIO_ALL_PERIPH(PERIPH) (((*(uint32_t*)&(PERIPH)) == GPIOA_BASE)|| \
((*(uint32_t*)&(PERIPH)) == GPIOB_BASE) || \
((*(uint32_t*)&(PERIPH)) == GPIOC_BASE) || \
((*(uint32_t*)&(PERIPH)) == GPIOD_BASE) || \
((*(uint32_t*)&(PERIPH)) == GPIOE_BASE) || \
((*(uint32_t*)&(PERIPH)) == GPIOF_BASE) || \
((*(uint32_t*)&(PERIPH)) == GPIOG_BASE))
这个宏定义的作用就是检查参数PERIPH,判断参数PERIPH是否为GPIOX(A...G)基址中的一个,只要有一个为真则其值为真,否则为假,不用多说,这是C语言中基本的逻辑运算。当然这个库函数也用的很有意思,看:首先对PERIPH进行取址,也就是求地址,&PERIPH,然后对这个地址强制转化为32位的指针,即前面加(uint32_t *),然后通过*进行访问这个地址(指针)中的内容。不多说了,看几遍就能明白。
下面我们再回到assert_param这个函数,这个函数是哪里的呢?在stm32f10x_conf.h寻找到原型如下:
#ifdef USE_FULL_ASSERT

#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t*)__FILE__, __LINE__))
void assert_failed(uint8_t* file, uint32_t line);
#else
#define assert_param(expr) ((void)0)
#endif 
这是一个预编译文件,若是定义了USE_FULL_ASSERT这个文件,则执行后面的文件,我们在程序中一般都没什么定义,即执行后面这个语句((void)0),这个语句不用多想,没有定义USE_FULL_ASSERT就是什么也不执行。说的明白点,对上面的那个语句IS_GPIO_ALL_PERIPH(GPIOx)不执行任何操作。
若是定义了USE_FULL_ASSERT它,我们调用这个函数assert_param时,及对参数IS_GPIO_ALL_PERIPH(GPIOx)的正确性进行检查,通过一个C语言中的双目运算符来判断,若是返回1,执行语句(void)0,跟上面一样,若是返回0,则执行后面的函数assert_failed((uint8_t *)__FILE__,__LINE__),函数的作用在库函数中有解释,用来指示出错的行数和文件。注意:__FILE__,__LINE__是标准库函数中的宏定义!切记
void assert_failed(uint8_t* file, uint32_t line);刚开始没看明白为什么加在这里,仔细一想是在头文件的函数声明。至于函数实体呢?我们从官方文件的模板中main.c中可以找到。如下:
void assert_failed(u8* file, u32 line) 
{ /* User can add his own implementation to report the file name and linenumber, 
ex: printf("Wrong parameters value: file %s on line %d\r\n", file,line) */ 
/* Infinite loop */ 
while (1) { } 
} 英文注释也说明了怎么应用,通过输入参数来确定位置,最简单的方法就是串口打印了,这个函数的主要思想是在输入参数有问题的时候,但是有编译不出来,它可以帮你检查参数的有效性,好处不必多言,自己领悟就行。
继续说明如下: assert_param是怎样包含进去的呢?我们在stm32f10x_conf.h这个头文件中定义的函数声明还是宏定义,怎么在其它文件中都能应用呢?也很多网上朋友在刚开始学习的时候都遇到编译不过去的问题出现,最后通过在文件中添加USE_STDPERIPH_DRIVER来解决的:

我们可以在整个工程中进行搜索USE_STDPERIPH_DRIVER,通过头文件可以看出,是使用标准外设文件。在stm32f10x.h文件中我们可以搜索到如下情况:
#if !defined USE_STDPERIPH_DRIVER
/**
* @brief Comment the line below if you will not use the peripherals drivers.
In this case, these drivers will not be included and the application code will 
be based on direct access to peripherals registers 
*/
#define USE_STDPERIPH_DRIVER
#endif

#ifdef USE_STDPERIPH_DRIVER
#include "stm32f10x_conf.h"
#endif
可以很容易看出来,我们不在那里添加,这个头文件中也给我们设置了开关,只要把第一个的注释去掉,就不用在配置中添加USE_STDPERIPH_DRIVER了,在第二个文件中我们可以知道怎样包含这个控制开关文件了,呵呵。我们也明白为什么我们在写程序的时候只要包含stm32f10x.h就能很容易的包含所有的文件文件了吧,我们只要在stm32f10x_conf.h配置一下就能包含所需要的库文件了。
通过以上可以看出,通过头文件的相互包含,来控制外设以及调试文件的调用,这样我们理清思路,理解起来就好多了。当然在学习中可能有些C语言问题还没有理解透彻,多上网搜一下,或者多看书,很快就搞明白的。



PS 2:

       

在STM32的固件库和提供的例程中,到处都可以见到assert_param()的使用。如果打开任何一个例程中的stm32f10x_conf.h文件,就可以看到实际上assert_param是一个宏定义;
在固件库中,它的作用就是检测传递给函数的参数是否是有效的参数。
所谓有效的参数是指满足规定范围的参数,比如某个参数的取值范围只能是小于3的正整数,如果给出的参数大于3,
则这个assert_param()可以在运行的程序调用到这个函数时报告错误,使程序员可以及时发现错误,而不必等到程序运行结果的错误而大费周折。

这是一种常见的软件技术,可以在调试阶段帮助程序员快速地排除那些明显的错误。

它确实在程序的运行上牺牲了效率(但只是在调试阶段),但在项目的开发上却帮助你提高了效率。

当你的项目开发成功,使用release模式编译之后,或在stm32f10x_conf.h文件中注释掉对USE_FULL_ASSERT的宏定义,所有的assert_param()检验都消失了,不会影响最终程序的运行效率。

#define assert_param(expr) ((expr) ? (void)0 : assert_failed((u8 *)__FILE__, __LINE__))
。。。

assert_param(IS_ADC_ALL_PERIPH(ADCx));
。。。

在执行assert_param()的检验时,如果发现参数出错,它会调用函数assert_failed()向程序员报告错误,在任何一个例程中的main.c中都有这个函数的模板,如下:

void assert_failed(uint8_t* file, uint32_t line)
{


while (1)
{}
}

你可以按照自己使用的环境需求,添加适当的语句输出错误的信息提示,或修改这个函数做出适当的错误处理。

1、STM32F10xD.LIB是DEBUG模式的库库文件。
2、STM32F10xR.LIB是Release模式的库库文件。
3、要选择DEBUG和RELEASE模式,需要修改stm32f10x_conf.h的内容。
    #define DEBUG 表示DEBUG模式,把该语句注释掉,则为RELEASE模式。
4、要选择DEBUG和RELEASE模式,也可以在Options,C/C++,Define里填入DEBUG的预定义。
    这样,就不需要修改stm32f10x_conf.h的内容。
5、如果把库加入项目,则不需要将ST的库源文件加入项目,比较方便。
    但是,库的选择要和DEBUG预定义对应。

<think> 我们正在讨论的是STM32固件库中的assert_param宏。根据引用内容,它的主要作用是在函数调用时检查传入的参数是否有效。如果启用了USE_FULL_ASSERT,并且参数无效(即expr为假),则调用assert_failed函数报告错误(包括文件名和行号);如果没有启用,则assert_param(expr)被定义为((void)0),即不产生任何效果。 具体来说,assert_param的使用是为了在开发调试阶段帮助程序员及时发现参数错误,避免因参数错误导致程序运行异常。在正式发布时,可以通过不定义USE_FULL_ASSERT来关闭这个检查,以减少代码大小和运行开销。 从引用中我们可以看到,assert_param宏的定义如下: 当定义了USE_FULL_ASSERT时: #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) 否则: #define assert_param(expr) ((void)0) 在固件库的函数中,通常会先使用assert_param来检查参数,然后再执行函数体。例如在ADC_Cmd函数中,使用assert_param检查ADCx是否为有效的外设以及NewState是否为有效的功能状态(DISABLE或ENABLE)。 因此,assert_param的作用总结如下: 1. 参数有效性检查:确保传递给函数的参数在允许的取值范围内。 2. 调试辅助:当参数无效时,会触发错误报告(调用assert_failed函数),通常这个函数会提供错误位置(文件名和行号)的信息,方便调试。 3. 可配置:通过是否定义USE_FULL_ASSERT宏,可以开启或关闭参数检查功能。在调试阶段开启,在发布阶段关闭,以平衡调试需求和运行效率。 注意:assert_failed函数需要用户自己实现,通常在一个名为main.h或类似的文件中声明,然后在main.c中实现。例如: void assert_failed(uint8_t* file, uint32_t line) { printf("Assert failed in file %s at line %d\n", file, line); while(1); } 下面我们按照要求整理回答: </think> ### `assert_param`的作用详解 `assert_param`是STM32固件库中用于参数校验的宏定义,其主要作用如下: #### 1. **参数有效性验证** - **运行时检查**:在函数执行时实时检测传入参数的有效性 - **范围验证**:确保参数在预设的有效范围内(例如:检查外设地址是否正确) - **错误拦截**:当检测到无效参数时立即触发错误处理机制 ```c void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState) { /* 参数检查 */ assert_param(IS_ADC_ALL_PERIPH(ADCx)); // 验证ADC外设地址有效性[^3] assert_param(IS_FUNCTIONAL_STATE(NewState)); // 验证状态值有效性 ... } ``` #### 2. **调试辅助机制** - **错误定位**:通过`__FILE__`和`__LINE__`宏精确报告错误位置[^2] - **可配置性**:通过`USE_FULL_ASSERT`宏控制启用/禁用 ```c #ifdef USE_FULL_ASSERT #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t*)__FILE__, __LINE__)) #else #define assert_param(expr) ((void)0) // 正式发布时禁用 #endif ``` #### 3. **错误处理机制** - **失败回调**:触发无效参数时调用`assert_failed()`函数 - **用户自定义**:开发者需自行实现错误处理逻辑(如日志记录或系统复位) ```c void assert_failed(uint8_t* file, uint32_t line) { printf("Assert失败: %s文件第%lu行", file, line); while(1); // 死循环防止继续执行 } ``` #### 4. **开发阶段优势** - **早期发现问题**:在编码阶段捕获参数错误(如外设标识符错误)[^4] - **减少调试成本**:避免因参数错误导致的异常行为调试难题 - **代码健壮性**:强制规范API使用方式(如时钟配置函数中的正确参数)[^5] #### 实际工作流程 ```mermaid graph TD A[函数调用] --> B{参数是否有效?} B -->|有效| C[正常执行] B -->|无效| D[触发assert_failed] D --> E[报告文件名/行号] E --> F[执行用户定义错误处理] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值