1.1 任务1 新建一个基于STM32固件库的工程模板

任务要求

建立一个基于V3.5版本固件库的Keil μVision4工程模板,方便以后每次新建工程时,可以直接复制使用该模板。

1.1.1 新建基于STM32固件库的Keil μVision4工程模板

本书使用的是Keil μVision4版本。Keil μVision4源自德国的Keil公司,集成了业内最领先的技术,包括μVision4集成开发环境与RealView编译器,支持Arm 7、Arm 9和最新的Cortex-M3核处理器,可以自动配置启动代码,集成了Flash烧写模块,具备强大的Simulation设备模拟、性能分析等功能。

1. 新建工程模板目录

下面介绍怎样建立基于V3.5版本固件库的工程模板目录,以后新建工程时,可以直接复制使用该模板。

(1)先在计算机的某个盘符下新建一个STM32_Project目录,作为基于STM32固件库的工程模板目录。

(2)在STM32_Project工程模板目录下,新建USER、CORE、OBJ以及STM32F10x_FWLib 4个子目录,如图1-1所示。

图1-1 工程STM32_Project模板目录

其中,CORE用来存放核心文件和启动文件;OBJ用来存放编译过程文件以及hex文件;STM32F10x_FWLib用来存放ST公司(意法半导体公司)官方提供的库函数源码文件;USER除了用来存放工程文件,还用来存放主函数文件main.c,以及system_stm32f10x.c、STM32F10x.s等文件。另外,很多人喜欢把子目录“USER”取名为“Project”,将工程文件都保存到“Project”子目录下面,也是可以的。

(3)把官方固件库“Libraries\STM32F10x_StdPeriph_Driver”下面的src和inc子目录复制到子目录STM32F10x_FWLib下面,如图1-2所示。

图1-2 STM32F10x_FWLib子目录

其中,src存放的是固件库的“.c”文件,inc存放的是其对应的“.h”文件。每个外设都对应一个“.c”文件和一个“.h”头文件。

(4)把官方固件库里相关的启动文件复制到CORE文件夹里面。

把官方固件库“Libraries\CMSIS\CM3\CoreSupport”下面的core_cm3.c和core_cm3.h文件复制到子目录CORE下面;把“Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm”下面的startup_stm32f10x_ld.s文件复制到CORE文件夹下面,如图1-3所示。

图1-3 CORE子目录

注意

本项目采用STM32F103R6芯片,该芯片的FLASH大小是32KB,属于小容量产品,所以启动文件使用startup_stm32f10x_ld.s文件。若采用其他容量的芯片,可以使用startup_stm32f10x_md.s或startup_stm32f10x_hd.s启动文件。

(5)先把官方固件库“Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x”下面的stm32f10x.h、system_stm32f10x.c、system_stm32f10x.h文件复制到子目录USER下面,然后把官方固件库“Project\STM32F10x_StdPeriph_Template”下面的stm32f10x_conf.h、stm32f10x_it.c、stm32f10x_it.h文件复制到子目录USER下面,如图1-4所示。

图1-4 USER子目录

通过前面几个步骤,就把需要的官方固件库相关文件复制到了工程目录模板“STM32_Project”下面。在以后的任务中,直接复制工程模板目录,然后修改成需要的名字即可使用。

2. 新建Keil μVision4工程模板

在建立工程之前,先在计算机的某个盘符下新建一个子目录“任务1 STM32_Project工程模板”;然后把工程目录模板“STM32_Project”复制到“任务1 STM32_Project工程模板”子目录里面,并修改工程目录模板名为“STM32_Project工程模板”。

(1)运行Keil μVision4软件。第一种方法是双击桌面上的Keil μVision4图标;第二种方法是单击桌面左下方的“开始”→“程序”→“Keil μVision4”,进入Keil μVision4集成开发环境,如图1-5所示。

图1-5 Keil μVision4集成开发环境

(2)单击Project->New uVision Project,如图1-6所示。

图1-6 新建工程

