你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】关于STM32中assert_param的分析

[复制链接]
STMCU小助手 发布时间:2022-6-30 18:00
    我们在学STM32的时候函数assert_param出现的几率非常大,上网搜索一下,网上一般解释为断言机制,做为程序开发调试阶段时使用。下面我就谈一下我对这些应用的看法,学习东西抱着知其然也要知其所以然。

! _9 g( J( u0 N
    我们在分析库函数的时候,几乎每一个函数的原型有这个函数assert_param();下面以assert_param(IS_GPIO_ALL_PERIPH(GPIOx));为例说一下我的理解,函数的参数IS_GPIO_ALL_PERIPH(GPIOx),我们可以寻找到原型
  1. #defineIS_GPIO_ALL_PERIPH(PERIPH) (((*(uint32_t*)&(PERIPH)) == GPIOA_BASE) ||
    4 @5 }9 ^  }  ?, h

  2. ) n+ B& l' ?$ x7 O* d" n
  3. ((*(uint32_t*)&(PERIPH)) ==GPIOB_BASE) ||
    * s6 ~2 P3 c! G4 E
  4.   T, F9 n0 l6 p% K- H/ A& i
  5. ((*(uint32_t*)&(PERIPH)) ==GPIOC_BASE) ||
    # M& r6 n) u7 C; ~1 y
  6. 4 r. N! `; B  {7 a
  7. ((*(uint32_t*)&(PERIPH)) ==GPIOD_BASE) ||1 w* Z, F. d$ b% j) a2 C- i
  8. . I# V2 {6 I; N! ]7 I
  9. ((*(uint32_t*)&(PERIPH)) ==GPIOE_BASE) ||
    ; v$ c. K2 s# S! {4 b" i2 ^

  10. 1 ]  r9 B5 P# K5 }
  11. ((*(uint32_t*)&(PERIPH)) ==GPIOF_BASE) ||* d" _3 a5 K" W  b/ K( }
  12. , p" [- A5 @6 _* H* ?  z
  13. ((*(uint32_t*)&(PERIPH)) ==GPIOG_BASE))
    1 C  Z# w2 s* C+ l
  14. + |% V# A1 n, r) H/ `5 f; w
复制代码

/ P; \, b" Z; z+ V5 w0 }- c

