|
本帖最后由 yceast 于 2015-11-8 21:23 编辑 我们在学STM32的时候函数assert_param出现的几率非常大,上网搜索一下,网上一般解释断言机制,做为程序开发调试阶段时使用。下面我就谈一下我对这些应用的看法,学习东西抱着知其然也要知其所以然。$ A; H. w' v# h 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)|| ' l; |% O; k0 H/ s8 q ((*(uint32_t*)&(PERIPH)) == GPIOB_BASE) || ; d6 G( e% z( i3 d ((*(uint32_t*)&(PERIPH)) == GPIOC_BASE) || |, l& T$ ]0 ^* f ((*(uint32_t*)&(PERIPH)) == GPIOD_BASE) || 4 A! g4 E7 F* O# X4 b9 g. G1 r ((*(uint32_t*)&(PERIPH)) == GPIOE_BASE) || ((*(uint32_t*)&(PERIPH)) == GPIOF_BASE) || 7 [% c. W$ y5 k5 ^9 u6 m ((*(uint32_t*)&(PERIPH)) == GPIOG_BASE))( } t9 M1 l( A" Z 这个宏定义的作用就是检查参数PERIPH,判断参数PERIPH是否为GPIOX(A...G)基址中的一个,只要有一个为真则其值为真,否则为假,不用多说,这是C语言中基本的逻辑运算。当然这个库函数也用的很有意思,看:首先对PERIPH进行取址,也就是求地址,&PERIPH,然后对这个地址强制转化为32位的指针,即前面加(uint32_t *),然后通过*进行访问这个地址(指针)中的内容。不多说了,看几遍就能明白。1 o$ [6 M6 @" p( y! F" Q4 K* l: r( | 下面我们再回到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); #else3 R1 i9 J, k# I #define assert_param(expr) ((void)0)* ?# W6 K+ y% b' u0 ^8 @2 a3 Q #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__是标准库函数中的宏定义!切记6 p/ {! P" u, l3 {8 E void assert_failed(uint8_t* file, uint32_t line);刚开始没看明白为什么加在这里,仔细一想是在头文件的函数声明。至于函数实体呢?我们从官方文件的模板中main.c中可以找到。如下: void assert_failed(u8* file, u32 line) + D% s3 {: a0 _ @8 R% l { /* User can add his own implementation to report the file name and linenumber, ex: printf("Wrong parameters value: file %s on line %drn", file,line) */ & J3 ^; ^2 p5 P/ d6 } /* Infinite loop */ while (1) { } 1 v0 L( C( Q" l% Y; v6 U. d } 英文注释也说明了怎么应用,通过输入参数来确定位置,最简单的方法就是串口打印了,这个函数的主要思想是在输入参数有问题的时候,但是有编译不出来,它可以帮你检查参数的有效性,好处不必多言,自己领悟就行。 继续说明如下: assert_param是怎样包含进去的呢?我们在stm32f10x_conf.h这个头文件中定义的函数声明还是宏定义,怎么在其它文件中都能应用呢?也很多网上朋友在刚开始学习的时候都遇到编译不过去的问题出现,最后通过在文件中添加USE_STDPERIPH_DRIVER来解决的: 我们可以在整个工程中进行搜索USE_STDPERIPH_DRIVER,通过头文件可以看出,是使用标准外设文件。在stm32f10x.h文件中我们可以搜索到如下情况:# ?# ?. X9 O4 H; K1 |2 Q( r: V #if !defined USE_STDPERIPH_DRIVER0 [: l& V$ I, F t* B /**: m" u' A( ^7 A+ x; S, C4 O * @brief Comment the line below if you will not use the peripherals drivers.' t/ @/ c" g" n( z3 a1 P. k In this case, these drivers will not be included and the application code will z% r8 O- h+ @+ y be based on direct access to peripherals registers W3 y& m: y* P8 q( A% q9 }- [4 n: I */. Y; u+ x1 ?$ o ? #define USE_STDPERIPH_DRIVER# S( _/ I' R8 i/ e: K7 r. M #endif #ifdef USE_STDPERIPH_DRIVER #include "stm32f10x_conf.h" #endif 可以很容易看出来,我们不在那里添加,这个头文件中也给我们设置了开关,只要把第一个的注释去掉,就不用在配置中添加USE_STDPERIPH_DRIVER了,在第二个文件中我们可以知道怎样包含这个控制开关文件了,呵呵。我们也明白为什么我们在写程序的时候只要包含stm32f10x.h就能很容易的包含所有的文件文件了吧,我们只要在stm32f10x_conf.h配置一下就能包含所需要的库文件了。" w' u$ O, f, L! b9 g9 a% ~6 L( a 通过以上可以看出,通过头文件的相互包含,来控制外设以及调试文件的调用,这样我们理清思路,理解起来就好多了。当然在学习中可能有些C语言问题还没有理解透彻,多上网搜一下,或者多看书,很快就搞明白的。 - N( G$ x% a, s PS 2: STM32中assert_param的使用 在STM32的固件库和提供的例程中,到处都可以见到assert_param()的使用。如果打开任何一个例程中的stm32f10x_conf.h文件,就可以看到实际上assert_param是一个宏定义;3 @/ E+ f% t: k/ t( @ 在固件库中,它的作用就是检测传递给函数的参数是否是有效的参数。 所谓有效的参数是指满足规定范围的参数,比如某个参数的取值范围只能是小于3的正整数,如果给出的参数大于3, Z/ ^* v3 v: _/ y* X1 O+ R6 `& v H 则这个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));# y! J" `0 d( F 。。。 在执行assert_param()的检验时,如果发现参数出错,它会调用函数assert_failed()向程序员报告错误,在任何一个例程中的main.c中都有这个函数的模板,如下: void assert_failed(uint8_t* file, uint32_t line) { while (1)# k! j/ Z8 x; o {} } 你可以按照自己使用的环境需求,添加适当的语句输出错误的信息提示,或修改这个函数做出适当的错误处理。 1、STM32F10xD.LIB是DEBUG模式的库库文件。 2、STM32F10xR.LIB是Release模式的库库文件。8 O! a7 Z$ r. H( I, I- P. U 3、要选择DEBUG和RELEASE模式,需要修改stm32f10x_conf.h的内容。 #define DEBUG 表示DEBUG模式,把该语句注释掉,则为RELEASE模式。: y( h- B3 e8 l% Q0 {) q1 ] 4、要选择DEBUG和RELEASE模式,也可以在Options,C/C++,Define里填入DEBUG的预定义。/ q7 g2 S; q$ _% y) \ 这样,就不需要修改stm32f10x_conf.h的内容。! r5 C6 C% o) G: M5 ^ 5、如果把库加入项目,则不需要将ST的库源文件加入项目,比较方便。 但是,库的选择要和DEBUG预定义对应。 |
微信公众号
手机版
这个看STM板卡商了,有没有零售版的,如果只有活动才有,那就是抢的。如果是人家自己花钱买的,人家爱怎么折腾是人家的事。。。。
不过既然楼主发了这个帖子了,也同时反映了一个问题,就是论坛中的板子靠抢,确实存在不合理的一面。如果板子靠积分换购,并且积分不会太低,那么换板子时可能大家会仔细考虑是否需要这块板子。
所以不能乱说的