STM32 Embedded Software 工作以来一直使用 ST 的 STM32 系列芯片,ST 为开发者提供了非常方便的开发库。到目前为止,有标准外设库(SPL 库)、HAL 库、LL 库 三种。前两者都是常用的库,后面的 LL 库是 ST 最近才添加,随 HAL 源码包一起提供,目前支持的芯片也偏少。各库如下所示:
7 r$ g6 E& c& k" V; U 9 U6 A6 f: N7 S7 |2 q, t( c) H0 k
: O" X; W0 A1 c
其中,SPL 库 和 HAL 库两者相互独立,互不兼容。几种库的比较如下:1 P* V4 i$ L% `/ a' D- Y
* m1 N. e9 O2 @& \ # W# y- X1 Z$ P
" i' Q7 W* L/ h( n5 Z5 V
目前几种库对不同芯片的支持情况如下:$ D; T' ?' g7 M. D# O
5 i& }& t" P, W- Z5 u1 E
) N' B% P# Q% L- p3 _ ST 中文官网上有一篇《关于ST库函数的代码性能对比》的文章,其中对比了各种库的性能如下:& w# y% R s/ D, T! q4 v
2 n6 T; A; L0 t) m4 D
! L, v- z* Z' V9 h/ b
" [: H6 \7 B- H0 m2 Q5 ]1 d' F; L3 I& O% e
STM32 Snippets/ H2 q( {; w/ D
它是代码示例的集合,直接基于 STM32 外设寄存器,可在文档和软件包中使用。由于处在最底层,因此需要开发者直接操作外设寄存器,对开发者要求比较高,通常针对于对汇编程序比较了解的资深嵌入式工程师!! p0 @9 N) R# e/ o# F0 x( V8 V
这个库使用比较少,目前只在 STM32F0 和 STM32L0 系列中有提供。两个库都有各自的说明文档。主要就是下图所示的这些:
7 y% h& l2 G3 f* l# F' d% B
% E+ m# T t2 L2 G y. w- Q6 X9 J& k1 H1 [6 k' D, S! Q
严格来说,它不能称为库,仅仅就是将 MCU 中的寄存器进行了结构化的封装,库文件主要就是一些 .h 文件。如下是对 ADC 的封装6 o3 \0 a4 ]! Z7 `$ J0 f! \
0 X* w0 \6 P3 e
- /**, }1 S2 Z1 v/ I9 R6 B% V
- * @brief Analog to Digital Converter
1 h/ |; l8 j$ S; }5 u5 T - */
' c( w' a1 a; A2 Z - : b1 w+ D6 ?5 r B
- typedef struct' I% y v6 i* o' _$ P$ p1 X. n1 h
- {
* d2 d( X5 V v: U* _ - __IO uint32_t ISR; /*!< ADC Interrupt and Status register, Address offset:0x00 */
0 k$ _; Q( ]& G - __IO uint32_t IER; /*!< ADC Interrupt Enable register, Address offset:0x04 */
( } Y( a# d5 r$ S# U - __IO uint32_t CR; /*!< ADC Control register, Address offset:0x08 */
' @; T. O2 d* b - __IO uint32_t CFGR1; /*!< ADC Configuration register 1, Address offset:0x0C */. U) F& V1 E6 m) f7 F) t# V
- __IO uint32_t CFGR2; /*!< ADC Configuration register 2, Address offset:0x10 */
* U$ b* a1 r) E; h - __IO uint32_t SMPR; /*!< ADC Sampling time register, Address offset:0x14 */& L3 Q1 j7 G3 n8 n" e0 z
- uint32_t RESERVED1; /*!< Reserved, 0x18 */8 y5 }2 d, K: V" o/ E7 e3 B
- uint32_t RESERVED2; /*!< Reserved, 0x1C */. S1 y( N+ G# j6 S$ @3 t
- __IO uint32_t TR; /*!< ADC watchdog threshold register, Address offset:0x20 */
: b6 L, R R/ w* j7 X - uint32_t RESERVED3; /*!< Reserved, 0x24 */
# m/ w, Z; Q; M. d$ y5 C* c* q - __IO uint32_t CHSELR; /*!< ADC channel selection register, Address offset:0x28 */
' H& O9 W' c- X& T4 A# J - uint32_t RESERVED4[5]; /*!< Reserved, 0x2C */ A3 E1 R2 s6 l5 c( j2 p
- __IO uint32_t DR; /*!< ADC data register, Address offset:0x40 */& U7 ]8 f K/ S$ G! J
- }ADC_TypeDef;! d9 m2 j* F5 c9 r8 A6 A7 p" B
- 5 d% M, j* c) \- }$ |
- typedef struct" a: j. A) o* n, P( o9 ?" t& S5 W
- {+ H. r5 d y( e# {/ L0 J! t# k
- __IO uint32_t CCR;& [/ U& \5 S" G/ W, f5 d9 m
- }ADC_Common_TypeDef;
复制代码 7 W! q2 \# A: ]# f2 @( C
! Q: c% j7 o; @+ L3 o
6 z, ^7 F7 z4 x: K) q* M1 F3 d在实际使用时,我们就可以结构化的访问 MCU 的寄存器,如下是配置 ADC 的函数实现:
& u' i8 r. J& e
- /**1 o* ? h6 P/ Z7 \
- * @brief This function configure the ADC to convert the internal reference voltage (VRefInt)
$ ^$ _7 m, X& s- j- ` y+ ^" R; ] - * The conversion frequency is 14MHz - t' r* q/ w4 Q/ n) p8 T+ W
- * @param None
' X7 X4 s$ P& t1 ]: F/ W8 x - * @retval None7 s/ u+ P2 Y6 a: A/ p
- */% G0 e+ L: Y( L/ R# b: D
- __INLINE void ConfigureADC(void)
2 t0 z9 c* Z1 e7 B, g - {
) V/ X( R8 m* e) { - /* (1) Select the clock mode (refer ADC_PRESCALER definition :
8 W3 g* ]! U" r8 ` - HSI14 by writing 00 in CKMODE (reset value)) W; B8 |* r0 p/ S
- DIV2 or DIV 4 */( ?1 N& ?. | Y' S/ b3 y) x
- /* (2) Select the auto off mode */7 h7 n) G0 G( s$ y+ O
- /* (3) Select CHSEL17 for VRefInt */8 h! U( }+ |# H4 _+ Q# {
- /* (4) Select a sampling mode of 111 i.e. 239.5 ADC clk to be greater than 17.1us */
/ f6 y6 J. M. f$ ?- R7 V0 O. p - /* (5) Wake-up the VREFINT (only for VBAT, Temp sensor and VRefInt) */$ T5 w H8 j: @9 c
- ADC1->CFGR2 |= ADC_PRESCALER; /* (1) */0 t9 C4 ^/ O S ^7 w" l0 n
- ADC1->CFGR1 |= ADC_CFGR1_AUTOFF; /* (2) */
4 _9 h o) F; B3 O$ [& n - ADC1->CHSELR = ADC_CHSELR_CHSEL17; /* (3) */
) [7 n7 j% R5 G6 L" Q$ A' \$ l0 c - ADC1->SMPR |= ADC_SMPR_SMP_0 | ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2; /* (4) */
0 D. q% E1 Z. X& P - ADC->CCR |= ADC_CCR_VREFEN; /* (5) */$ Q' e& i% T0 \* q: M! Y
- }
复制代码 . [& L$ [9 T* F- J" Y
标准外设库(Standard Peripheral Libraries)
, H% ~* g1 F8 A& z; f! b 标准外设库(Standard Peripherals Library)是对 STM32 芯片的一个完整的封装,包括所有标准器件外设的器件驱动器。这应该是目前使用最多的 ST 库。几乎全部使用 C 语言实现。但是,标准外设库也是针对某一系列芯片而言的,没有可移植性。3 F- I2 B0 e- T: i# h H
. }6 n: T5 l: {2 [. x, J, [0 e
. a: [& y/ k$ u8 I3 d9 G 相对于 HAL 库,标准外设库仍然接近于寄存器操作,主要就是将一些基本的寄存器操作封装成了 C 函数。开发者需要关注所使用的外设是在哪个总线之上,具体寄存器的配置等底层信息。
+ X4 \$ Y. w+ l5 T- S& B
: ]0 |9 a+ t5 U 相对于 STM32 Snippets,标准外设库对各外设的进行了一次封装,而不是仅仅局限在对寄存器的封装。实现了各外设的基本操作接口。
* R. }1 J& n% B+ G! { 在文档方面,我只发现了针对 F2 系列和 F3 系列的详细说明文档,如下图所示:
' u% ~4 p! Y# Y
: b+ u9 r4 H% ^3 N+ ^
) f- A9 y/ ?$ K* z: q! `8 L* W8 e- w
标准外设库的文件基本架构并不复杂。下图显示了 STM32F10xx 标准外设库文件的基本架构0 \& P9 G0 _6 P* j, N8 x. v
* |3 Y$ W4 O, y1 q, h0 x& X8 n4 V Z
8 a. ~" Y, f/ ^
K* P7 G. M) T# V6 [0 m
其他系列的库文件结构和上图基本都是一致的!
2 l7 ~; T( d1 A, y7 V; V$ H
1 Q% w8 ^% S; t ST 为各系列提供的标准外设库稍微有些区别。例如,STM32F1x 的库和 STM32F3x 的库在文件结构上就有些不同,此外,在内部的实现上也稍微有些区别,这个在具体使用(移植)时,需要注意一下!但是,不同系列之间的差别并不是很大,而且在设计上是相同的。STM32 的标准外设库涵盖以下 3 个抽象级别:
4 ] C) z$ s& l2 p
- 包含位,位域和寄存器在内的完整的寄存器地址映射
- 涵盖所有外围功能(具有公共API的驱动器)的例程和数据结构的集合。
- 一组包含所有可用外设的示例,其中包含最常用的开发工具的模板项目。
! X# P/ b! D* P3 Q0 j* a2 L6 g9 g; g. r& F3 y' K2 \
关于更详细的信息,可以参考 ST 的官方文档,文档中对于标准外设库函数命名、文件结构等都有详细的说明。
$ q7 c1 r x5 o+ j) I: ^- V+ C STM32Cube
1 j, }0 \3 r3 p- ` ST 为新的标准库注册了一个新商标:STMCube™。并且,ST专门为其开发了配套的桌面软件 STMCubeMX,开发者可以直接使用该软件进行可视化配置,大大节省开发时间。
$ Z% g+ u' T H+ r6 ~( Q8 V 4 e( [( r7 \* i, l
$ I5 f, ^- ]" S' h 这其中就包含了 HAL 库和最近新增的 LL 库。如下图:4 m& Z* \/ y8 X$ Z- d
! C q* I. O* R
, _& `+ T1 S, B6 s) o* L
2 B2 O" ]9 D3 P# x$ K
从上图不难看出,LL 库和 HAL 库两者相互独立,只不过 LL 库更底层。而且,部分 HAL 库会调用LL库(例如:USB驱动)。同样,LL 库也会调用 HAL 库
8 h% Q% n- P) M8 k& R+ z2 m6 y 用户可以使用 STMCubeMX 直接生成对应芯片的整个项目(目前主流开发工具的项目基本全支持),STMCubeMX 负责给整理各种需要的源码文件。 1 C" |5 R' h0 s
注意:
& d" r0 R; z; [+ J 1. 个人感觉STMCubeMX生成的项目并不够简洁,源码的组织结构也并不是很好。
9 C: v8 \- E$ [ 2. STMCubeMX在生产项目时,可以选择使用HAL库或者LL库。但是部分组件的HAL库会调用LL库 * B" f$ ^. o% C: \. A/ D- q0 z4 c9 l
HAL 库7 S" j( }! y9 d6 a* T7 {; E: x
HAL是 Hardware Abstraction Layer 的缩写,中文名:硬件抽象层。HAL 库是 ST 为 STM32 最新推出的抽象层嵌入式软件,可以更好的确保跨 STM32 产品的最大可移植性。该库提供了一整套一致的中间件组件,如 RTOS,USB,TCP/IP 和 图形 等。
8 f8 Q( E" {# D2 i HAL 库是基于一个非限制性的 BSD 许可协议(Berkeley Software Distribution)而发布的开源代码。 ST 制作的中间件堆栈(USB 主机和设备库,STemWin)带有允许轻松重用的许可模式, 只要是在 ST 公司的 MCU 芯片上使用,库中的中间件(USB 主机/设备库,STemWin)协议栈即被允许随便修改,并可以反复使用。至于基于其它著名的开源解决方案商的中间件(FreeRTOS,FatFs,LwIP和PolarSSL)也都具有友好的用户许可条款。
9 I+ J' C, n" G& C( [ 作为目前 ST 主推的外设库,HAL库相关的文档还是非常详细的。
5 q" |1 m+ v1 Y6 J; C) y 7 t/ m8 e4 U; s p. V' @: g
0 B$ C& {4 X! u7 y; b& f u& k
可以说HAL 库就是用来取代之前的标准外设库的。相比标准外设库,STM32Cube HAL 库表现出更高的抽象整合水平,HAL API 集中关注各外设的公共函数功能,这样便于定义一套通用的用户友好的API函数接口,从而可以轻松实现从一个STM32产品移植到另一个不同的STM32系列产品。HAL库是ST未来主推的库,从前年开始ST新出的芯片已经没有STD库了,比如 F7 系列。目前,HAL库已经支持STM32全线产品。4 O5 i# e3 s7 K
9 l5 [3 W! w8 T f
使用HAL库编程,最好尽量符合HAL库编程的整体架构。
_+ f- Q. [' D LL 库' ~4 `; e; F" q2 |" G
LL库(Low Layer)是 ST 最近新增的库,与 HAL 库捆绑发布,文档也是和 HAL 库文档在一起的,比如:在STM32F3x 的 HAL 库说明文档中,ST 新增了LL库这一章节,但是在 F2x 的HAL文档中就没有。* ^$ M& B3 m0 Q; A$ i3 V$ r
LL 库更接近硬件层,对需要复杂上层协议栈的外设不适用,直接操作寄存器。其支持所有外设。使用方法: - 独立使用,该库完全独立实现,可以完全抛开 HAL 库,只用LL库编程完成。在使用STM32CubeMX生成项目时,直接选LL库即可。如果使用了复杂的外设,例如 USB,则会调用 HAL 库
- 混合使用,和 HAL 库结合使用。$ q. o l- l: M9 Z v
& m; U- G8 D) C& h
目前,CubeMX 在生成项目时,可以选择采用 LL 库。
LL 库文件的命名方式和 HAL 库基本相同。个人感觉,LL 库就是原来的标准外设库移植到 Cube下的新的实现,但是其实现方法更加高效、简洁。使用 LL 库编程和使用标准外设库的方式基本一样,但是确会得到比标准外设库更高的效率。
& R$ H8 A! M9 y5 H) C6 k% P
0 h# s" n+ ]! y# u" @ |