零死角玩转stm32-初级篇之初识STM32库

作者:
上传时间为: 2013-07-03 09:50 PM

    4、初识STM32库

   本章通过简单介绍STM32库的各个文件及其关系,让读者建立STM32库的概念,看完后对库有个总体印象即可,在后期实际开发时接触了具体的库时,再回头看看这一章,相信你对STM32库又会有一个更深刻的认识。

4.1 STM32神器之库开发

   4.1.1什么是STM32库?

在51单片机的程序开发中,我们直接配置51单片机的寄存器,控制芯片的工作方式,如中断,定时器等。配置的时候,我们常常要查阅寄存器表,看用到哪些配置位,为了配置某功能,该置1还是置0。这些都是很琐碎的、机械的工作,因为51单片机的软件相对来说较简单,而且资源很有限,所以可以直接配置寄存器的方式来开发。

STM32库是由ST公司针对STM32提供的函数接口,即API (Application Program Interface)开发者可调用这些函数接口来配置STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。

当我们调用库的API的时候可以不用挖空心思去了解库底层的寄存器操作,就像当年我们学习C语言的时候,用prinft()函数时只是学习它的使用格式,并没有去研究它的源码实现,如非必要,可以说是老死不相往来。

实际上,库是架设在寄存器与用户驱动层之间的代码,向下处理与寄存器直接相关的配置,向上为用户提供配置寄存器的接口。库开发方式与直接配置寄存器方式的区别见错误!未找到引用源。4-1。

图4-1

4.1.2为什么采用库来开发?

对于STM32,因为外设资源丰富,带来的必然是寄存器的数量和复杂度的增加,这时直接配置寄存器方式的缺陷就突显出来了:

1.开发速度慢

2.程序可读性差

这两个缺陷直接影响了开发效率,程序维护成本,交流成本。库开发方式则正好弥补了这两个缺陷。

而坚持采用直接配置寄存器的方式开发的程序员,会列举以下原因:

1.更直观

2.程序运行占用资源少

初学STM32的读者,普遍因为第一个原因而选择以直接配置寄存器的方法来学习。认为这种方法直观,能够了解到是配置了哪些寄存器,怎样配置寄存器。事实上,库函数的底层实现恰恰是直接配置寄存器方式的最佳例子,想深入了解芯片是如何工作的话,只要追踪到库的最底层实现就能理解,相信你会为它严谨、优美的实现方式而陶醉。要想修炼C语言,就从ST的库开始吧。这将在 错误!未找到引用源。进行详细分析。

相对于库开发的方式,直接配置寄存器方式生成的代码量的确会少一点,但因为STM32有充足的资源,权衡库的优势与不足,绝大部分时候,我们愿意牺牲一点资源,选择库开发。一般只有在对代码运行时间要求极苛刻的地方,才用直接配置寄存器的方式代替,如频繁调用的中断服务函数。

对于库开发与直接配置寄存器的方式,在STM32刚推出时引起程序员的激烈争论,但是,随着ST库的完善与大家对库的了解,更多的程序员选择了库开发。

本书采用STM32的库进行讲解,既介绍如何使用库接口,也讲解库接口的实现方式。使读者既能利用库进行快速开发,也能深入了解STM32的工作原理。

为进一步解答读者为什么使用库开发,请读者先思考一下为会么采用c语言开发软件而不是采用汇编。相比之下,可以发现调用库接口开发与直接配置寄存器开发的关系,犹如c语言与汇编的关系。见表 41

和表 42。

据某无从考证的IT大师说过,“一切计算机科学的问题都可以用分层来解决。”从汇编到c,从直接配置寄存器到使用库,从裸机到系统,从操作系统到应用层软件,无不体现着这样的分层思想。开发的软件多了,跨越的软件层次多了,会深刻地认同他这句话,分层思想在软件开发上体现得淋漓尽致,分层使得问题变得更简单,使得能够屏蔽下层实现方式的差异,使得软件开发变成简单的调用函数接口,而不用管它的实现,大大提高效率。

库就是建立了一个新的软件抽象层,库的优点,其实就是分层的优点,库的缺点,也是软件分层带来的缺点,而对于STM32这样高性能的芯片,我想我们会愿意承受分层带来的缺点的。

