简介

stm32f10x_conf.h文件有2个作用:①提供对assert_param运行时参数检查宏函数的定义。②将开发者用到的标准外设头文件集中在这个文件里面,而stm32f10x_conf.h又被包含到stm32f10x.h中去了,因此方便开发者在写自己的库时,只需一股脑的包含stm32f10x.h就行了。

我本人是强烈不推荐第②功能。一个合格的C开发者应该知道它在写一个模块时,需要包含什么头文件,不需要包含什么头文件。而第②功能的做法就是,不管你用不用,我都全部包含进去。包含不会用到的头文件一般不是什么错误,但是它会影响代码的编译速度,代码的整洁和可读性。而他的第①功能又可有可无,因此我很早就打算将这个文件从工程中删除了。

本文主要介绍,在使用ST提供的标准外设驱动库V3.5.0开发stm32项目时,如何从工程中删除这个头文件,而又不影响正常开发。

关于assert_param

ST提供的标准外设库V3.5.0在实现时,为了防止用户传递的参数不合法,大量使用了运行时断言。这个断言函数名为assert_param。
例如库中的GPIO_ReadInputData函数:

uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
{
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); //如果参数GPIOx不是一个合法的GPIO,则运行时会调用assert_failed来处理错误 return ((uint16_t)GPIOx->IDR);
}

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 */

解释:当我们在stm32f10x_conf.h文件定义了 USE_FULL_ASSERT(代表开发者需要启用标准库的运行时参数检查机制),那么assert_param作用为:即当参数为false时,调用assert_failed函数,否则当参数为true时,等于(void)0,也就是什么都不做。而assert_failed是一个留给开发者自己去实现的函数,他通常实现在main.c中,主体通常是一个死循环,其参数是错误发生的文件名和代码行号。也就是说当你在调用一个标准库的函数时,如果参数传递不合法,那么就会陷入死循环,因为程序没有继续允许下去的意义了。如果合法,则什么也不会发生。

当我们在stm32f10x_conf.h文件中不定义 USE_FULL_ASSERT(代表开发者不要启用标准库的运行时参数检查机制),assert_param总是等价于 :((void)0),即总是不起任何作用。这样就无法检测调用标准库时参数是否传递错误。

//------------文件:main.c------------------
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
while ()
{
}
}
#endif

所以,USE_FULL_ASSERT是一个功能裁剪宏,定义后,标准库会启用参数检查机制;取消它的定义, 标准库会禁用参数检查机制。

第一个问题

为什么我们要使用V3.5.0标准库,就必须将stm32f10x_conf.h包含到工程中去(具体说是将stm32f10x_conf.h包含到stm32f10x.h中去)?

答:因为标准库V3.5.0为了实现运行时参数检查,使用了assert_param宏函数,而这个宏函数定义在stm32f10x_conf.h文件中。如果不包含这个文件到项目中去,编译器在编译标准库时,就会找不到这个函数的定义而报错。

第二个问题

我们如何将stm32f10x_conf.h包含到工程中去?

答:

在stm32f10x.h中,有如下代码片段:

//-----------文件: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

也就是说我们需要在stm32f10x.h中定义 USE_STDPERIPH_DRIVER 这个宏,才能将stm32f10x_conf.h包含到stm32f10x.h中去。而定义USE_STDPERIPH_DRIVER这个宏有2种方法:①修改stm32f10x.h,开启对USE_STDPERIPH_DRIVER的宏定义。②在编译器的宏定义选项中添加USE_STDPERIPH_DRIVER的定义。
然而为了防止开发者误修改stm32f10x.h文件,ST将这个文件的属性设置为只读的,因此第一种方法是不建议的。

第三个问题

为什么我们使用标准外设库V3.5.0,就必须提供对USE_STDPERIPH_DRIVER宏的定义?

答:结合上面的问题一和问题二就明白了。

最后一个问题

