CMSIS (微控制器软件接口标准):Cortex Microcontroller Software Interface Standard,是由ARM和与其合作的芯片厂商(比如ST、NXP公司等)、软件工具厂商(Keil、IAR公司等),共同制定的标准。如果没有此标准,ARM公司的产品兼容性非常差。
下图中分为三层,第一层是用户层,主要是用户代码、在线调试;第二层是中间层(CMISIS),具体CMSIS体现到用户手册是CMSIS-Pack,每个公司提供的包可能不太一样,会根据自己的产品制作CMSIS包,其中HAL库(Peripheral HAL)属于其内容;第三层是硬件层。用户想要去开发硬件层,需要经过中间层,中间层的CMSIS包由众多厂商提供。
ST 为了方便用户开发 STM32芯片开发提供了三种库:
下图是直接操作寄存器、标准库、HAL库(不同系列兼容性好,具体表现是不同外设初始化都是使用一个函数,只是结构体、变量不一样,但是函数接口一样,移植方便。HAL库是抽象层的库,经过很多次的封装,导致效率比较低)、LL库(弥补了HAL库执行效率低的缺点,在HAL库内部一些API函数就会调用LL库中内容,但是LL库也有其缺点,不匹配部分复杂外设,比如USB)的特点:
Cube固件包其实就说CMSIS-Pack,STM32Cube固件包是ST公司根据标准提供的CMSIS-Pack。
之前文章中已经总结如何获取,具体查看【07】STM32·HAL库开发-新建寄存器版本MDK工程 |下载STM32Cube固件包 | 新建MDK工程步骤中的第1.1小节。
【_htmresc】文件夹中包含了一些图片之类的,对我们来说没任何作用;
【Documentation】文件夹中为固件包说明文档;
【Drivers】文件夹比较重要是驱动源码,新建工程时需要用到;
【Middlewares】中为中间文件,保存的是ST或者第三方的一些中间文件,后续会用到,新建工程可能暂时用不到;
【Projects】为ST官方开发板例程,参考作用;
【Utilities】为ST关方开发板例程提供公共组件,一般用不到,可以用作参考;
License.md文件包含了软件许可、软件版本信息;
package.xml为固件包版本信息;
Readme.md为自述文件;
Release_Notes.html为补充或更新说明的链接,可用浏览器打开。
F1系列【Drivers】文件夹中包含了三个文件夹【BSP】、【CMSIS】、HAL库驱动【STM32F1xx_HAL_Driver】。【BSP】中为板级驱动源码,也就是板级支持包,适配ST官方的开发板板级驱动(针对外设、或者板子上面的资源做的驱动),不同开发板板级驱动是不一样的,所以此文件更多是参考意义;【CMSIS】文件夹包含符合CMSIS的组件,包括:DSP库、Cortex-M内核及其设备文件、微控制器专用头文件、启动文件、专用系统文件等,创建工程时会复制其中部分文件;【STM32F1xx_HAL_Driver】为F1系列HAL库外设驱动源码,创建工程时会复制其中部分文件。
【Middlewares】中包含了【ST】和【Third_Party】两个文件夹。
在【CMSIS】文件夹中主要关注【Device】和【include】两个文件夹,新建工程时也是用到这两个文件夹;【Device】文件夹中包含微控制器专用头文件、启动文件、专用系统文件;【include】文件夹中包含Cortx-M内核及其设备文件、编译器相关头文件。
CMSIS标准规定软件包目录也就是STM32Cube固件库文件夹所包含的内容。用到的有【Driver】和【Include】文件夹。
【Device】文件夹中用到的文件包含以下文件,以下文件是简略后的文件。
stm32f103后面的xe是根据FLASH容量进行选择,在【include】文件夹中保存3个文件:stm32f1xx.h、stm32f103xe.h、system_stm32f1xx.h。
以下目录中保留文件system_stm32f1xx.c,因为编译器是ARM,所以保留【arm】文件夹中的启动文件,并根据型号和容量来选择保留哪个启动文件。
【include】文件夹中用到以下文件:cmsis_armcc.h(针对AC5编译器)、cmsis_armclang.h(针对AC6编译器)、cmsis_compiler.h(AC5和AC6编译器共用)、cmsis_version.h(编译器版本,也是共用的)、core_cm3.h (Cortex-M3内核,重点关注此文件)、 mpu_armv7.h(mpu内存保护)。
F1系列的HAL库被保存到【STM32F1xx_HAL_Driver】文件夹中,Src(Source):外设驱动源码;Inc(Include):外设驱动源码头文件。前面提到HAL库和LL库是捆绑发布的,就体现在驱动源码包含在【Inc】和【Src】文件夹中。【Inc】和【Src】文件夹中的【Legacy】文件夹是补充源文件,并不是所有系列都需要。
文件名中有hal的就是hal库文件,有ll的就是LL库文件。stm32f1xx_hal_conf.h文件可用于裁剪HAL库中用不到的功能,减少编译后得文件大小。
用户一般不会直接调用LL库,而是通过调用HAL库间接使用LL库,所以不必深入学习LL库。
可以将以下初始化函数中的PPP替换成GPIO,反初始化函数很少用到,主要用于失能时钟、恢复默认配置等。
初始化/反初始化函数:HAL_PPP_Init(), HAL_PPP_DeInit()
外设读写函数:HAL_PPP_Read(),HAL_PPP_Write(),HAL_PPP_Transmit(), HAL_PPP_Receive()
控制函数:HAL_PPP_Set (),HAL_PPP_Get ()
状态和错误:HAL_PPP_GetState (), HAL_PPP_GetError ()
此类回调函数通常被_weak修饰(弱函数),允许用户重新定义该函数。
CMSIS核心层文件就是将【CMSIS】文件夹中的【Device】和【Include】文件夹中裁剪后剩的11个文件,这11个文件对于新建MDK工程是必须包含的;设备驱动层就是HAL库及LL库的源码,也就是【STM32F1xx_HAL_Driver】文件夹中的源码;用户程序文件,正点原子例程都将main.h、stm32f1xx_hal_msp.c删除掉了,放到了例如初始化UART就将UART的在stm32f1xx_hal_msp.c中的回调函数放到usart.c中,stm32f1xx_it.c和stm32f1xx_it.h里面本身就定义了一些中断,所以选择保留,如果写串口中断,推荐写到usart.c中。。
如果是F1系列那么就修改stm32f1xx_hal_conf.h。获取此文件可以从STM32官方例程中获取,也可以从HAL库驱动源码中获取,只是名称不同,复制时只需要将template删除即可。
STM32HAL库裁剪就是将stm32f1xx_hal_conf.h中用不到的宏定义注释掉,因为在HAL库驱动源码中都是用的ifdef
判断宏是否被定义。以GPIO为例如下所示:
通过对工程中用到什么外设就加什么驱动文件,也是起到裁剪作用。如下所示,是部分HAL库驱动文件:
以下代码是设置高速外部晶振,宏USE_STM3210C_EVAL
判断是否使用ST官方开发板,默认为25MHz,否则为8MHz。要根据开发板实际情况进行设置。如果要省事可以简化以下代码为:#define HSE_VALUE 8000000U
#if !defined (HSE_VALUE)
#if defined(USE_STM3210C_EVAL)
#define HSE_VALUE 25000000U /*!< Value of the External oscillator in Hz */
#else
#define HSE_VALUE 8000000U /*!< Value of the External oscillator in Hz */
#endif
#endif /* HSE_VALUE */
高速内部晶振和低速内部晶振可以根据芯片手册进行设置:
#if !defined (HSI_VALUE)
#define HSI_VALUE 8000000U
#endif /* HSI_VALUE */
#if !defined (LSI_VALUE)
#define LSI_VALUE 40000U
#endif /* LSI_VALUE */
#if !defined (LSE_VALUE)
#define LSE_VALUE 32768U
#endif /* LSE_VALUE */
使用HAL库最关心的就说HAL库初始化函数HAL_Init()
,不同系列可能有些小区别,但是核心代码如下:
HAL_StatusTypeDef HAL_Init(void)
{
__HAL_FLASH_PREFETCH_BUFFER_ENABLE(); /* 使能FLASH预取缓冲 */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); /* 配置中断优先级分组 */
/* 使用滴答定时器作为时钟基准,配置 1ms 滴答(重置后默认的时钟源为 HSI) */
HAL_InitTick(TICK_INT_PRIORITY);
HAL_MspInit(); /* 初始化其它底层硬件(如果必要) */
return HAL_OK; /* 返回函数状态 */
}
第一段程序作用是使能FLASH预取缓冲,STM32是32位的,每次读取的指令也是32位的,FLASH存在预取缓冲区,预取缓冲区由2个64位组成,CPU每次读取指令,将指令放在预取缓冲区,就可以起到程序加快运行的效果。
__HAL_FLASH_PREFETCH_BUFFER_ENABLE(); /* 使能FLASH预取缓冲 */
第二段程序是设置中断优先级分组,默认情况下设置位为4,正点原子默认设置成2。
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); /* 配置中断优先级分组 */
第三段是使能系统滴答定时器,并且配置为1ms。正点原子实际配置滴答定时器在delay.c中的delay_Init()
函数中。
/* 使用滴答定时器作为时钟基准,配置 1ms 滴答(重置后默认的时钟源为 HSI) */
HAL_InitTick(TICK_INT_PRIORITY);
HAL_MspInit()
为空函数,不用管它。
HAL_MspInit(); /* 初始化其它底层硬件(如果必要) */
1,使用HAL库出现问题,还是得通过参考手册检查是否硬件操作是否有问题;
2,尽量不通过修改库源码实现功能,这样不方便库更新;
3,HAL库可能会存在错误,要有质疑精神;
4,有些HAL库API函数执行效率偏低,我们可能会直接通过操作寄存器的方式代替。