表 41

表 42

4.2 STM32结构及库层次关系

4.2.1 CMSIS标准

我们知道由ST公司生产的STM32采用的是Cortex-M3内核,内核是整个微控制器的CPU。该内核是ARM公司设计的一个处理器体系架构。ARM公司并不生产芯片,而是出售其芯片技术授权。ST公司或其它芯片生产厂商如TI,负责设计的是在内核之外的部件,被称为核外外设片上外设设备外设。如芯片内部的模数转换外设ADC、串口UART、定时器TIM等。内核与外设,如同PC上的CPU与主板、内存、显卡、硬盘的关系。见错误!未找到引用源。

图 42

因为基于Cortex的某系列芯片采用的内核都是相同的,区别主要为核外的片上外设的差异,这些差异却导致软件在同内核,不同外设的芯片上移植困难。为了解决不同的芯片厂商生产的Cortex微控制器软件 的兼容性问题,ARM与芯片厂商建立了CMSIS标准(Cortex MicroController Software Interface Standard)。

所谓CMSIS标准,实际是新建了一个软件抽象层。见错误!未找到引用源。。

错误!未找到引用源。

CMSIS标准中最主要的为CMSIS核心层,它包括了:

内核函数层:其中包含用于访问内核寄存器的名称、地址定义,主要由ARM公司提供。

设备外设访问层:提供了片上的核外外设的地址和中断定义,主要由芯片生产商提供。

可见CMSIS层位于硬件层与操作系统或用户层之间,提供了与芯片生产商无关的硬件抽象层,可以为接口外设、实时操作系统提供简单的处理器软件接口,屏蔽了硬件差异,这对软件的移植是有极大的好处的。STM32的库,就是按照CMSIS标准建立的。

4.2.2 库目录、文件简介

STM32的3.5版库可以从官网获得,也可以直接从本书的附录光盘得到。本书主要采用最新版的3.5库文件,在高级篇的章节有部分代码是采用3.0的库开发的,因为3.5与3.0的库文件兼容性很好,对于旧版的代码我们仍然使用用3.0版的。

解压后进入库目录:stm32f10x_stdperiph_libSTM32F10x_StdPeriph_Lib_V3.5.0

各文件夹内容说明见图 41

图 41

Libraries文件夹下是驱动库的源代码及启动文件。

Project 文件夹下是用驱动库写的例子跟一个工程模板。

还有一个已经编译好的HTML文件,是库帮助文档,主要讲的是如何使用驱动库来编写自己的应用程序。说得形象一点,这个HTML就是告诉我们:ST公司已经为你写好了每个外设的驱动了,想知道如何运用这些例子就来向我求救吧。不幸的是,这个帮助文档是英文的,这对很多英文不好的朋友来说是一个很大的障碍。但野火要告诉大家,英文仅仅是一种工具,绝对不能让它成为我们学习的障碍。其实这些英文还是很简单的,我们需要的是拿下它的勇气。网上流传有一份中文版本的库帮助文档,但那个是2.x版本的,但3.x以上版本的目录结构和库函数接口跟2.x版本的区别还是比较大的,这点大家要注意下。

在使用库开发时,我们需要把libraries目录下的库函数文件添加到工程中,并查阅库帮助文档来了解ST提供的库函数,这个文档说明了每一个库函数的使用方法。

进入Libraries文件夹看到,关于内核与外设的库文件分别存放在CMSISSTM32F10x_StdPeriph_Driver文件夹中。

LibrariesCMSISCM3文件夹下又分为CoreSupportDeviceSupport文件夹。

4.2.2.1 core_cm3.c文件

CoreSupport中的是位于CMSIS标准的核内设备函数层 的M3核通用的源文件core_cm3.c和头文件core_cm3.h,它们的作用是为那些采用Cortex-M3核设计SOC的芯片商设计的芯片外设提供一个进入M3内核的接口。这两个文件在其它公司的M3系列芯片也是相同的。至于这些功能是怎样用源码实现的,我们可以不用管它,我们只需把这个文件加进我们的工程文件即可,有兴趣的朋友可以深究。