然后把目录定位到“STM32_Project工程模板\USER”下面,我们的工程文件都保存在这里。将工程命名为“STM32_Project”,单击“保存”按钮,如图1-7所示。

图1-7 保存新建工程

(3)接下来弹出选择芯片“Select Device Target‘Target1’”对话框,本项目使用的是STM32F103R6芯片,选择STMicroelectronics下面的STM32F103R6即可。如果使用的是其他系列的芯片,选择相应的型号就可以了,如图1-8所示。

图1-8 选择芯片型号

(4)单击OK按钮,弹出对话框“Copy STM32 Startup Code to project…”,询问是否添加启动代码到我们的工程中,这里选择“否”,因为我们使用的ST固件库文件已经包含了启动文件,如图1-9所示。

图1-9 是否添加启动代码对话框

启动代码是一段和硬件相关的汇编代码,是必不可少的!这段代码具体如何工作,我们不必太关心。

现在我们就可以看到新建工程后的界面,如图1-10所示。

图1-10 新建工程后的界面

3. 新建组和添加文件到STM32_Project工程模板

建好STM32_Project工程后,下面介绍如何在STM32_Project工程下新建USER、CORE、OBJ和STM32F10x_FWLib组,并添加文件到相应的组中。

(1)在图1-10中,通过快捷工具栏(或File菜单)的按钮新建一个文件,并保存为main.c,主文件main.c一定要放在USER组里面。在该文件中输入如下代码:

#include "stm32f10x.h"
int main(void) 
{ 
 while(1) 
 { 
  ; 
 } 
}

“#include"stm32f10x.h"”语句是一个“文件包含”处理语句。stm32f10x.h是STM32开发中最为重要的一个头文件,就像C51单片机的reg52.h头文件一样,在应用程序中是至关重要的,通常包括在主文件中。这里的main()函数是一个空函数,方便以后添加需要的代码。

注意

stm32f10x.h是ST公司V3.5及以后版本统一使用的库函数头文件,也就是把V2.0版本的stm32f10x_lib.h头文件换成了stm32f10x.h头文件,规范了代码,不需要再包含那么多的头文件了。使用高版本编译,将找不到stm32f10x_lib.h。

(2)在USER组里面,打开STM32_Project工程,然后在Project窗格的Target1上单击鼠标右键,选择Manage Components选项,如图1-11所示。

图1-11 调出Manage Components

(3)弹出图1-12所示Components Environment and Books对话框。

图1-12 Components Environment and Books对话框

(4)先把Project Targets栏下的Target1修改为STM32_Project,把Groups栏下的Source Group1删除。然后在Groups栏(中间栏)单击新建按钮(也可以双击下面的空白处),新建USER、CORE和STM32F10x_FWLib组,如图1-13所示。

图1-13 新建的组

(5)往USER、CORE和STM32F10x_FWLib组里面添加我们需要的文件。

先选中Groups栏下的STM32F10x_FWLib,然后单击Add Files按钮,定位到工程目录的STM32F10x_FWLib/src子目录。把里面的所有文件都选中(组合键Ctrl+A),然后单击Add按钮,最后单击Close按钮,就可以看到Files栏下面出现了我们添加的所有文件,如图1-14所示。

图1-14 STM32F10x_FWLib组里添加的文件

用同样的方法,添加CORE子目录里面的core_cm3.c和startup_stm32f10x_ld.s文件,添加USER子目录里面的main.c、stm32f10x_it.c和system_stm32f10x.c文件。

这样,需要添加的文件都添加到工程里面了,最后单击OK按钮,退出Components Environment and Books对话框。这时,会发现在Target树下多了三个组名和其中添加的文件,如图1-15所示。

图1-15 完成新建组和添加文件的工程

注意

• 为方便后面使用,我们把所有外设的库文件都添加进工程了,不用每次增加外设时都要再添加相应的库文件,这样做的坏处就是当工程太大时,编译速度全变慢;

• 本任务只用了GPIO,所以可以只添加stm32f10x_gpio.c,其他的不用添加。

4. 工程配置与编译