我们到底要不要启用标准库的运行时参数检查机制?

答:如果启用,则标准库的代码会在运行时执行参数检查,影响库函数的执行效率,生成的hex文件的也会膨胀。所以建议在开发时启用标准库的运行时参数检查机制,项目稳定后,发布时可以关闭。

移除stm32f10x_conf.h文件

最开始我已经表达了对stm32f10x_conf.h的态度,我不打算使用这个头文件,甚至以后得工程都不再使用它。但我又需要使用标准库,那就必须在stm32f10x.h中直接提供对assert_param的定义,而不是放在stm32f10x_conf.h中。

第一步:【右键】stm32f10x.h,将其只读属性取消,这样就可以修改它了。

第二步:

删除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

按如下操作继续修改stm32f10x.h

/*************************************************************************************
如果开发者需要启用标准库的运行时参数检查机制,则在编译器的宏定义选项中定义STDPERIPH_DRIVER_USE_ASSERT
如果开发者不想启用标准库的运行时参数检查机制,则不定义STDPERIPH_DRIVER_USE_ASSERT 提示:启用标准库的运行时参数检查机制,导致运行时执行参数检查代码,影响库函数的执行效率和生成的hex文件的体积。
建议在开发时启用标准库的运行时参数检查机制,发布时可以不启用。
*/
#ifdef STDPERIPH_DRIVER_USE_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 //====================用上面的代码替换stm32f10x.h中的下面的代码===================== #ifdef USE_STDPERIPH_DRIVER
#include "stm32f10x_conf.h"
#endif

第三步:

把stm32f10x.h文件的属性改回为只读,防止开发时错误的修改。

最后对main.c的修改:
如果开启了标准库的运行时参数检查机制,则还要在main.c中提供对assert_failed函数的实现,用于在参数检查错误时处理这个错误。
因此替换原先的assert_failed的定义如下:

#ifdef STDPERIPH_DRIVER_USE_ASSERT
//当标准外设库函数进行参数合法性检查发现不通过时,调用的回调处理函数
//file为错误发生的文件名,line为错误行号
void assert_failed(uint8_t* file, uint32_t line)
{
while ()
{
}
}
#endif

如何使用

通过上面的修改,我们已经不再需要stm32f10x_conf.h头文件(和里面的USE_FULL_ASSERT宏),也不再需要USE_STDPERIPH_DRIVER宏。取而代之的是一个新的宏:STDPERIPH_DRIVER_USE_ASSERT。

如果我们需要启用运行时参数检查,则在编译器的宏定义选项中提供STDPERIPH_DRIVER_USE_ASSERT的定义,否则不提供STDPERIPH_DRIVER_USE_ASSERT的定义。

建议通过上面的步骤,构建一个自己的开发模板,以便后续继续使用。

这样做的好处

1、工程少了一个头文件,管理起来更加方便

2、在工程中使用标准外设库时,不再被强迫定义USE_STDPERIPH_DRIVER宏了