core_cm3.c文件还有一些与编译器相关条件编译语句,用于屏蔽不同编译器的差异,我们在开发时不用管这部分,有兴趣可以了解一下。里面包含了一些跟编译器相关的信息,如:RealView Compiler (RVMDK),ICC Compiler (IAR),GNU Compiler。

/* define compiler specific symbols */

#if defined ( __CC_ARM   )

#define __ASM            __asm

#define __INLINE         __inline

#elif defined ( __ICCARM__ )

#define __ASM           __asm

#define __INLINE        inline

#elif defined   (  __GNUC__  )

#define __ASM            __asm                                        #define __INLINE         inline

#elif defined   (  __TASKING__  )

#define __ASM            __asm

#define __INLINE         inline

#endif

较重要的是在core_cm3.c文件中包含了stdin.h 这个头文件,这是一个ANSI C 文件,是独立于处理器之外的,就像我们熟知的C语言头文件 stdio.h 文件一样。位于RVMDK这个软件的安装目录下,主要作用是提供一些新类型定义,如:

/* exact-width signed integer types */

typedef   signed          char int8_t;

typedef   signed short     int int16_t;

typedef   signed           int int32_t;

typedef   signed       __int64 int64_t;

/* exact-width unsigned integer types */

typedef unsigned          char uint8_t;

typedef unsigned short     int uint16_t;

typedef unsigned           int uint32_t;

typedef unsigned       __int64 uint64_t;

这些新类型定义屏蔽了在不同芯片平台时,出现的诸如int的大小是16位,还是32位的差异。所以在我们以后的程序中,都将使用新类型如int8_t 、int16_t……

在稍旧版的程序中还可能会出现如u8、u16、u32这样的类型,请尽量避免这样使用,在这里提出来是因为初学时如果碰到这样的旧类型让人一头雾水,而且在以新的库建立的工程中是无法追踪到u8、u16、u32这些的定义的。

core_cm3.c跟启动文件一样都是底层文件,都是由ARM公司提供的,遵守CMSIS标准,即所有CM3芯片的库都带有这个文件,这样软件在不同的CM3芯片的移植工作就得以简化。

4.2.2.2 system_stm32f10x.c文件

DeviceSupport文件夹下的是启动文件、外设寄存器定义&中断向量定义层 的一些文件,这是由ST公司提供的。见图 42

图 42

system_stm32f10x.c,是由ST公司提供的,遵守CMSIS标准。该文件的功能是设置系统时钟和总线时钟, M3比51单片机复杂得多,并不是说我们外部给一个8M的晶振,M3整个系统就以8M为时钟协调整个处理器的工作。我们还要通过M3核的核内寄存器来对8M的时钟进行倍频,分频,或者使用芯片内部的时钟。所有的外设都与时钟的频率有关,所以这个文件的时钟配置是很关键的。

system_stm32f10x.c在实现系统时钟的时候要用到PLL(锁相环),这就需要操作寄存器,寄存器都是以存储器映射的方式来访问的,所以该文件中包含了stm32f10x.h 这个头文件。

4.2.2.3 stm32f10x.h文件

stm32f10x.h 这个文件非常重要,是一个非常底层的文件。

所有处理器厂商都会将对内存的操作封装成一个宏,即我们通常说的寄存器,并且把这些实现封装成一个系统文件,包含在相应的开发环境中。这样,我们在开发自己的应用程序的时候只要将这个文件包含进来就可以了。

4.2.2.4 启动文件

LibrariesCMSISCoreCM3startuparm文件夹下是由汇编编写的系统启动文件,不同的文件对应不同的芯片型号,在使用时要注意。见图 43

图 43

文件名的英文缩写的意义如下:

cl:互联型产品,stm32f105/107系列

vl:超值型产品,stm32f100系列

xl:超高密度(容量)产品,stm32f101/103系列

ld:低密度产品,FLASH小于64K

md:中等密度产品,FLASH=64 or 128

hd:高密度产品,FLASH大于128

野火M3开发板中用的芯片是STM32F103VET6,64KRAM,512KROM,是属于高密度产品,所以启动文件要选择startup_stm32f10x_hd.s。

启动文件是任何处理器在上点复位之后最先运行的一段汇编程序。在我们编写的c语言代码运行之前,需要由汇编为c语言的运行建立一个合适的环境,接下来才能运行我们的程序。所以我们也要把启动文件添加进我们的的工程中去。