到此为止,新建的基于STM32的Keil μVision4工程就已经基本完成了。接下来进行工程配置和编译。

(1)单击工具栏的“Target Options…”按钮,弹出“Options for Target 'Leddl'”对话框,选择C/C++选项卡,添加要编译文件的路径。这个步骤非常重要,务必添加正确的路径,否则编译会出现错误。C/C++选项卡配置界面如图1-16所示。

图1-16 C/C++选项卡配置界面

(2)单击Include Paths最右边的方块按钮,弹出添加路径的Folder Setup对话框,然后把STM32F10x_FWLib\inc、CORE和USER子目录都添加进去。此操作是为了设定编译器的头文件包含路径,在以后的任务中会经常用到,如图1-17所示。

图1-17 添加所要编译文件的路径

在这里,还需要在C/C++选项卡配置界面中,填写“STM32F10X_LD,USE_STDPERIPH_DRIVER”到Define输入框里,如图1-18所示。

图1-18 配置一个全局的宏定义

之所以要填写“STM32F10X_LD,USE_STDPERIPH_DRIVER”,是因为V3.5版的库函数在配置和选择外设时,是通过宏定义来选择的,所以需要配置一个全局的宏定义变量,否则工程编译会出错。若使用中容量芯片,就把STM32F10X_LD修改为STM32F10X_MD;若使用大容量芯片,就修改为STM32F10X_HD。

(3)设置完C/C++选项卡配置界面后,单击OK按钮,在“Options for Target 'Leddl'”对话框中,选择Output选项卡。先选中Greate HEX File选项;再单击“Select Folder for Objects…”按钮,在弹出的对话框中选中OBJ子目录,单击OK按钮。以后工程编译的HEX文件以及垃圾文件就会放到OBJ子目录里面,保持了工程简洁不乱,如图1-19所示。

图1-19 配置Output选项卡

(4)单击OK按钮,退出“Options for Target'Target 1'”对话框,然后单击工具栏的Rebuild按钮,对工程进行编译。若编译发生错误,要进行分析检查,直到编译正确为止,如图1-20所示。

图1-20 工程编译

对工程进行第一次编译时,单击工具栏的Rebuild按钮。不管工程的文件有没有编译过,Rebuild都会对工程中的所有文件重新进行编译并生成可执行文件,因此重编译时间较长。若只编译工程中上次修改的文件,单击工具栏的Build按钮即可。

另外,在主文件main.c代码的最后一定要加上一个回车,否则编译会有警告信息。

注意

• 基于STM32的Keil μVision4工程已经完成了,可将其作为开发的工程模板。以后开发项目时直接复制使用,再把编写的主文件和其他文件添加进来就可以了,这为以后的开发工作带来了极大方便。

1.1.2 认识STM32固件库

ST公司为了方便用户开发程序,提供了一套丰富的STM32固件库。什么是固件库呢?在STM32应用程序开发中,固件库与寄存器有什么区别和联系呢?

1. STM32固件库开发与寄存器开发的关系

从51单片机开发转入STM32开发时,由于我们习惯了51单片机的寄存器开发方式,突然要使用STM32固件库开发,会不知道如何下手。下面通过一个简单的例子来说明STM32固件库到底是什么,与寄存器开发有什么关系?

在51单片机开发中,我们若想控制某些I/O端口的状态,就会直接操作寄存器,如:

P2=0x0fe;

而在STM32的开发中,我们同样也可以直接操作寄存器,如:

GPIOx->BRR = 0x00fe;

由于STM32有数百个寄存器,初学者要想很快地掌握每个寄存器的用法,并能正确使用,是非常困难的。

于是ST公司推出了官方的STM32固件库,将这些寄存器的底层操作封装起来,提供一整套接口(API)供开发者调用。在大多数场合下,开发人员不需要知道操作的是哪个寄存器,只需要知道调用哪些函数即可。

上面的例子直接操作STM32的BRR寄存器,可以实现电平控制。STM32固件库封装了一个函数:

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{ 
   GPIOx->BRR = GPIO_Pin; 
}