关于stm32f10x_conf.h文件的更多相关文章

  1. stm32f10x_it.c、stm32f10x_it.h和stm32f10x_conf.h文件作用

    如上图,在STM32的Keil工程文件(Project)中一般都包含stm32f10x_it.c.stm32f10x_it.h和stm32f10x_conf.h这三个文件,但是在ST官方提供的标准库“ ...

  2. stm32f10x.h文件分析理解

    今天再看过半年前自己写的这篇发现自己当时理解有误,stm32f10x.h与库开发并未存在太大关系,只是一个最为重要的寄存器地址到寄存器结构体变量的映射. stm32f10x.h 这个头文件是STM32 ...

  3. .lib文件 .h文件 .dll文件

    .lib代表的是静态数据连接库,在windows系统中起到链接程序和函数的作用,存放的是函数的是函数调用的信息,是obj文件的集合.相当于linux中的.a或.0. .so文件.lib文件是不对外公开 ...

  4. 对于.h文件和.c文件

    C语言中.h文件和.c文件详细解析_云止水_新浪博客http://blog.sina.com.cn/s/blog_73006d600102wcx5.html

  5. 有关stdint.h 文件

    有关stdint.h 文件 Google C++编程规范的P25页有如下叙述: <stdint.h> 定义了 int16_t . uint32_t . int64_t 等整型,在需要确定大 ...

  6. 下位机多个".c, .h"文件的相互包含及排版

    一.背景: 自从接触单片机编程以来,由于工作上的需要,不可避免的时常会接手别人的代码,但常常由于上一位同事的编码随意性有点大,导致可读性非常的差,有时候不得不完全舍弃原有代码,推倒重来,无形中增加了工 ...

  7. C语言中 *.c和*.h文件的区别!

    C语言中 *.c和*.h文件的区别!  http://blog.163.com/jiaoruijun07@126/blog/static/68943278201042064246409/        ...

  8. delegate 集成在类中,还是单独写在.h文件中?

    转:http://stackoverflow.com/questions/11382057/declaring-a-delegate-protocol There definitely are sub ...

  9. 编译过程中,termcap.h 文件找不到路径 licli.a终于生成

    编译过程中,termcap.h      文件找不到路径   查看是linux  源码下找不到termcap.h文件   安装了所有关于*cap*的源码包也不起作用     今天终于解决了这个问题,搜 ...

随机推荐

  1. Ribbon架构剖析

    在学习Ribbon之前,先看一下这张图,这张图完美的把Ribbon的基础架构给描述出来了 这张图的核心是负载均衡管理器,围绕着它的是外面的这5大功能点,咱们就从核心开始看然后再带出来其他的功能 首先看 ...

  2. E203译码模块(3)

    下面的代码译码出指令的立即数,不同的指令有不同的立即数编码形式. //I类型指令的imm,[31:20],符号位扩展成32位. wire [31:0] rv32_i_imm = { {20{rv32_ ...

  3. vuejs的导航栏固定

    https://blog.csdn.net/wang1006008051/article/details/78003974 博主文章,超级详细,上面传送们 不过博主的导航栏跳动比较明显,我自己做了修复 ...

  4. Confluence 邮箱设置

    Confluence有两种方法设置邮箱 原理: confluence服务器配置好邮箱信息,用户触发邮件发送规则时,confluence服务使用已配置的邮箱信息登录到邮箱服务器,进行发件服务. 那么我们 ...

  5. Dockerfile解析(八)

    一.Dockerfile是什么 Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本. 1. 构建的步骤 运行容器:docker run 构建新的镜像:docker ...

  6. 洛谷 U87052 一线天

    洛谷 U87052 一线天 题目传送门 题目背景 \(JDFZ\)即将举办第一届"一线天"趣味运动会...... 题目描述 "一线天"运动会在\(JLU\)南岭 ...

  7. (转)Tomcat与Jetty区别

    一.简介 Tomcat: Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目,由Apache.Sun和其他一些公司及个人共 ...

  8. [POJ1952]BUY LOW, BUY LOWER

    题目描述 Description The advice to "buy low" is half the formula to success in the bovine stoc ...

  9. ASP.NET开发实战——(五)ASP.NET MVC & 分层

    上一篇文章简要说明了MVC所代表的含义并提供了详细的项目及其控制器.视图等内容的创建步骤,最终完成了一个简单ASP.NET MVC程序. 注:MVC与ASP.NET MVC不相等,MVC是一种开发模式 ...

  10. 【2019.7.25 NOIP模拟赛 T1】变换(change)(思维+大分类讨论)

    几个性质 我们通过推式子可以发现: \[B⇒AC⇒AAB⇒AAAC⇒C\] \[C⇒AB⇒AAC⇒AAAB⇒B\] 也就是说: 性质一: \(B,C\)可以相互转换. 则我们再次推式子可以发现: \[ ...