st官方固件库是在寄存器操作之上的,但是使用寄存器操作的话,需要注意的地方很多,需要对照参考手册一个一个赋值,稍有不慎便会出错,所以固件库将外设的初始化封装成初始化结构体,将外设的操作封装在函数中,将寄存器赋值的操作都封装起来,我们只需要调用API就可以,这样一来既提高了开发效率,也减少了代码量,如果还不能在MDK里熟练使用固件库编程,建议先补基础~接下来,我们在上一节寄存器工程的基础上,添加固件库,使用固件库进行开发。
1.创建固件库工程
将上一节的寄存器工程复制过来,改名为03-template-lib
,然后再其中创建如下目录结构,便于工程管理:
startup
:存放启动文件cmsis
:stm32固件库中的cmsis支持库libraries
:stm32固件库中的外设驱动库user
:存放用户自己的文件doc
:存放说明文档
然后就开始从固件库中复制文件了:
startup
:直接将工程中的已有启动文件startup_stm32f10x_hd
移动进去即可;
cmsis
:将工程中已有的内核支持文件core_cm3.h
(不要从固件库中添加core_cm3.c
进去,添加后编译会出错), 时钟配置文件system_stm32f10x.h
和system_stm32f10x.c
(从固件库中复制),以及stm32头文件stm32f10x.h
;
libraries
:这个直接将固件库中STM32F10X_StdPeriph_Driver
中的内容拷贝过来即可:
user
:因为使用了固件库,所以将固件库配置头文件stm32f10x_conf.h
,中断服务程序文件stm32f10x_it.h
和stm32f10x_it.c
都复制进去,这些文件从固件库的project/STM32F10X_StdPeriph_Template
中复制,最后将我们工程中已有的main文件复制进去:
到这里,我们的工程模板就建好了,然后就是编写makefile来编译整个工程了:
2.编译固件库工程
固件库编译的时候有几点需要注意两点:
- 文件和makefile不是同一个目录时,如何告诉makefile找到头文件呢?
- 固件库中如此多的c文件,如何添加依赖关系呢?
首先来解决第一个问题——如何告诉编译器找到这些头文件?
其实这个问题在讲使用gcc的时候有讲:当头文件不和源文件在一个目录时,需要使用-I
(大写i)参数给编译器添加头文件搜索目录,所以修改一下makefile:
在这里插入图片描述
然后来解决第二个问题 —— 如何添加大量的源文件及其依赖到makefile?
其实文件虽然多,但是仔细理一下会发现可以分为以下几步:
- 找到全部的c文件;
- 将每个c文件编译为目标文件,不进行链接;
- 将所有的目标文件一起链接,生成可执行文件;
下面一步一步来~
首先如何找到所有的c文件呢?可以在c文件全部使用相对于makefile的相对路径,显然这是最笨的办法,因为这么多文件,不可能一步一步添加,但是,在linux下可以使用find
命令来寻找目录下的文件,所以我们使用find ./ -name '*.c'
来寻找当前目录下所有的c文件,如图:
修改makefile,添加所有的c源文件:
接下来解决第二个小问题,如何将这些源文件分别编译为对应的目标文件呢? 当然是不可能一个一个写的,这里使用makfile的静态模式,它的语法是这样的:
代码语言:javascript复制<targets>:<target-pattern>:<prereq-patterns>
<commands>
这里面targets定义了一系列目标文件,可以有通配符,是目标的一个集合;target-pattern指明了targets的模式,prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义: 首先我们需要一个目标集合,也就是使用刚刚的静态模式将上一步所有搜索到的.c文件换为同名.o文件:
最后将目标文件添加到链接中:
这个时候,我们基本的两个大问题就解决完了,接下来编译器参数,一个是添加宏定义,首先是芯片型号宏定义STM32F10X_HD
,然后是USE_STDPERIPH_DRIVER
,表明我们使用了标准外设驱动库;另一个是添加我们之前的头文件路径:
还有一点需要注意,启动文件也需要修改:
最后修改clean:
这里我们的makefile就完成了,如果可以的话,还可以使用#
添加必要的注释进去,最后进行一下优化,让它默认生成bin文件和hex文件:
接下来还不能直接make
,之前我们配置时钟是在main.c
里配置,这里引入了固件库中的system_stm32f10x.c
,时钟配置在这里面已经完成,默认配置为72M,所以可以将main.c中的时钟初始化去了,然后使用make指令即可编译。