总的来说,启动文件的作用是:

1.初始化堆栈指针SP;

2.初始化程序计数器指针PC;

3.设置堆、栈的大小;

4.设置异常向量表的入口地址;

5.配置外部SRAM作为数据存储器(这个由用户配置,一般的开发板可没有外部SRAM);

6.设置C库的分支入口__main(最终用来调用main函数);

7.在3.5版的启动文件还调用了在system_stm32f10x.c文件中的SystemIni() 函数配置系统时钟,在旧版本的工程中要用户进入main函数自己调用SystemIni() 函数。

4.2.2.5 STM32F10x_StdPeriph_Driver文件夹

LibrariesSTM32F10x_StdPeriph_Driver文件夹下有inc(include的缩写)跟src(source的简写)这两个文件夹,这都属于CMSIS的设备外设函数 部分src里面是每个设备外设的驱动程序,这些外设是芯片制造商在Cortex-M3核外加进去的。

进入libraries目录下的STM32F10x_StdPeriph_Driver文件夹,见图 44。

图 44

src inc文件夹里的就是ST公司针对每个STM32外设而编写的库函数文件,每个外设对应一个 .c .h 后缀的文件。我们把这类外设文件统称为:stm32f10x_ppp.c stm32f10x_ppp.h文件,PPP表示外设名称。

如针对模数转换(ADC)外设,在src文件夹下有一个stm32f10x_adc.c源文件,在inc文件夹下有一个stm32f10x_adc.h头文件,若我们开发的工程中用到了STM32内部的ADC,则至少要把这两个文件包含到工程里。

见图 45。

图 45

这两个文件夹中,还有一个很特别的misc.c文件,这个文件提供了外设对内核中的NVIC(中断向量控制器)的访问函数,在配置中断时,我们必须把这个文件添加到工程中。

4.2.2.6 stm32f10x_it.c、 stm32f10x_conf.h文件

在库目录的ProjectSTM32F10x_StdPeriph_Template目录下,存放了官方的一个库工程模板,我们在用库建立一个完整的工程时,还需要添加这个目录下的stm32f10x_it.c、stm32f10x_it.h、stm32f10x_conf.h这三个文件。

stm32f10x_it.c,是专门用来编写中断服务函数的,在我们修改前,这个文件已经定义了一些系统异常 的接口,其它普通中断服务函数由我们自己添加。但是我们怎么知道这些中断服务函数的接口如何写呢?是不是可以自定义呢?答案当然不是的,这些都有可以在汇编启动文件中找到,具体的大家自个看库的启动文件的源码去吧。

stm32f10x_conf.h,这个文件被包含进stm32f10x.h 文件。是用来配置使用了什么外设的头文件,用这个头文件我们可以很方便地增加或删除上面driver目录下的外设驱动函数库。如下面的代码配置表示使用了gpio、rcc、spi、usart的外设库函数,其它的注释掉的部分,表示没有用到。

/* Includes ------------------------------------------------------------------*

* Uncomment/Comment the line below to enable/disable peripheral header file inclusion *

/#include "stm32f10x_adc.h"

//#include "stm32f10x_bkp.h"

//#include "stm32f10x_can.h"

//#include "stm32f10x_cec.h"

//#include "stm32f10x_crc.h"

//#include "stm32f10x_dac.h"

//#include "stm32f10x_dbgmcu.h"

//#include "stm32f10x_dma.h"

//#include "stm32f10x_exti.h"

//#include "stm32f10x_flash.h"

//#include "stm32f10x_fsmc.h"

#include "stm32f10x_gpio.h"

//#include "stm32f10x_i2c.h"

//#include "stm32f10x_iwdg.h"

//#include "stm32f10x_pwr.h"

#include "stm32f10x_rcc.h"

//#include "stm32f10x_rtc.h"

//#include "stm32f10x_sdio.h"

#include "stm32f10x_spi.h"

//#include "stm32f10x_tim.h"

#include "stm32f10x_usart.h"

//#include "stm32f10x_wwdg.h"

//#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */

stm32f10x_conf.h这个文件还可配置是否使用“断言”编译选项,在开发时使用断言可由编译器检查库函数传入的参数是否正确,软件编写成功后,去掉“断言”编译选项可使程序全速运行。可通过定义USE_FULL_ASSERT或取消定义来配置是否使用断言。

