1.1 基于标准外设库的软件开发1.1.1 STM32标准外设库概述 STM32标准外设库之前的版本也称固件函数库或简称固件库,是一个固件函数包,它由程序、数据结构和宏组成,包括了微控制器所有外设的性能特征。该函数库还包括每一个外设的驱动描述和应用实例,为开发者访问底层硬件提供了一个中间API,通过使用固件函数库,无需深入掌握底层硬件细节,开发者就可以轻松应用每一个外设。因此,使用固态函数库可以大大减少用户的程序编写时间,进而降低开发成本。每个外设驱动都由一组函数组成,这组函数覆盖了该外设所有功能。每个器件的开发都由一个通用API (application programming interface 应用编程界面)驱动,API对该驱动程序的结构,函数和参数名称都进行了标准化。 ST公司2007年10月发布了V1.0版本的固件库,MDK ARM3.22之前的版本均支持该库。2008年6月发布了V2.0版的固件库,从2008年9月推出的MDK ARM3.23版本至今均使用V2.0版本的固件库。V3.0以后的版本相对之前的版本改动较大,本书使用目前较新的V3.4版本。 1.1.2 使用标准外设库开发的优势简单的说,使用标准外设库进行开发最大的优势就在于可以使开发者不用深入了解底层硬件细节就可以灵活规范的使用每一个外设。标准外设库覆盖了从GPIO到定时器,再到CAN、I2C、SPI、UART和ADC等等的所有标准外设。对应的C源代码只是用了最基本的C编程的知识,所有代码经过严格测试,易于理解和使用,并且配有完整的文档,非常方便进行二次开发和应用。 1.1.3 STM32F10XXX标准外设库结构与文件描述
1 ?* {! @" \2 H, U1. 标准外设库的文件结构在上一小节中已经介绍了使用标准外设库的开发的优势,因此对标准外设库的熟悉程度直接影响到程序的编写,下面让我们来认识一下STM32F10XXX的标准外设库。STM32F10XXX的标准外设库经历众多的更新目前已经更新到最新的3.5版本,开发环境中自带的标准外设库为2.0.3版本,本书中以比较稳定而且较新的V3.4版本为基础介绍标准外设库的结构。 可以从ST的官方网站下载到各种版本的标准外设库,首先看一下3.4版本标准外设库的文件结构,如图 5‑3所示。3.0以上版本的文件结构大致相同,每个版本可能略有调整。
& M* r1 J6 r% f* ~+ Z" Q8 \
6 [' D* h* V' Z6 K u5 E3 `! _
图 5‑3 STM32F10XXX V3.4标准外设库文件结构 表 5‑4中介绍了每个文件夹所包含的主要内容。 表 5‑4 STM32F10XXX V3.4标准外设库文件夹描述 STM32F10x_StdPeriph_Lib_V3.4.0 | _htmresc | 本文件夹包含了所有的html页面资源 | Libraries | CMSIS | 见表 5‑6 | STM32F10x_StdPeriph_Driver | inc | 标准外设库驱动头文件 | src | 标准外设库驱动源文件 | Project | Examples | 标准外设库驱动的完整例程 | Template | MDK-ARM | KEIL RVMDK的项目模板示例 | RIDE | Raisonance RIDE的项目模板示例 | EWARM | IAR EWARM的项目模板示例 | Utilities | STM3210-EVAL | 本文件夹包含了用于STM3210B-EVAL和STM3210E-EVAL评估板的专用驱动 |
, i1 W% m( i! [, i6 I' i标准外设库的第一部分是CMSIS 和STM32F10x_StdPeriph_Driver,CMSIS 是独立于供应商的Cortex-M 处理器系列硬件抽象层,为芯片厂商和中间件供应商提供了简单的处理器软件接口,简化了软件复用工作,降低了Cortex-M 上操作系统的移植难度,并减少了新入门的微控制器开发者的学习曲线和新产品的上市时间。STM32F10x_StdPeriph_Driver则包括了分别对应包括了所有外设对应驱动函数,这些驱动函数均使用C语言编写,并提供了统一的易于调用的函数接口,供开发者使用。Project文件夹中则包括了ST官方的所有例程和基于不同编译器的项目模板,这些例程是学习和使用STM32的重要参考。Utilities包含了相关评估板的示例程序和驱动函数,供使用官方评估板的开发者使用,很多驱动函数同样可以作为学习的重要参考。 STM32F10xxx标准外设库体系结构如图 5‑4所示。图中很好的展示了各层以及具体文件之间的联系,各文件的具体功能说明如表 5‑5所示。 1 g3 h0 h; e7 T6 Y! w' K
- C0 d c# ]7 q# D: _+ q
图 5‑4 STM32F10xxx标准外设库体系结构 表 5‑5 文件功能说明 文件名 | 功能描述 | 具体功能说明 | core_cm3.h core_cm3.c | Cortex-M3内核及其设备文件 | 访问Cortex-M3内核及其设备:NVIC,SysTick等 访问Cortex-M3的CPU寄存器和内核外设的函数 | stm32f10x.h | 微控制器专用头文件 | 这个文件包含了STM32F10x全系列所有外设寄存器的定义(寄存器的基地址和布局)、位定义、中断向量表、存储空间的地址映射等 | system_stm32f10x.h system_stm32f10x.c | 微控制器专用系统文件 | 函数SystemInit,用来初始化微控制器 函数Sysem_ExtMemCtl,用来配置外部存储器控制器。它位于文件startup_stm32f10x_xx.s /.c,在跳转到main前调用 SystemFrequncy,该值代表系统时钟频率 | startup_stm32f10x_Xd.s | 编译器启动代码 | 微控制器专用的中断处理程序列表(与头文件一致) 弱定义(Weak)的中断处理程序默认函数(可以被用户代码覆盖) 该文件是与编译器相关的 | stm32f10x_conf.h | 固件库配置文件 | 通过更改包含的外设头文件来选择固件库所使用的外设,在新建程序和进行功能变更之前应当首先修改对应的配置。 | stm32f10x_it.h stm32f10x_it.c | 外设中断函数文件 | 用户可以相应的加入自己的中断程序的代码,对于指向同一个中断向量的多个不同中断请求,用户可以通过判断外设的中断标志位来确定准确的中断源,执行相应的中断服务函数。 | stm32f10x_ppp.h stm32f10x_ppp.c | 外设驱动函数文件 | 包括了相关外设的初始化配置和部分功能应用函数,这部分是进行编程功能实现的重要组成部分。 | Application.c | 用户文件 | 用户程序文件,通过标准外设库提供的接口进行相应的外设配置和功能设计。 | * b5 ]3 @9 j$ A7 l/ F; {- i
2. 基于CMSIS标准的软件架构根据调查研究,软件开发已经被嵌入式行业公认为最主要的开发成本。对于ARM公司来说,一个ARM内核往往会授权给多个厂家,生产种类繁多的产品,如果没有一个通用的软件接口标准,那么当开发者在使用不同厂家的芯片时将极大的增加了软件开发成本,因此,ARM与Atmel、IAR、Keil、hami-nary Micro、Micrium、NXP、SEGGER和ST等诸多芯片和软件厂商合作,将所有Cortex芯片厂商产品的软件接口标准化,制定了CMSIS标准。此举意在降低软件开发成本,尤其针对新设备项目开发,或者将已有软件移植到其他芯片厂商提供的基于Cortex处理器的微控制器的情况。有了该标准,芯片厂商就能够将他们的资源专注于产品外设特性的差异化,并且消除对微控制器进行编程时需要维持的不同的、互相不兼容的标准的需求,从而达到降低开发成本的目的。 如图 5‑5所示,基于CMSIS标准的软件架构主要分为以下4层:用户应用层、操作系统及中间件接口层、CMSIS层、硬件寄存器层。其中CMSIS层起着承上启下的作用:一方面该层对硬件寄存器层进行统一实现,屏蔽了不同厂商对Cortex-M系列微处理器核内外设寄存器的不同定义;另一方面又向上层的操作系统及中间件接口层和应用层提供接口,简化了应用程序开发难度,使开发人员能够在完全透明的情况下进行应用程序开发。也正是如此,CMSIS层的实现相对复杂。 , e/ a' ]; ]8 R' s0 o
( Q% @/ B2 `$ ^
图 5‑5 CMSIS标准的软件架构 层主要分为以下3 个部分: (1) 核内外设访问层(CPAL,Core Peripheral Access Layer):该层由ARM 负责实现。包括对寄存器名称、地址的定义,对核寄存器、NVIC、调试子系统的访问接口定义以及对特殊用途寄存器的访问接口(例如:CONTROL,xPSR)定义。由于对特殊寄存器的访问以内联方式定义,所以针对不同的编译器ARM 统一用来屏蔽差异。该层定义的接口函数均是可重入的。 (2) 片上外设访问层(DPAL, Device Peripheral Access Layer):该层由芯片厂商负责实现。该层的实现与CPAL 类似,负责对硬件寄存器地址以及外设访问接口进行定义。该层可调用CPAL 层提供的接口函数同时根据设备特性对异常向量表进行扩展,以处理相应外设的中断请求。 (3) 外设访问函数(AFP, Access Functions for Peripherals):该层也由芯片厂商负责实现,主要是提供访问片上外设的访问函数,这一部分是可选的。 对一个Cortex-M 微控制系统而言,CMSIS 通过以上三个部分实现了: l 定义了访问外设寄存器和异常向量的通用方法; l 定义了核内外设的寄存器名称和核异常向量的名称; l 为RTOS 核定义了与设备独立的接口,包括Debug 通道。 这样芯片厂商就能专注于对其产品的外设特性进行差异化,并且消除他们对微控制器进 行编程时需要维持的不同的、互相不兼容的标准需求,以达到低成本开发的目的。CMSIS中的具体文件结构如表 5‑6所示。 表 5‑6 CMSIS文件夹结构 CMSIS | Core | Documentation | CMSIS文档 | CM3 | Startup | arm | MDK ARM编译器启动文件 | startup_stm32f10x_hd.s: 大容量产品启动文件 startup_stm32f10x_md.s: 中容量产品启动文件 startup_stm32f10x_ld.s: 小容量产品启动文件 | gcc_ride7 | GCC编译器启动文件 | iar | IAR编译器启动文件 | TrueSTUDIO | TrueSTUDIO编译器启动文件 | 本文件夹包含STMF10xxx CMSIS文件:微控制器外设访问层和内核设备访问层: core_cm3.h:CMSIS的Cortex-M3内核设备访问层头文件 core_cm3.c:CMSIS的Cortex-M3内核设备访问层源文件 stm32f10x.h:CMSIS的Cortex-M3 STM32f10xxx微控制器外设访问层头文件 system_stm32f10x.h:CMSIS的Cortex-M3 STM32f10xxx微控制器外设访问层头文件 system_stm32f10x.c:CMSIS的Cortex-M3 STM32f10xxx微控制器外设访问层源文件 |
- K6 j4 b# @! c% a0 ^$ Z在实际开发过程中,根据应用程序的需要,可以采取2种方法使用标准外设库(StdPeriph_Lib): (1) 使用外设驱动:这时应用程序开发基于外设驱动的API(应用编程接口)。用户只需要配置文件”stm32f10x_conf.h”,并使用相应的文件”stm32f10x_ppp.h/.c”即可。 (2) 不使用外设驱动:这时应用程序开发基于外设的寄存器结构和位定义文件。 这两种方法的优缺点在“使用标准外设库开发的优势”小节中已经有了具体的介绍,这里仍要说明的是,使用使用标准外设库进行开发可以极大的减小软件开发的工作量,也是目前嵌入式系统开发的一个趋势。 标准外设库(StdPeriph_Lib)支持STM32F10xxx系列全部成员:大容量,中容量和小容量产品。从表 5‑6中也可以看出,启动文件已经对不同的系列进行了划分,实际开发中根据使用的STM32产品具体型号,用户可以通过文件”stm32f10x.h”中的预处理define或者通过开发环境中的全局设置来配置标准外设库(StdPeriph_Lib),一个define对应一个产品系列。 下面列出支持的产品系列 STM32F10x_LD:STM32小容量产品 STM32F10x_MD:STM32中容量产品 STM32F10x_HD:STM32大容量产品 在库文件中这些define的具体作用范围是: l 文件“stm3210f.h”中的中断IRQ定义 l 启动文件中的向量表,小容量,中容量,大容量产品各有一个启动文件 l 外设存储器映像和寄存器物理地址 l 产品设置:外部晶振(HSE)的值等 l 系统配置函数 因此通过宏定义这种方式,可以使标准外设库适用于不同系列的产品,同时也方便与不同产品之间的软件移植,极大的方便了软件的开发。 1.1.4 STM32F10XXX标准外设库的使用标准外设库中包含了众多的变量定义和功能函数,如果不能了解他们的命名规范和使用规律将会给编程带来很大的麻烦,本节将主要叙述标准外设库中的相关规范,通过这些规范的学习可以更加灵活的使用固件库,同时也将极大增强程序的规范性和易读性,同时标准外设库中的这种规范也值得我们在进行其他相关的开发时使用和借鉴。 1. 缩写定义标准外设库中的主要外设均采用了缩写的形式,通过这些缩写可以很容易的辨认对应的外设。 缩写 | 外设/单元 | ADC | 模数转换器 | BKP | 备份寄存器 | CAN | 控制器局域网模块 | CEC | | CRC | CRC计算单元 | DAC | 数模转换器 | DBGMCU | 调试支持 | DMA | 直接内存存取控制器 | EXTI | 外部中断事件控制器 | FLASH | 闪存存储器 | FSMC | 灵活的静态存储器控制器 | GPIO | 通用输入输出 | I2C | I2C接口 | IWDG | 独立看门狗 | PWR | 电源/功耗控制 | RCC | 复位与时钟控制器 | RTC | 实时时钟 | SDIO | SDIO接口 | SPI | 串行外设接口 | TIM | 定时器 | USART | 通用同步/异步收发器 | WWDG | 窗口看门狗 | ! x2 I$ q9 T% K+ T& }
2. 命名规则标准外设库遵从以下命名规则 PPP表示任一外设缩写,例如:ADC。源程序文件和头文件命名都以“stm32f10x_”作为开头,例如:stm32f10x_conf.h。常量仅被应用于一个文件的,定义于该文件中;被应用于多个文件的,在对应头文件中定义。所有常量都由英文字母大写书写。寄存器作为常量处理。他们的命名都由英文字母大写书写。在大多数情况下,他们采用与缩写规范一致。外设函数的命名以该外设的缩写加下划线为开头。每个单词的第一个字母都由英文字母大写书写,例如:SPI_SendData。在函数名中,只允许存在一个下划线,用以分隔外设缩写和函数名的其它部分。对于函数命名,总的来说有以下规则: l 名为PPP_Init的函数,其功能是根据PPP_InitTypeDef中指定的参数,初始化外设PPP,例如TIM_Init. l 名为PPP_DeInit的函数,其功能为复位外设PPP的所有寄存器至缺省值,例如TIM_DeInit. l 名为PPP_Init的函数,其功能为通过设置PPP_InitTypeDef 结构中的各种参数来定义外设的功能,例如:USART_Init . l 名为PPP_Cmd的函数,其功能为使能或者失能外设PPP,例如: SPI_Cmd. l 名为PPP_ITConfig的函数,其功能为使能或者失能来自外设PPP某中断源,例如: RCC_ITConfig. l 名为PPP_DMAConfig的函数,其功能为使能或者失能外设PPP的DMA接口,例如:TIM1_DMAConfig. l 用以配置外设功能的函数,总是以字符串“Config”结尾,例如GPIO_PinRemapConfig. l 名为PPP_GetFlagStatus的函数,其功能为检查外设PPP某标志位被设置与否,例如:I2C_GetFlagStatus. l 名为PPP_ClearFlag的函数,其功能为清除外设PPP标志位,例如:I2C_ClearFlag. l 名为PPP_GetITStatus的函数,其功能为判断来自外设PPP的中断发生与否,例如:I2C_GetITStatus. l 名为PPP_ClearITPendingBit的函数,其功能为清除外设PPP中断待处理标志位,例如: I2C_ClearITPendingBit. 这样的命名方式非常便于程序的编写和阅读,以标准外设库中的示例函数为例,下面摘录了STM32F10x_StdPeriph_Examples\ADC\3ADCs_DMA\mian.c中的一段程序。 - DMA_InitType Def DMA_InitStructure;" I i: _+ c9 H% ~% K
; t t! d* t! F6 `% Y6 H3 r( B- /* DMA1 channel1 configuration ----------------------------------------------*/
: D2 I U+ n C; M' e. z; y/ E8 L% f - ) {5 {' n+ Q3 \% f
- DMA_DeInit(DMA1_Channel1);
1 r0 v p0 R0 Y
- p* [' a: H, t @* Y, O, I- DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
- s0 Y5 ^4 q5 a* N
$ E5 k1 T' O+ a2 |7 B- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC1ConvertedValue;
3 R2 M3 d7 R3 g+ k& U6 d# X4 e/ w( D - ' B' N/ x! n& Z2 y* i. k; k2 i
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;1 G' K( s6 M2 U) {- M* i. U- n x
- 2 t2 a+ G* `6 M4 E! R. P
- DMA_InitStructure.DMA_BufferSize = 1;
: s( x8 L0 y* Q( }7 e - ( t' t+ }4 I5 g+ X- L+ }4 `. w+ X
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
, b! M7 B: P4 S# R1 [5 j
3 U7 R/ E5 V2 F5 x7 L0 t3 _- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;) _8 k% g, C% t$ W
- ) J+ ?0 Q6 o. M; ~& v4 k* I
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
+ O4 a' Y- j! \% v l4 i/ v" D. v
8 s! i9 L. y* _. c$ w+ x F- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;$ u7 y& g+ |* q; o8 ]
4 X, ]# S' F+ C7 C' \- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
( D. D# s2 K+ `; o0 s* Y8 j - ) D9 A( ]/ q N- F# J. J) \6 x
- DMA_InitStructure.DMA_Priority = DMA_Priority_High;
1 Y/ B; P5 ?; A5 t- I - / _! g/ b. G- u6 B7 l
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;) Y5 {5 H( N: e6 i) |. M8 o- g
- ' A. z" a ^9 C2 c4 |8 D: T0 m& O
- DMA_Init(DMA1_Channel1, &DMA_InitStructure);
: h# i5 ]3 i# q; ? - % z; M1 \1 x7 T
- /* Enable DMA1 channel1 */ b6 [- N6 M3 A, W, [& j
2 K1 ^7 }/ B2 L `. o# G, F- DMA_Cmd(DMA1_Channel1, ENABLE);
复制代码 ; N m, d. _( s6 U
这段程序完成了DMA1通道的配置,首先定义了DMA_InitType DMA_InitStructure,接着配置DMA_InitType的各种参数,各参数的命名方式也均使用约定的命名方式,从命名就能够很容易的看出各参数所指代的具体功能。功能参数配置完成后,使用DMA_Init(DMA1_Channel1, &DMA_InitStructure);完成相应外设的初始化,最后使用DMA_Cmd(DMA1_Channel1, ENABLE) 使能相应外设。从这个例子就能够很容易的看出标准外设库这种规范化的命名规则给编写和阅读程序带来的好处。 3. 变量定义在早期的版本中有24个变量定义,在Keil的安装根目录下,可以找到对应的定义,路径为:Keil\ARM\INC\ST\STM32F10x\stm32f10x_type.h - /* Includes ------------------------------------------------------------------*/0 B j$ J4 \6 J, E( O
; X9 W2 _2 f* |- /* Exported types ------------------------------------------------------------*/. ?4 {# k3 r% R u, z. j
- " y4 L0 t; k6 K& W6 ?& ?" ?8 C
- typedef signed long s32;$ Y& U; N8 d; r
* n* {& Z8 a" \; \6 U4 J* }- typedef signed short s16;
4 l6 d8 f4 z0 o - 1 m- h |2 {9 w3 E+ g$ k7 }! Z$ c
- typedef signed char s8;, i% U' d( I! s! C4 Q% T2 O
0 X/ ?5 E/ c( c6 A: c- typedef signed long const sc32; /* Read Only */
4 c J! l- y& B- w$ W7 ~! }
$ d3 h5 k( x; ^' Y- typedef signed short const sc16; /* Read Only */
' H" V: d" g) h( r3 z
" k, R- [! `( J. a8 E( [- G- typedef signed char const sc8; /* Read Only */
, R$ F( j& u, z4 f, R3 [
) x( q* R: ? t* I0 o; C2 |- typedef volatile signed long vs32;
: A5 z' X% g$ q
D2 |$ r k" r5 M) ?7 z5 F: t- typedef volatile signed short vs16;
: j& N8 n" ^) ^+ L - + s/ J/ x$ f" x+ w! ]
- typedef volatile signed char vs8; }+ l3 T8 S4 R5 v, d5 }
1 p" I2 w& v% X5 X7 a7 I4 Y% P- typedef volatile signed long const vsc32; /* Read Only */
4 N# j6 ?7 }2 W- D - % S* O: R; [, m" v5 l
- typedef volatile signed short const vsc16; /* Read Only */
: W$ |6 d& d, ]; I: j" X6 f6 n1 ]' D
, }1 z' o9 \- |! _/ E- typedef volatile signed char const vsc8; /* Read Only *// Y0 C. i# L- R# V& y" Q# @
0 X8 d+ R3 E" s0 X$ a- typedef unsigned long u32;
/ z- l/ h/ B i* U* d. U
& |" u& j1 f" X- F7 L1 A4 W) U- typedef unsigned short u16;
3 f. R5 G5 G! d( }" O" r - & z4 V2 |3 @+ U/ M e8 Z
- typedef unsigned char u8;
" K% m7 p% k' V1 B$ g9 B5 W* q
y5 R6 n8 N" P9 r, T- typedef unsigned long const uc32; /* Read Only */6 b, ~- z. X) [3 A* W) O
" ` j0 _# O9 \# J- typedef unsigned short const uc16; /* Read Only */
8 |9 c3 j4 A. `
' c! |' v. R1 o4 [5 H8 s' Y" @5 b7 T- typedef unsigned char const uc8; /* Read Only */
0 m8 o- [* n, t$ B; g* V1 Y - ) R) i) l$ k0 V/ Y' e
- typedef volatile unsigned long vu32;0 x; u& T4 m1 l4 U, r
- 7 U- V+ g- Y# X. c
- typedef volatile unsigned short vu16;; u9 _7 s6 r' ]6 {
( }$ f- _5 k7 p' u6 ^- R+ \- R# R6 |5 d- typedef volatile unsigned char vu8;
9 d- W- b; u8 m$ e: Q. w$ w) \
+ u+ a9 C) L g# o- typedef volatile unsigned long const vuc32; /* Read Only */1 g7 v; @" H z* d. ]8 a/ r1 q
1 M [1 r' ]" ~- U- typedef volatile unsigned short const vuc16; /* Read Only */
5 m3 k: l7 e/ o0 u6 g
7 @8 n9 i6 }: h4 I3 y6 H- typedef volatile unsigned char const vuc8; /* Read Only */
复制代码
j( k. {( P, m3.0以后的版本中使用了CMSIS数据类型,变量的定义有所不同,但是出于兼容旧版本的目的,以上的数据类型仍然兼容。CMSIS的IO类型限定词如表 5‑7所示,CMSIS和STM32固件库的数据类型对比如表 5‑8所示。这些数据类型可以在STM32F10x_StdPeriph_Lib_V3.4.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\stm32f10x.h中找到具体的定义,此部分定义如下。 - /*!< STM32F10x Standard Peripheral Library old types (maintained for legacy purpose) */ h6 n3 e# t) Z4 b# g
- " r) ]- P9 q$ ]) G! N
- typedef int32_t s32;
6 v; I' k0 J( K
0 D2 C c0 r% ^, B ]2 g2 k- typedef int16_t s16;
2 l% r4 |- n0 _5 ?% P
) t7 f1 i& h" N" U, r* l) G- typedef int8_t s8;
2 O, ?& Z' c* A1 | - ) M+ {" n5 f }; f
- typedef const int32_t sc32; /*!< Read Only */* L5 w# B, b1 s# W; c. O
- . w& R2 ]" P. o4 h0 X/ d2 E
- typedef const int16_t sc16; /*!< Read Only */
* n9 C' `- U: c) U7 m2 E
# [* \, ?! ~. F1 S& `& J% s- typedef const int8_t sc8; /*!< Read Only */
4 D3 S* t E; I* q: m! @
4 @* Z- G* E$ i- typedef __IO int32_t vs32; S6 U9 b2 T* K% W2 G9 F
1 u" [1 p) t- c% c2 Y/ T- typedef __IO int16_t vs16;: ^5 M" L) D* ~5 M" V! U
- ; x0 v$ V5 M% w. U6 m0 a8 K
- typedef __IO int8_t vs8;
. ]+ A0 e8 n% @" Q+ g( `+ B
. p4 B* n5 O: L- typedef __I int32_t vsc32; /*!< Read Only */# r3 X/ S' O' \: C, G" _
& g& U$ ~7 X5 [ q4 M t- typedef __I int16_t vsc16; /*!< Read Only */
$ q) D0 k' `) x( H5 D, \* @5 b - * X1 F; j0 T, Y1 X- v
- typedef __I int8_t vsc8; /*!< Read Only */8 N: N! c5 ]0 [4 L: A
# f- g# a* W* _3 S$ D6 B' E4 r- typedef uint32_t u32;0 c8 [3 o6 @' ^# w
" `! c$ z i# @. j" j( H$ z- typedef uint16_t u16;
( x+ |; i8 h' z& w
+ M$ S) c7 V' U! q5 |9 ?- typedef uint8_t u8;5 Y* c7 s' N- W* X
- * M: S8 T; A Z9 `3 ]8 M
- typedef const uint32_t uc32; /*!< Read Only */
/ Z9 u9 ]$ `% G3 ^5 W, I) Y - + `) j. b9 {2 N. k. @
- typedef const uint16_t uc16; /*!< Read Only */$ \) Y" E! U9 ~- ?* H! ?7 {
- 4 v' S' B D* P* s* X
- typedef const uint8_t uc8; /*!< Read Only */
1 \3 ?2 i; w) z( l1 y - ) ]* Y+ [6 `$ o2 Z3 ?+ V) T
- typedef __IO uint32_t vu32;
" |8 j5 f6 J3 S) D" l+ P - 7 P* S- C d- k. Z' \; a# i7 v
- typedef __IO uint16_t vu16;2 \7 v* |5 r V; {3 S
1 ]) V/ J [( ]0 a5 o0 d* |- typedef __IO uint8_t vu8;7 P! O& g& O9 w; K
- ; p( C5 \; p2 R$ O
- typedef __I uint32_t vuc32; /*!< Read Only */5 l1 R. `9 i' E
- 0 y* G* f* N# e1 O7 U0 L. K5 Y0 r
- typedef __I uint16_t vuc16; /*!< Read Only */
! i* L. s+ I9 u& |) M8 p9 I - # g0 X0 E: s( Q3 J; W+ p/ @
- typedef __I uint8_t vuc8; /*!< Read Only */
复制代码
- I, r/ x9 r3 s( y1 }) X5 k# Z2 c表 5‑7 CMSIS IO类型限定词 IO类限定词 | #define | 描述 | _I | volatile const | 只读访问 | _O | volatile | 只写访问 | _IO | volatile | 读和写访问 | 8 y9 [! |; q) o8 {3 A$ r4 R) {
7 L. e. Y) H: T6 l9 q
表 5‑8 固件库与CMSIS数据类型对比 固件库类型 | CMSIS类型 | 描述 | s32 | int32_t | 易挥发只读有符号32位数据 | s16 | int16_t | 易挥发只读有符号16位数据 | s8 | int8_t | 易挥发只读有符号8位数据 | sc32 | const int32_t | 只读有符号32位数据 | sc16 | const int16_t | 只读有符号16位数据 | sc8 | const int8_t | 只读有符号8位数据 | vs32 | _IO int32_t | 易挥发读写访问有符号32位数据 | vs16 | _IO int16_t | 易挥发读写访问有符号16位数据 | vs8 | _IO int8_t | 易挥发读写访问有符号8位数据 | vsc32 | _I int32_t | 易挥发只读有符号32位数据 | vsc16 | _I int16_t | 易挥发只读有符号16位数据 | vsc8 | _I int8_t | 易挥发只读有符号8位数据 | u32 | uint32_t | 无符号32位数据 | u16 | uint16_t | 无符号16位数据 | u8 | uint8_t | 无符号8位数据 | uc32 | const uint32_t | 只读无符号32位数据 | uc16 | const uint16_t | 只读无符号16位数据 | uc8 | const uint8_t | 只读无符号8位数据 | vu32 | _IO uint32_t | 易挥发读写访问无符号32位数据 | vu16 | _IO uint16_t | 易挥发读写访问无符号16位数据 | vu8 | _IO uint8_t | 易挥发读写访问无符号8位数据 | vuc32 | _I uint32_t | 易挥发只读无符号32位数据 | vuc16 | _I uint16_t | 易挥发只读无符号16位数据 | vuc8 | _I uint8_t | 易挥发只读无符号8位数据 | 8 l& m9 H0 k! j8 P" t5 ]
stm32f10x.h文件中还包含了常用的布尔形变量定义,如: - typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
5 {! c" p! }3 ~5 V# A0 j
* r3 [5 ]3 V* |; j/ J- typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;8 o# m) J d: d1 F' B
5 r" B5 A: n( K& l: @- #define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))
# ^' I1 I; u) N3 W, `! y0 G - 3 a% }5 r7 I& K& p5 i* n: \ m
- typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus;
复制代码
- }: f- W9 K: }& ~) ~4 `* z' l' n" k
不同版本的标准外设库的变量定义略有不同,如3.4版本中就没有之前版本的TRUE和FALSE的定义,用户也可以根据自己的需求按照上面的格式定义自己的布尔形变量。在使用标准外设库进行开发遇到相关的定义问题时应首先找到对应的头文件定义。 4. 使用步骤前面几个小节已经详细介绍了标准外设库的组成结构以及部分主要文件的功能描述,那么如果在开发中使用标准外设库需要哪些描述呢?下面就进行简要的介绍,这儿介绍的使用方法是与开发环境无关的,在不同的开发环境中可能在操作方式上略有不同,但是总体的流程都是一样的,下一小节将介绍在MDK ARM开发环境下使用标准外设库的详细过程。 首先新建一个项目并设置工具链对应的启动文件,可以使用标准外设库中提供的模板,也可以自己根据自己的需求新建。标准外设库中已经提供了不同工具链对应的文件,位于STM32F10x_StdPeriph_Lib_V3.4.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup目录下。 其次按照使用产品的具体型号选择具体的启动文件,加入工程。文件主要按照使用产品的容量进行区分,根据产品容量进行选择即可。每个文件的具体含义可以在“stm32f10x.h”文件中找到对应的说明,摘录如下: - #if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD) && !defined (STM32F10X_HD_VL) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL)
+ @. d% _9 Y: S9 V V( p# C ^
# J% ^& W2 r8 Y+ _+ R# ]( w* ]" f- /* #define STM32F10X_LD */ /*!< STM32F10X_LD: STM32 Low density devices */7 B q' D+ d6 D: `0 E% S, J& i
- ; i" q0 Q m5 v
- /* #define STM32F10X_LD_VL */ /*!< STM32F10X_LD_VL: STM32 Low density Value Line devices */& M' L" U3 J, A
% k: l0 B; w. C& _- w: a8 R, r- /* #define STM32F10X_MD */ /*!< STM32F10X_MD: STM32 Medium density devices */1 t. e0 i) U3 O `+ r
- 8 C3 b e U* L& \$ y
- /* #define STM32F10X_MD_VL */ /*!< STM32F10X_MD_VL: STM32 Medium density Value Line devices */ /* #define STM32F10X_HD */ /*!< STM32F10X_HD: STM32 High density devices */
$ E: I5 E: D2 z
6 d- a, ]1 h/ Q, u- /* #define STM32F10X_HD_VL */ /*!< STM32F10X_HD_VL: STM32 High density value line devices */' f3 ~5 f0 [% l2 P+ ], E% q3 q
, @7 J0 E# X5 q* F+ A& J4 V- /* #define STM32F10X_XL */ /*!< STM32F10X_XL: STM32 XL-density devices */
; N2 P; [2 l$ s4 p - `- o. ]) k1 {$ G! \
- /* #define STM32F10X_CL */ /*!< STM32F10X_CL: STM32 Connectivity line devices */6 C. ?/ U# Y; I+ C
- 7 u! N* x$ T# P/ [2 O+ K4 E& Z
- #endif
( u* f7 h$ |, q" c* d, F - ) L* i( {* M( q3 ~. ?) {* b, B
- /* Tip: To avoid modifying this file each time you need to switch between these
& A. S8 m3 w1 f) ^8 @( s
/ w; m% {8 U3 d! [/ y2 b# u) Z) s- devices, you can define the device in your toolchain compiler preprocessor.( D0 C9 F) }# c$ ~5 w
- $ Q1 |8 F- R% I
- - Low-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers/ ]5 {/ \1 S3 L' x, f% Z3 I& h
- & L8 o* v. _5 k* z
- where the Flash memory density ranges between 16 and 32 Kbytes.! b/ `9 Z1 l! z+ U6 X$ ~; z
- * O" O7 i/ d1 W& b8 [
- - Low-density value line devices are STM32F100xx microcontrollers where the Flash+ j: R+ o9 t2 @8 _
" M- M( X u3 L* y6 e- memory density ranges between 16 and 32 Kbytes.+ U# w4 r1 `6 ~+ h: m
- " R5 W6 a, U, l
- - Medium-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers
5 H m; T7 B" k3 m9 Z& w( d - ; H4 l3 S$ B! i6 n2 u
- where the Flash memory density ranges between 64 and 128 Kbytes.7 P0 j: f) U$ {& O- f! w: p4 a/ N
0 r7 Z" V3 c7 |9 P! J5 L) _- - Medium-density value line devices are STM32F100xx microcontrollers where the
% f E3 z; s: n& B) H/ P* L - * b O% |5 L9 f: L* I" L+ i8 e! A
- Flash memory density ranges between 64 and 128 Kbytes.
$ Q( x" \) s/ u7 h3 ?
; B! C0 z" o: k9 e5 H; ?# F- - High-density devices are STM32F101xx and STM32F103xx microcontrollers where% [$ u7 X5 o7 k
2 ^6 q7 K6 z- e, P1 g- the Flash memory density ranges between 256 and 512 Kbytes.
# G& }5 Q, y* i
9 |4 N! p9 g6 I5 u% N0 Y( W. Q- - High-density value line devices are STM32F100xx microcontrollers where the
; D+ c1 W4 S1 `4 S3 V# B
@" R8 N7 S; |+ H9 {7 p; w- Flash memory density ranges between 256 and 512 Kbytes." g. V! {. N4 H6 [
8 h: `4 y; Y$ ]; q- i- - XL-density devices are STM32F101xx and STM32F103xx microcontrollers where5 p7 G1 S) j- c" O
- ; Y6 r' F; P8 N3 x! i. z! [
- the Flash memory density ranges between 512 and 1024 Kbytes.
' Y- N( t) o7 w/ a6 ?
- t- J, z( P- Q% M8 C- - Connectivity line devices are STM32F105xx and STM32F107xx microcontrollers.
# c: j& {9 {5 y3 f4 `. ?/ J5 x
3 i. T1 \. d% t6 a; Q- */
复制代码 % l4 _7 E& l G' c- F [' x% \
“stm32f10x.h”是整个标准外设库的入口文件,这个文件包含了STM32F10x全系列所有外设寄存器的定义(寄存器的基地址和布局)、位定义、中断向量表、存储空间的地址映射等。为了是这个文件适用于不同系列的产品,程序中是通过宏定义来实现不同产品的匹配的,上面这段程序的注释中已经详细给出了每个启动文件所对应的产品系列,与之对应,也要相应的修改这个入口文件,需要根据所使用的产品系列正确的注释/去掉相应的注释define。在这段程序的下方同样有这样的一个注释程序/*#define USE_STDPERIPH_DRIVER*/ 用于选择是否使用标准外设库,如果保留这个注释,则用户开发程序可以基于直接访问“stm32f10x.h”中定义的外设寄存器,所有的操作均基于寄存器完成,目前不使用固件库的单片机开发,如51、AVR、MSP430等其实都是采用此种方式,通过在对应型号的头文件中进行外设寄存器等方面的定义,从而在程序中对相应的寄存器操作完成相应的功能设计。 如果去掉/*#define USE_STDPERIPH_DRIVER*/的注释,则是使用标准外设库进行开发,用户需要使用在文件“stm32f10x_conf.h”中,选择要用的外设,外设同样是通过注释/去掉注释的方式来选择。示例程序如下: - /* Uncomment the line below to enable peripheral header file inclusion */
8 q8 a; q% ^% r4 ~
) I6 M6 D$ b# _6 m& {% p9 J- #include "stm32f10x_adc.h"% f, B( w, P) l' a
' h8 N: [! y0 a; F* C- /* #include "stm32f10x_bkp.h" */( O0 @+ e+ ?8 `$ @0 X! \9 q
- ) e5 u5 T. }) u. w2 F+ N
- /* #include "stm32f10x_can.h" */
$ ]$ ?2 p; o+ ]- z( S - . _! t0 w/ }- x- E$ J* i) {
- /* #include "stm32f10x_cec.h" */; M$ E/ n [4 L& a" E( M' Z
- ; J2 t* a4 k* A* g# B
- /* #include "stm32f10x_crc.h" */
t) J/ L5 k+ B2 b# F
- {: P8 ]2 y( W3 k( C o: M- /* #include "stm32f10x_dac.h" */
/ `( L/ o( j. q; u4 B
9 @4 @' \3 b0 g8 P- /* #include "stm32f10x_dbgmcu.h" */
, K0 T6 i# R) \4 J, q; P8 L - 7 V$ B* |' f- ~4 X# _
- #include "stm32f10x_dma.h"
4 ]) J' c* a- |; v7 X
* o/ o4 a9 S% f; Q4 H, Q2 o- r- e- /* #include "stm32f10x_exti.h" */' a0 Y3 A+ s& O( c+ q- J B
- 2 d2 y/ B' [. b u$ }. h! W S
- /* #include "stm32f10x_flash.h" */
9 ^1 ^# w9 A$ F- T
: h& V+ [- |3 A+ f+ D4 H2 a- /* #include "stm32f10x_fsmc.h" */. v% g9 [4 f: |# Q2 a
. J7 B# y4 n* u9 l* s2 d- #include "stm32f10x_gpio.h"% O; m W5 [* z: p
- 2 ^! S G; ]2 V7 u# ?
- /* #include "stm32f10x_i2c.h" */
3 |* F8 u7 ~1 q. r0 Y - . e2 n. F- P; z
- /* #include "stm32f10x_iwdg.h" */
8 ^' q( z1 v1 @ H+ P Y - & r5 V- c U- }
- /* #include "stm32f10x_pwr.h" */5 x" t8 M% o6 ] e6 |, W
' D9 y8 k3 C# {# M- #include "stm32f10x_rcc.h" _3 B) ] j8 s/ A- F u
- I9 K5 v# ]7 `- /* #include "stm32f10x_rtc.h" */
7 A+ m; f1 [, l - ' k7 O1 m; K$ U* d2 F
- /* #include "stm32f10x_sdio.h" */
% f, b. @$ F! K4 C5 H2 e
& X. `% ~5 H' g: p x% B- /* #include "stm32f10x_spi.h" */, U: v0 f7 b: R
- 9 ~* p2 c M+ W
- /* #include "stm32f10x_tim.h" */1 }& Y) o" I5 r$ Z( J" O8 V6 R9 x
- # e9 a' w* E+ H9 G# X
- /* #include "stm32f10x_usart.h" */& l- f }. q9 t3 _" G7 ?1 g: W
: t L4 y# U' D1 ^1 p1 t- /* #include "stm32f10x_wwdg.h" */% q1 v6 B% l3 n: c' a
& q. {8 {) c8 c8 H5 Q- #include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */
复制代码
1 ?* L9 t0 q$ [( _$ P' g8 V; V9 m, O" n
上面一段程序来自于例程中的AD采集程序,程序使用了AD和DMA,因此去掉相应的注释,同时几乎所有的应用都需要使用复位与时钟以及通用I/O,因此这两项是必须的, 而多数程序同样要使用NVIC中断IRQ设置和SysTick时钟源设置,那么 “misc.h”这一项也是必须的。 上面已经针对具体的产品信号和程序功能进行了针对性的配置,接下来需要配置系统所使用的时钟,系统时钟在“system_stm32f10x.c”同样通过注释的方式来配置,程序如下: - #if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)0 }- u3 H2 M, f) H/ V
- J' j4 l6 v, O) R! ^( p
- /* #define SYSCLK_FREQ_HSE HSE_VALUE */8 k2 X' |/ z6 o! Q
+ Q* o" w) ]' z! v- #define SYSCLK_FREQ_24MHz 24000000! n5 ]" z3 c2 | h0 F
6 k( [1 q5 K1 E" G' P* s, Y; {- #else
. K- B# W! w- \* d6 Z - 7 f& L$ J C" l, l
- /* #define SYSCLK_FREQ_HSE HSE_VALUE */
! G: c0 a: t8 X% J" c - $ U+ t& N1 u8 r6 m: F* j
- /* #define SYSCLK_FREQ_24MHz 24000000 */
) t9 ^+ K% u3 P# X
8 P; Q) Z. t m3 |- /* #define SYSCLK_FREQ_36MHz 36000000 */
2 M0 [# e. a" F( r - / p8 q5 i, J9 n/ z: b3 a/ @
- /* #define SYSCLK_FREQ_48MHz 48000000 */
4 h/ O: {7 E* I - 5 b1 f8 @! k+ D5 Y) E; O
- /* #define SYSCLK_FREQ_56MHz 56000000 */
- X2 ?' H* y* p9 `* F( X - + e3 U: J% \3 A6 P$ H" j
- #define SYSCLK_FREQ_72MHz 72000000# r& m. a% R: _
- 9 a5 S5 S, h- A! E' }2 I
- #endif
复制代码
% N! l0 {5 r6 A, p如果这儿没有明确的定义那么HSI时钟将会作为系统时钟。 至此,已经配置了系统的主要外部参数,这些参数主要是通过更改相关的宏定义来实现的,有些开发环境,例如Keil支持在软件设置中加入全局宏定义,因此像芯片系列定义,是否使用固件库定义等也可以通过软件添加来实现。 完成了主要参数配置以后就可以进行程序的开发了,标准外设库开发就可以使用标准外设库中提供的方便的API函数进行相应的功能设计了。在4.2.2小节中已经介绍了基于标准外设库开发的优势,配置完成后,程序中仍然可以直接更改相应寄存器的配置,通过对寄存器的操作可以提高程序的效率,因此可以使用标准外设库和寄存器操作两种相结合的方式。 * ^, A! {7 }8 M4 c2 j
8 G, w- `5 K: y. j) ~" S! `
|