这时,开发人员就不需要操作BRR寄存器了,知道如何使用GPIO_ResetBits()函数就可以了。另外,通过固件库的函数名字,我们就能知道这个函数的功能是什么,该怎么使用。

2. STM32固件库与CMSIS标准

STM32固件库是函数的集合,固件库函数的作用是向下与寄存器直接打交道,向上提供用户调用函数的接口(API)。那么对这些函数有什么要求呢?这就涉及一个CMSIS标准的基础知识。

Arm是一个做芯片标准的公司,它负责的是芯片内核的架构设计,而TI、ST等公司并不是做标准的,只是一个芯片公司,它们根据Arm公司提供的芯片内核标准来设计自己的芯片。芯片虽然由芯片公司设计,但是内核却要服从Arm公司提出的Cortex-M3内核标准。芯片公司每卖出一片芯片,需要向Arm公司交纳一定的专利费。

所以,所有Cortex-M3芯片的内核结构都是一样的,只是在存储器容量、片上外设、端口数量、串口数量以及其他模块上有所区别,芯片公司可以根据自己的需求理念来设计这些资源。同一家公司设计的多种Cortex-m3内核芯片的片上外设也会有很大的区别,如STM32F103RBT和STM32F103ZET在片上外设方面就有很大的区别。

为了保证不同芯片公司生产的Cortex-M3芯片能在软件上基本兼容,Arm公司和芯片生产商共同提出了一套标准——CMSIS标准( Cortex Microcontroller Software Interface Standard),即“Arm Corte微控制器软件接口标准”。ST官方库(STM32固件库)就是根据这套标准设计的,CMSIS共分3个基本功能层。

(1)核内外设访问层:Arm公司提供的访问,定义处理器内部寄存器地址以及功能函数。

(2)中间件访问层:定义访问中间件的通用API,由Arm公司提供。

(3)外设访问层:定义硬件寄存器的地址以及外设的访问函数。

CMSIS向下负责与内核和各个外设直接打交道,向上负责提供实时操作系统用户程序调用的函数接口。如果没有CMSIS标准,那么各个芯片公司就会设计自己喜欢的风格的库函数,而CMSIS标准就是要强制规定,芯片生产公司的库函数必须按照CMSIS这套规范来设计。

另外,CMSIS还对各个外设驱动文件的文件名字规范化、函数名字规范化等做了一系列规定。比如前面用到的GPIO_ResetBits函数,其名字是不能随便定义的,必须遵循CMSIS规范。

又如,在我们使用STM32芯片时,首先要进行系统初始化。CMSIS就规定系统初始化函数名必须为SystemInit,所以各个芯片公司设计自己的库函数时,都必须用SystemInit对系统进行初始化。

1.1.3 STM32固件库关键子目录和文件

STM32固件库是不断完善升级的,目前存在多个不同的版本,本书使用的是V3.5版本的固件库(目前最新版本)。STM32固件库的目录结构如图1-21所示。

图1-21 STM32固件库目录结构

从图1-21可以看出,解压后的STM32F10x_StdPeriph_Lib_V3.5.0目录下包含了STM32固件库的全部文件。

其中,Release_Notes.html文件是关于该库文件相比之前版本有何改动,stm32f10x_stdperiph_lib_um.chm文件是固件库的英文帮助文档,在开发过程中,这个文档会经常用到。

1. STM32固件库关键子目录

STM32固件库关键子目录主要有Libraries和Project。另外,_htmresc子目录里是ST的图标,Utilities子目录是官方评估板的一些对应源码,跟开发完全无关。

(1)Libraries子目录

Libraries子目录下面有CMSIS子目录和STM32F10x_StdPeriph_Driver子目录,这两个子目录包含固件库核心的所有子文件夹和文件,主要包含大量的头文件、源文件和系统文件,是开发必须使用的。Libraries的目录结构如图1-22所示。

图1-22 Libraries目录结构

