一、软件安装
二、新建工程
2.1 时钟配置
2.2 外设配置
2.3 工程配置
2.4 生成工程
2.5 经验总结
三、HAL库相关
3.1 HAL库文件
3.2 HAL API
3.3 外设句柄
3.4 三种编程方式-轮询、中断、DMA
3.5 三大回调函数
3.6 HAL库移植使用
基本步骤
资料整理自网络资料
针对STM32CubeMX,有两种开发方式
方式1:参考STM32CubeMX初始化代码,自建工程框架
方式2:使用STM32CubeMX配置片上资源,自动生成工程框架
作为开发者,个人使用方式1,参考STM32CubeMX初始化代码,自建工程框架
教程参考原子、安富莱、野火等开源教程
软件版本:STM32CubeMX V5
硬件平台:STM32H750
一、软件安装
JRE、STM32CubeMX、HAL库
二、新建工程
2.1 时钟配置
2.2 外设配置
2.3 工程配置
2.4 生成工程
2.5 经验总结
1,自己代码写在指定位置(BEGIN---END),否则重新生成工程代码会被删除
2,工程目录最好不包含中文路径
三、HAL库相关
1.标准外设库:Standard Peripheral Libraries,简写SPL.
2.Cube硬件抽象层库:STM32Cube Hardware Abstraction Layer,简写Cube HAL.
3.Cube底层库:STM32Cube Low-Layer,简写Cube LL.
STM32 HAL固件库是Hardware Abstraction Layer的缩写,中文名称是:硬件抽象层。HAL库是ST公司为STM32的MCU最新推出的抽象层嵌入式软件,为更方便的实现跨STM32产品的最大可移植性
3.1 HAL库文件
官方给出的HAL库的包含结构:
stm32f2xx.h:主要包含STM32同系列芯片的不同具体型号的定义,是否使用HAL库等的定义
stm32f2xx_hal.h:stm32f2xx_hal.c/h 主要实现HAL库的初始化、系统滴答相关函数、及CPU的调试模式配置
stm32f2xx_hal_conf.h :该文件是一个用户级别的配置文件,用来实现对HAL库的裁剪,其位于用户文件目录,不要放在库目录中
HAL库文件名均以stm32f2xx_hal开头,后面加上_外设或者模块名(如:stm32f2xx_hal_adc.c)
库文件:
stm32f2xx_hal_ppp.c/.h // 主要的外设或者模块的驱动源文件,包含了该外设的通用API
stm32f2xx_hal_ppp_ex.c/.h // 外围设备或模块驱动程序的扩展文件。这组文件中包含特定型号或者系列的芯片的特殊API。以及如果该特定的芯片内部有不同的实现方式,则该文件中的特殊API将覆盖_ppp中的通用API。
stm32f2xx_hal.c/.h // 此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的时间延迟等相关的API
其他库文件
用户级别文件:
stm32f2xx_hal_msp_template.c // 只有.c没有.h。它包含用户应用程序中使用的外设的MSP初始化和反初始化(主程序和回调函数)。使用者复制到自己目录下使用模板。
stm32f2xx_hal_conf_template.h // 用户级别的库配置文件模板。使用者复制到自己目录下使用
system_stm32f2xx.c // 此文件主要包含SystemInit()函数,该函数在刚复位及跳到main之前的启动过程中被调用。 **它不在启动时配置系统时钟(与标准库相反)**。 时钟的配置在用户文件中使用HAL API来完成。
startup_stm32f2xx.s // 芯片启动文件,主要包含堆栈定义,终端向量表等
stm32f2xx_it.c/.h // 中断处理函数的相关实现
3.2 HAL API
根据HAL库的命名规则,其API可以分为以下三大类:
- 初始化/反初始化函数: HAL_PPP_Init(), HAL_PPP_DeInit()
- IO 操作函数: HAL_PPP_Read(), HAL_PPP_Write(),HAL_PPP_Transmit(), HAL_PPP_Receive()
- 控制函数: HAL_PPP_Set (), HAL_PPP_Get ().
状态和错误: HAL_PPP_GetState (), HAL_PPP_GetError ().
HAL库最大的特点就是对底层进行了抽象。在此结构下,用户代码的处理主要分为三部分:
处理外设句柄(实现用户功能)
处理MSP
处理各种回调函数
3.3 外设句柄
用户代码的第一大部分:对于外设句柄的处理
HAL库在结构上,对每个外设抽象成了一个称为ppp_HandleTypeDef的结构体,其中ppp就是每个外设的名字。*所有的函数都是工作在ppp_HandleTypeDef指针之下。
1. 多实例支持:每个外设/模块实例都有自己的句柄。 因此,实例资源是独立的
2. 外围进程相互通信:该句柄用于管理进程例程之间的共享数据资源。
下面,以ADC为例
/**
* @brief ADC handle Structure definition
*/
typedef struct
{
ADC_TypeDef *Instance; /*!< Register base address */
ADC_InitTypeDef Init; /*!< ADC required parameters */
__IO uint32_t NbrOfCurrentConversionRank; /*!< ADC number of current conversion rank */
DMA_HandleTypeDef *DMA_Handle; /*!< Pointer DMA Handler */
HAL_LockTypeDef Lock; /*!< ADC locking object */
__IO uint32_t State; /*!< ADC communication state */
__IO uint32_t ErrorCode; /*!< ADC Error code */
}ADC_HandleTypeDef;
从上面的定义可以看出,ADC_HandleTypeDef中包含了ADC可能出现的所有定义
当然,对于那些共享型外设或者说系统外设来说,他们不需要进行以上这样的抽象,这些部分与原来的标准外设库函数基本一样。
例如以下外设:
- GPIO
- SYSTICK
- NVIC
- RCC
- FLASH
以GPIO为例,对于HAL_GPIO_Init() 函数,其只需要GPIO 地址以及其初始化参数即可
3.4 三种编程方式-轮询、中断、DMA
HAL库对所有的函数模型也进行了统一。在HAL库中,支持三种编程模式:轮询模式、中断模式、DMA模式(如果外设支持)。其分别对应如下三种类型的函数(以ADC为例):
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc);
其中,带_IT的表示工作在中断模式下;带_DMA的工作在DMA模式下(注意:DMA模式下也是开中断的);什么都没带的就是轮询模式(没有开启中断的)
此外,新的HAL库架构下统一采用宏的形式对各种中断等进行配置(原来标准外设库一般都是各种函数)。针对每种外设主要由以下宏:
HAL_PPP_ENABLE_IT(__HANDLE, INTERRUPT): 使能一个指定的外设中断
HAL_PPP_DISABLE_IT(__HANDLE, INTERRUPT):失能一个指定的外设中断
HAL_PPP_GET_IT (__HANDLE, __ INTERRUPT ):获得一个指定的外设中断状态
__HAL_PPP_CLEAR_IT (__HANDLE, __ INTERRUPT ):清除一个指定的外设的中断状态
__HAL_PPP_GET_FLAG (__HANDLE, FLAG):获取一个指定的外设的标志状态
HAL_PPP_CLEAR_FLAG (__HANDLE, FLAG):清除一个指定的外设的标志状态
HAL_PPP_ENABLE(__HANDLE) :使能外设
HAL_PPP_DISABLE(__HANDLE) :失能外设
HAL_PPP_XXXX (__HANDLE, PARAM) :指定外设的宏定义
HAL_PPP_GET_ IT_SOURCE (__HANDLE, __ INTERRUPT __):检查中断源
3.5 三大回调函数
在HAL库的源码中,到处可见一些以__weak开头的函数
所有带有__weak关键字的函数表示,就可以由用户自己来实现。如果出现了同名函数,且不带__weak关键字,那么连接器就会采用外部实现的同名函数。
通常来说,HAL库负责整个处理和MCU外设的处理逻辑,并将必要部分以回调函数的形式给出到用户,用户只需要在对应的回调函数中做修改即可
- 外设系统级初始化/解除初始化回调函数(用户代码的第二大部分:对于MSP的处理):HAL_PPP_MspInit()和 HAL_PPP_MspDeInit** 例如:__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)。在HAL_PPP_Init() 函数中被调用,用来初始化底层相关的设备(GPIOs, clock, DMA, interrupt)
- 处理完成回调函数:HAL_PPP_ProcessCpltCallback(Process指具体某种处理,如UART的Tx),例如:__weak void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef hspi)。当外设或者DMA工作完成后时,触发中断,该回调函数会在外设中断处理函数或者DMA的中断处理函数中被调用
- 错误处理回调函数:HAL_PPP_ErrorCallback例如:__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef hspi)*。当外设或者DMA出现错误时,触发终端,该回调函数会在外设中断处理函数或者DMA的中断处理函数中被调用
绝大多数用户代码均在以上三大回调函数中实现。
HAL库结构中,在每次初始化前(尤其是在多次调用初始化前),先调用对应的反初始化(DeInit)函数是非常有必要的。某些外设多次初始化时不调用返回会导致初始化失败。
完成回调函数有多中,例如串口的完成回调函数有HAL_UART_TxCpltCallback 和 HAL_UART_TxHalfCpltCallback等
3.6 HAL库移植使用
基本步骤
- 复制stm32f2xx_hal_msp_template.c,参照该模板,依次实现用到的外设的HAL_PPP_MspInit()和 HAL_PPP_MspDeInit。
- 复制stm32f2xx_hal_conf_template.h,用户可以在此文件中自由裁剪,配置HAL库。
在使用HAL库时,必须先调用函数:HAL_StatusTypeDef HAL_Init(void)(该函数在stm32f2xx_hal.c中定义,也就意味着第一点中,必须首先实现HAL_MspInit(void)和HAL_MspDeInit(void))
HAL库与STD库不同,HAL库使用RCC中的函数来配置系统时钟,用户需要单独写时钟配置函数(STD库默认在system_stm32f2xx.c中)
关于中断,HAL提供了中断处理函数,只需要调用HAL提供的中断处理函数。用户自己的代码,不建议先写到中断中,而应该写到HAL提供的回调函数中。
对于每一个外设,HAL都提供了回调函数,回调函数用来实现用户自己的代码。整个调用结构由HAL库自己完成。
例如:Uart中,HAL提供了void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);函数,用户只需要触发中断后,用户只需要调用该函数即可,同时,自己的代码写在对应的回调函数中即可!如下:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);
使用了哪种就用哪个回调函数即可!
基本结构
综上所述,使用HAL库编写程序(针对某个外设)的基本结构(以串口为例)如下:
1, 配置外设句柄 例如,建立UartConfig.c,在其中定义串口句柄 UART_HandleTypeDef huart;,
2, 接着使用初始化句柄(HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef huart))
3, 编写Msp 例如,建立UartMsp.c,在其中实现
void HAL_UART_MspInit(UART_HandleTypeDef huart) 和
void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
4,实现对应的回调函数 例如,建立UartCallBack.c,在其中实现上文所说明的三大回调函数中的完成回调函数和错误回调函数