% F1 W( Y$ N1 T# r3 O
    这个宏定义的作用就是检查参数PERIPH,判断参数PERIPH是否为GPIOX(A...G)基址中的一个,只要有一个为真则其值为真,否则为假,不用多说,这是C语言中基本的逻辑运算。当然这个库函数也用的很有意思,看:首先对PERIPH进行取址,也就是求地址,&PERIPH,然后对这个地址强制转化为32位的指针,即前面加(uint32_t *),然后通过*进行访问这个地址(指针)中的内容。不多说了,看几遍就能明白。
    下面我们再回到assert_param这个函数,这个函数是哪里的呢?在stm32f10x_conf.h寻找到原型如下:
  1. #ifdef USE_FULL_ASSERT
    4 S) ?: B* }1 C5 x1 L

  2. 9 w1 ^1 c- D* u1 F/ f3 ~% K
  3. #define assert_param(expr) ((expr) ? (void)0 :assert_failed((uint8_t *)__FILE__, __LINE__)); N: x1 A+ j; |
  4. * S4 J5 U5 ~- z: N! M3 X; ]0 D" c* d
  5. void assert_failed(uint8_t* file,uint32_t line);3 O0 F' k# [$ p. V- K

  6. / b9 |8 s% o6 M# I# y& _
  7. #else
    . g8 d) Q( I7 D' G& T2 M
  8. - t" j  r& e0 d( g: X# I* a
  9. #define assert_param(expr)((void)0)& ~% f) L2 J( I+ h
  10. # i( L' U9 e( ~- r; L8 A
  11. #endif
复制代码

+ |7 n5 w/ q- p" _4 d! }1 F) w% F
    这是一个预编译文件,若是定义了USE_FULL_ASSERT这个文件,则执行后面的文件,我们在程序中一般都没什么定义,即执行后面这个语句((void)0),这个语句不用多想,没有定义USE_FULL_ASSERT就是什么也不执行。说的明白点,对上面的那个语句IS_GPIO_ALL_PERIPH(GPIOx)不执行任何操作。

! {; C" f' |1 D& d7 d! p% B8 T
    若是定义了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中可以找到。如下:
  1. void assert_failed(u8* file, u32line) - r; `% k9 d6 x" x* p$ O# A

  2. 1 a% ^2 Z' \5 ], E( N7 t
  3. {
    9 r& i! u( U0 l, n

  4. ; q6 Y* v# e3 |# K3 ?3 q2 {+ L6 I4 ~
  5.     /* User can add his ownimplementation to report the file name and line number,
    & V. q; t" f& {

  6. 2 q# V+ J/ i8 V2 N
  7.        ex: printf("Wrong parametersvalue: file %s on line %drn", file, line) */
    ! B3 d, ^7 M! B1 b

  8. ) Y% O8 t3 N: ?8 ~: g
  9.     /* Infinite loop */ # L0 ?, y  V, `9 u) a" h' Q

  10. 8 ]7 x2 |9 {, v4 s; Q  h
  11.     while (1) { } 1 \3 P0 f: V: L  {( a7 ^% Q7 N
  12. & V& k" x! |9 G1 l
  13. }
复制代码

* P4 p5 R/ O* W. p9 N& L
    英文注释也说明了怎么应用,通过输入参数来确定位置,最简单的方法就是串口打印了,这个函数的主要思想是在输入参数有问题的时候,但是有编译不出来,它可以帮你检查参数的有效性,好处不必多言,自己领悟就行。
4 Y) D5 [$ J8 r- ~" q6 t( T5 m
    继续说明如下:assert_param是怎样包含进去的呢?我们在stm32f10x_conf.h这个头文件中定义的函数声明还是宏定义,怎么在其它文件中都能应用呢?也很多网上朋友在刚开始学习的时候都遇到编译不过去的问题出现,最后通过在文件中添加USE_STDPERIPH_DRIVER来解决的:
1 t$ _6 }1 C3 i! Y3 u, o
微信图片_20220629180904.jpg
3 z7 ^; R: T, I. @( x0 N: ]
    我们可以在整个工程中进行搜索USE_STDPERIPH_DRIVER,通过头文件可以看出,是使用标准外设文件。在stm32f10x.h文件中我们可以搜索到如下情况:
  1. #if !defined USE_STDPERIPH_DRIVER
    ) Z4 Y- \1 ^. {- k, e8 {6 N
  2. 1 R1 t9 k$ P# \
  3. /**
    7 l$ o+ r9 O; H4 ~: C- O

  4. 7 L' u0 U9 o. s& B8 c. w
  5. * @brief Comment the line belowif you will not use the peripherals drivers.
    , h& p, ~* f! r. m. r

  6. 2 F8 \* \% I* U# R, L/ x
  7. In this case, these drivers willnot be included and the application code will + \9 ]0 N! U& f8 Q
  8. $ ]3 ?4 y1 D0 @
  9. be based on direct access toperipherals registers
    2 E1 z5 o+ ~! V: s+ G: ?" [
  10. 3 ?+ C8 R# y( T- u
  11. */, y  Q% [; _% o: }- C, {# a+ d

  12. " Y0 D1 T* A0 p& u
  13. #define USE_STDPERIPH_DRIVER
    $ ~1 F! X$ M: H

  14. ; R3 D4 ]  b6 `3 j, Q! F
  15. #endif# [, A* _* X! K( B4 R8 L% [, b" @
  16. ) z- L1 C1 Q( Z: F5 F
  17. 2 h( \, N9 M6 t+ S0 m7 M9 N
  18. # h' i! q3 g- L+ Z  f7 z% k4 f
  19. #ifdef USE_STDPERIPH_DRIVER
    9 Y) u$ a6 ~! ^. ^7 X) T$ l

  20. : {5 a/ T7 w& y1 D- L
  21. #include "stm32f10x_conf.h"
    " P5 e0 A5 E1 r  ~

  22. 1 [: q3 j) C# w. H+ n
  23. #endif
复制代码

/ }% g/ z& v+ r
: c3 K) R7 [! n4 o6 x5 b
    可以很容易看出来,我们不在那里添加,这个头文件中也给我们设置了开关,只要把第一个的注释去掉,就不用在配置中添加USE_STDPERIPH_DRIVER了,在第二个文件中我们可以知道怎样包含这个控制开关文件了,呵呵。我们也明白为什么我们在写程序的时候只要包含stm32f10x.h就能很容易的包含所有的文件文件了吧,我们只要在stm32f10x_conf.h配置一下就能包含所需要的库文件了。

$ `, r+ Q, p2 H
    通过以上可以看出,通过头文件的相互包含,来控制外设以及调试文件的调用,这样我们理清思路,理解起来就好多了。当然在学**可能有些C语言问题还没有理解透彻,多上网搜一下,或者多看书,很快就搞明白的。
4 b; o, p0 P+ L: v2 C" W
赞 收藏 评论0 发布时间:2022-6-30 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版