CMSIS子目录存放的是启动文件。STM32F10x_StdPeriph_Driver子目录存放的是STM32固件库源码文件,其下的inc子目录存放的是stm32f10x_xxx.h头文件,无须改动;src子目录存放的是stm32f10x_xxx.c固件库源码文件。

每一个“.c”文件和一个相应的“.h”文件对应,这里的文件也是固件库的核心文件,即每个外设对应一组文件。

(2)Project子目录

Project子目录下面有STM32F10x_StdPeriph_Examples和STM32F10x_StdPeriph_Template子目录。

STM32F10x_StdPeriph_Examples子目录存放的是ST官方提供的固件实例源码,包含了几乎所有STM32F10x外设的详细使用源代码。在以后的开发过程中,可以修改这个官方提供的参考实例,以快速驱动自己的外设。很多开发板的实例,也都参考了官方提供的例程源码,这些源码对以后的学习非常重要。

STM32F10x_StdPeriph_Template子目录存放的是工程模板。

2. STM32固件库的关键文件

在这里,我们着重介绍STM32固件库Libraries子目录下的几个重要文件。

(1)core_cm3.c和core_cm3.h

core_cm3.c和core_cm3.h文件位于\Libraries\CMSIS\CM3\CoreSupport子目录下面,分别是内核访问层的源文件和头文件,提供进入M3内核的接口。这两个文件是由Arm公司提供的CMSIS核心文件,对所有M3内核的芯片都一样,永远都不需要修改。

(2)STM32F10x子目录的3个文件

DeviceSupport和CoreSupport是同一级的,STM32F10x子目录放在DeviceSupport子目录下面。DeviceSupport\ST\STM32F10x子目录主要存放一些启动文件、比较基础的寄存器定义以及中断向量定义的文件。

在STM32F10x子目录下面有3个文件:system_stm32f10x.c、system_stm32f10x.h以及stm32f10x.h文件,是外设访问层的源文件和头文件。

system_stm32f10x.c文件和对应的system_stm32f10x.h头文件的功能,是设置系统和总线时钟,其中有一个非常重要的SystemInit()函数,在系统启动时都会被调用,用来设置系统的整个时钟系统。这也就是不需要用户配置时钟,程序就能运行的原因。

stm32f10x.h文件相当重要,主要包含了STM32F10x系列所有外设寄存器的定义、位定义、中断向量表、存储空间的地址映射等。只要做STM32开发,就要查看这个文件相关的定义。打开这个文件时可以看到,里面有非常多的结构体以及宏定义。

(3)启动文件

在STM32F10x子目录下面还有一个startup子目录,里面放的是启动文件。在\startup\arm目录下,我们可以看到8个以startup开头的“.s”文件,如图1-23所示。

图1-23 startup子目录结构

从图1-23中可以看出,共有8个启动文件,不同容量(容量是指FLASH的大小)的芯片,对应的启动文件也不一样。在stm32f 103系列芯片中,主要使用其中3个启动文件。

• startup_stm32f10x_ld.s:适用于小容量产品,Flash≤32 KB;

• startup_stm32f10x_md.s:适用于中等容量产品,64 KB≤Flash≤128 KB;

• startup_stm32f10x_hd.s:适用于大容量产品,256 KB≤Flash。

本项目采用的是STM32F103R6芯片,其Flash容量是32 KB,属于小容量产品,所以选择startup_stm32f10x_ld.s启动文件。

那么,启动文件到底有什么作用呢?

启动文件主要进行堆栈等的初始化、中断向量表以及中断函数定义,还要引导程序进入main函数。

(4)STM32F10x_StdPeriph_Template子目录的3个文件

在Project\STM32F10x_StdPeriph_Template子目录下有3个关键文件:stm32f10x_it.c、stm32f10x_it.h和stm32f10x_conf.h。

stm32f10x_it.c和stm32f10x_it.h是外设中断函数文件,用来编写中断服务函数,用户可以相应地加入自己的中断程序代码。

stm32f10x_conf.h是固件库配置文件,有很多#include。在建立工程时,可以注释掉一些不用的外设头文件,只选择固件库使用的外设。