4.2.3 库各文件间的关系

前面向大家简单介绍了各个库文件的作用,库文件是直接包含进工程即可,丝毫不用修改,而有的文件就要我们在使用的时候根据具体的需要进行配置。接下来从整体上把握一下各个文件在库工程中的层次或关系,这些文件对应到CMSIS标准架构上。见图 06

图 06(STM32库文件结构)

图 06描述了STM32库各文件之间的调用关系,这个图省略了DSP核(Cortex-M3没有DSP核)和实时系统层部分的文件关系。在实际的使用库开发工程的过程中,我们把位于CMSIS层的文件包含进工程,丝毫不用修改,也不建议修改。

对于位于用户层的几个文件,就是我们在使用库的时候,针对不同的应用对库文件进行增删(用条件编译的方法增删)和改动的文件。

4.2.4 使用库帮助文档

野火坚信,授之以鱼不如授之以渔。官方资料是所有关于STM32知识的源头,所以在本小节介绍如何使用官方资料。官方的帮助手册,是最好的教程,几乎包含了所有在开发过程中遇到的问题。这些资料已整理到了附录光盘。

4.2.4.1常用官方资料

1.《stm32f10x_stdperiph_lib_um.chm》

这个就是前面提到的库的帮助文档,在使用库函数时,我们最好通过查阅此文件来了解库函数原型,或库函数的调用 的方法。也可以直接阅读源码里面的函数的函数说明。

2.《STM32参考手册.pdf 》

这个文件相当于STM32的datasheet,它把STM32的时钟、存储器架构、及各种外设、寄存器都描述得清清楚楚。当我们对STM32的库函数的实现方式 感到困惑时,可查阅这个文件,以直接配置寄存器方式开发的话查阅这个文档的频率会更高。但你会累死。

3.《Cortex-M3权威指南》 宋岩译。

该手册详细讲解了Cortex内核的架构和特性,要深入了解Cortex-M3内核,这是首选,经典中的经典呀。

当然还有其他很有用的官方文档,这里就不再赘述……

4.4.2.2 初识库函数

所谓库函数,就是STM32的库文件中为我们编写好的函数接口,我们只要调用这些库函数,就可以对STM32进行配置,达到控制目的。我们可以不知道库函数是如何实现的,但我们调用函数必须要知道函数的功能、可传入的参数及其意义、和函数的返回值

于是,有读者就问那么多函数我怎么记呀?野火的回答是:会查就行了,哪个人记得了那么多。所以我们学会查阅库帮助文档 是很有必要的。

打开库帮助文档stm32f10x_stdperiph_lib_um.chm见图 07

图 07

层层打开文档的目录标签ModulesSTM32F10x_StdPeriph_Driver,可看到

STM32F10x_StdPeriph_Driver标签下有很多外设驱动文件的名字MISC、ADC、BKP、CAN等标签。我们试着查看ADC的初始化库函数(ADC_Init) 看看,继续打开标签ADCADC_Exported_FunctionsFunctionsADC_Init 见图 08

图 08

利用这个文档,我们即使没有去看它的具体代码,也知道要怎么利用它了。

如它的功能是:以ADC_InitStruct参数配置ADC,进行初始化。原型为void ADC_Init(ADC_TypeDef * ADCx , ADC_Init_TypeDef * ADC_InitStruct)

其中输入的参数 ADCx 和 ADC_InitSturct 均为库文档中定义的 自定义数据类型,这两个传入参数均为结构体指针。初学时,我们并不知道如ADC_TypeDef这样的类型是什么意思,可以点击函数原型中带下划线的 ADC_TypeDef 就可以查看这是什么类型了。

就这样初步了解了一下库函数,读者就可以发现STM32的库是写得很优美的。每个函数和数据类型都符合见名知义 的原则,当然,这样的名称写起来特别长,而且对于我们来说要输入这么长的英文,很容易出错,所以在开发软件的时候,在用到库函数的地方,直接把库帮助文档 中函数名称复制粘贴到工程文件就可以了。

全部评论 ()

创建讨论帖子

登录 后参与评论
系统提示