STM32F0_HAL库驱动描述——HAL驱动程序概述

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

STM32F0_HAL库驱动描述——HAL驱动程序概述
HAL库⽂件结构:
HAL驱动⽂件:
外设驱动API⽂件和头⽂件:包含了常见主要的通⽤API,其中ppp表⽰外设名称,如adc、usart、gpio、irda等;
stm32f0xx_hal_ppp.c
stm32f0xx_hal_ppp.h
外设驱动扩展API⽂件和头⽂件:包含指定的API和内部不同实现以覆盖通⽤API的新定义API接⼝函数,其中ppp表⽰外设名称;
stm32xx_hal_ppp_ex.c
stm32xx_hal_ppp_ex.h
初始化HAL库⽂件、包含DBGMCU(调试接⼝)、Remap(重映射)和SysTick的TimeDelay;
stm32xx_hal.c
stm32xx_hal.h
⾃带的相应库函数例⼦:包含相应外设的初始化和去初始化;
stm32xx_hal_msp_template.c
stm32xx_hal_conf_template.h
通⽤HAL资源定义:包含通⽤定义声明、枚举、结构和宏定义;
stm32xx_hal_def.h
⽤户应⽤⽂件:
⽤于在main函数前初始化系统时钟,包含SystermInit()函数,但不会在StartUp时配置相同时钟(与标准库不同的地⽅);
system_stm32f0xx.c
包含reset handler处理函数、中断向量、并允许调整堆栈⼤⼩:
startup_stm32f0xx.s
EWARM⼯具链⽂件,⽤以调整堆栈⼤⼩以适应应⽤程序的要求;
stm32f0xx_flash.icf
⽤户⾃定义外设初始化⽂件:包括初始化和去初始化,包含主例程和回调;
stm32f0xx_hal_msp.c
⽤户⾃定义驱动⽂件:允许⽤户⾃定义HAL驱动,可以使⽤默认配置⽽⽆需修改;
stm32f0xx_hal_conf.h
异常处理和外设中断服务⽂件:会在SysTick_Handler()函数中反复调⽤HAL_IncTick()以实现延时;
stm32f0xx_it.c/.h
主函数:调⽤HAL_Init()函数、在Debug模式下使⽤的assert_failed()时间检测函数、系统时钟配置函数、外设HAL初始化和应⽤代码;
main.c/.h
通过STM32CubeMX配置的⼯程,已经默认做好如下的配置:
HAL初始化完成;
SysTick中断服务实现HAL_Delay()延时功能;
系统时钟配置为器件最⼤频率的时钟;
HAL数据结构:
每⼀个HAL驱动都遵循以下数据结构:
外设句柄结构Peripheral handle structures
初始化和配置结构Initialization and configuration structures
特殊的过程结构Specific process structures
Peripheral handle structures:
PPP_HandlerTypeDef *handler是HAL驱动程序中实现的主要结构;它处理外设模块配置、注册、嵌⼊外围设备所需要的所有结构和变量;
该句柄结构主要⽤于:
可以初始化多个实例(可以使⽤相同的结构定义和配置多个相同外设,如USART1、USART2、USART3),使每个初始化的外设都有相同的完整的结构;
外围进程互通,管理进程之间的共享数据资源,如全局变量、DMA句柄结构、状态机;
存储,⽤于管理对应的初始化HAL外设驱动程序中的全局变量;
外设句柄结构举例:
typedef struct
{
USART_TypeDef *Instance; /* USART registers base address */
USART_InitTypeDef Init; /* Usart communication parameters */
uint8_t *pTxBuffPtr;/* Pointer to Usart Tx transfer Buffer */
uint16_t TxXferSize; /* Usart Tx Transfer size */
__IO uint16_t TxXferCount;/* Usart Tx Transfer Counter */
uint8_t *pRxBuffPtr;/* Pointer to Usart Rx transfer Buffer */
uint16_t RxXferSize; /* Usart Rx Transfer size */
__IO uint16_t RxXferCount; /* Usart Rx Transfer Counter */
DMA_HandleTypeDef *hdmatx; /* Usart Tx DMA Handle parameters */
DMA_HandleTypeDef *hdmarx; /* Usart Rx DMA Handle parameters */
HAL_LockTypeDef Lock; /* Locking object */
__IO HAL_USART_StateTypeDef State; /* Usart communication state */
__IO HAL_USART_ErrorTypeDef ErrorCode;/* USART Error code */
}USART_HandleTypeDef;
多实例特性意味着应⽤程序的所有API都是可重⼊的,因此会避免使⽤全局变量,当⼦例程递归调⽤时,如果⼦例程依赖全局变量保持不变但是该变量在循环调⽤时发⽣改变,则可能会造成⼦例程⽆法重⼊;
因此要遵守:
可重⼊代码区域不应包含任何静态或全局的⾮常量数据,可重⼊函数则可以使⽤全局数据;例如在整个中断服务函数中使⽤硬件状态全局变量,会造成硬件状态的易失性;使⽤静态全局变量期间不应发⽣中断或其他的信号响应,并因尽量只应⽤在对其本⾝读-修改-写的过程中;
可重⼊代码不会修改⾃⼰的代码;
当外设使⽤DMA全双⼯同时管理多个外设时,每个进程的DMA接⼝句柄都会更新对应的外设PPP_HandleTypeDef;即每个可以使⽤DMA 的外设句柄handler中都包含了DMA_HandleTypeDef;
对于共享(所有外设和系统配置都可以使⽤的)和系统外围设备,没有句柄或实例对象;包括GPIO、SYSTICK、NVIC、PWR、RCC、FLASH;
外设句柄结构的定义:在外设驱动头⽂件stm32f0xx_hal_ppp.h中定义;其名称通常都是PPP_HandleTypeDef;
这些结构通常都是⽤来初始化⼦模块和⼦实例;
特定的进程结构:具体的流程结构使⽤特定的流程(通⽤API),通常也是定义在外设驱动头⽂件中;
API分类:
通⽤API,存在于所有通⽤的HAL驱动程序中;
HAL_StatusTypeDef HAL_ADC_Init(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_DeInit(ADC_HandleTypeDef *hadc);
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);
void HAL_ADC_IRQHandler(ADC_HandleTypeDef* hadc);
扩展API,存在于扩展外设库⽂件中,有两类;
第⼀种是同于特定系列的扩展API;
HAL_StatusTypeDef HAL_ADCEx_Calibration_Start(ADC_HandleTypeDef* hadc, uint32_t SingleDiff);
uint32_t HAL_ADCEx_Calibration_GetValue(ADC_HandleTypeDef* hadc, uint32_t SingleDiff);
第⼆种是⽤于特定型号的API;
#if defined(STM32F042x6) || defined(STM32F048xx) || defined(STM32F072xB) ||
defined(STM32F078xx) || \
defined(STM32F091xC) || defined(STM32F098xx)
#endif /* STM32F042x6 || STM32F048xx || STM32F072xB || STM32F078xx || */
/* STM32F091xC || STM32F098xx */
HAL驱动规则:
HAL_API命名规则:下⾯这个表可以仔细看看;
其中PPP是外设模式,⽽不是指外设本⾝;
MODE指的是过程模式,是轮循、中断或DMA模式;
FEATURE指的是实现功能,如Start、Stop;
HAL通⽤命名规则:
以下外围设备其初始化不需要提供句柄handler和实例对象instance object;GPIO、SYSTICK、NVIC、RCC、FLASH 处理中断和特定时钟配置的宏在每个外设/模块驱动头⽂件中定义;
NVIC和SYSTICK是ARMCortex的两个核⼼功能,与这些功能相关的API位于stm32f0xx_hal_cortex.c中;
从寄存器中读取状态位或标志时,它由位移值构成,且通常返回的宽度为32位;
在初始化HAL_PPP_Init() 的API中,Init函数在修改句柄字段之前会检查句柄PPP_HandleTypeDef内容是否为空;HAL_PPP_Init(PPP_HandleTypeDef)
if(hppp == NULL)
{
return HAL_ERROR;
}
宏定义分为两类:
条件宏定义;
#define ABS(x) (((x) > 0) ? (x) : -(x))
伪代码宏(多指令宏);
#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD_, __DMA_HANDLE_) \
do{ \
(__HANDLE__)->__PPP_DMA_FIELD_ = &(__DMA_HANDLE_); \
(__DMA_HANDLE_).Parent = (__HANDLE__); \
} while(0)
HAL中断处理程序和回调函数
除了API,HAL外设驱动还包含:
HAL_PPP_IRQHandler()外设中断处理函数;
⽤户定义回调函数,系统默认的回调函数定义为weak属性,⼀旦⽤户⾃⼰定义了回调函数会覆盖系统默认的回调函数;
有三种类型的回调函数:
外围系统初始化/去初始化回调:HAL_PPP_MspInit()、HAL_PPP_MspDeInit();
处理完整进程的回调函数:HAL_PPP_ProcessCpltCallback;
错误处理回调函数:HAL_PPP_ErrorCallback;
HAL通⽤APIs:
通⽤的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扩展APIs:
扩展API通常是特定系列或同⼀系列中特定功能或覆盖已修改的API,扩展功能通常由stm32f0xx_hal_ppp_ex.c/h⽂件构成;
HAL程序由五种不同的⽅式处理特定的IP功能:
添加部分特定的功能:将新的API添加到stm32f0xx_hal_ppp_ex.c扩展⽂件中,并命名为HAL_PPPEx_Function();
添加⼀系列的功能:操作同添加部分特定功能相似;
添加新的外围设备:在stm32f0xx_hal_newppp.c中添加新的可⽤外围设备,同时在stm32f0xx_hal_conf.h中包含这个新外围设备的宏;
#define HAL_NEWPPP_MODULE_ENABLED
更新已有的通⽤API:想要覆盖⼀个在stm32f0xx_hal_ppp.c中已经存在的API函数,则在stm32f0xx_hal_ppp_ex.c扩展⽂件中使⽤相同名称的定义,因为通⽤API定义位weak,所以编译器将通过新定义的函数覆盖原始例程;
升级已存在的数据结构:通过采⽤不同的器件宏定义来重新定义数据结构PPP_InitTypeDef;
#if defined (STM32F072xB)
typedef struct
{
(…)
}PPP_InitTypeDef;
#endif /* STM32F072xB */
⽂件包含模型:
在这其中stm32f0xx_hal.h是连接整个HAL库源和⽤户源的唯⼀头⽂件;
其中⽂件包含关系如下图所⽰:其基本的包含关系是
HAL库⽂件或main⽂件—stm32f0xx_hal.h—stm32f0xx_hal_conf.h—HAL库头⽂件
由于相应的外设功能需要在外设模块选择中添加相应的功能宏,在配置⽂件stm32f0xx_hal_conf.h中可以找到;
HAL共有配置:
HALStatus:除了布尔函数和IRQ处理程序,⼏乎所有的HAL_API都使⽤HAL状态,它返回当前API操作的状态;
Typedef enum
{
HAL_OK = 0x00,
HAL_ERROR = 0x01,
HAL_BUSY = 0x02,
HAL_TIMEOUT = 0x03
} HAL_StatusTypeDef;
HALLocked:锁定状态防⽌意外的共享数据修改和读写;
typedef enum
{
HAL_UNLOCKED = 0x00, /*!<Resources unlocked */
HAL_LOCKED = 0x01/*!< Resources locked */
} HAL_LockTypeDef;
除了共有的资源,stm32f0xx_hal_def.h⽂件还调⽤CMSIS库中的stm32f0xx.h⽂件来获取所有外设的数据结构和地址映射:外设寄存器和位定义的声明;
⽤于访问外设寄存器硬件的宏(读写寄存器.etc);
CommonMarco:
NULL和HAL_MAX_DELAY宏定义;
#define HAL_MAX_DELAY 0xFFFFFFFF
将PPP外设链接到DMA结构指针的宏:__HAL_LINKDMA();
#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD_, __DMA_HANDLE_) \
do{ \
(__HANDLE__)->__PPP_DMA_FIELD_ = &(__DMA_HANDLE_); \
(__DMA_HANDLE_).Parent = (__HANDLE__); \
} while(0)
HAL配置:
配置⽂件stm32f0xx_hal_conf.h允许⽤户⾃定义配置参数和定义;
⽰例配置⽂件stm32f0xx_hal_conf_template.h中开启了所有的HAL库定义,并将时钟配置为最⼤时钟数值;
HAL外围设备处理:
时钟Clock:
两个主要的功能配置时钟:
HAL_RCC_OscConfig (RCC_OscInitTypeDef *RCC_OscInitStruct)
⽤来配置和使能时钟源,如HSE、HSI、LSE、LSI、PLL;
HAL_RCC_ClockConfig (RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)
⽤来选择SystemClock系统时钟、配置AHB和APB时钟分频、配置Flash等待状态的数量、HCLK时钟更改时更新SysTick配置;
某些外设时钟不是从SystemClock系统时钟中派⽣的(USB、RTC),这种情况下,时钟配置由stm32f0xx_hal_rcc_ex.c中定义的扩展API 执⾏:
HAL_RCCEx_PeriphCLKConfig(RCC_PeriphCLKInitTypeDef *PeriphClkInit)
提供额外的RCC_HAL驱动程序功能:
HAL_RCC_DeInit()时钟去启动功能,将时钟配置返回到复位状态;
获取时钟功能,并允许检索各种时钟配置(system clock、HCLK、PCLKn);
MCO和CSS配置功能;
在stm32f0xx_hal_rcc.h和stm32f0xx_hal_rcc_ex.h中定义了⼀组宏;它们允许在RCC块寄存器上执⾏基本操作,例如外设时钟门控/复位控制:
__PPP_CLK_ENABLE/__PPP_CLK_DISABLE to enable/disable the peripheral clock
__PPP_FORCE_RESET/__PPP_RELEASE_RESET to force/release peripheral reset
__PPP_CLK_SLEEP_ENABLE/__PPP_CLK_SLEEP_DISABLE to enable/disable the
peripheral clock during low power (Sleep) mode.
GPIOs:
GPIO HAL API主要包含:
HAL_GPIO_Init()/HAL_GPIO_DeInit()
HAL_GPIO_ReadPin()/HAL_GPIO_WritePin()
HAL_GPIO_TogglePin ()
除了标准GPIO模式(输⼊、输出、模拟)外,引脚模式还可以配置成带有中断IT和事件⽣成EVENT的EXTI模式;
此模式需要从stm32f0xx_it.c中调⽤HAL_GPIO_EXTI_IRQHandler()并实现回调函数HAL_GPIO_EXTI_Callback();
下表是介绍了GPIO_InitTypeDef结构可以配置的参数列表:
可以参考GPIO的⼀些基础配置:
配置GPIO为输出PP模式,控制LED灯;
GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
配置GPIO作为下降沿触发外部中断模式;
GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
配置复⽤模式为串⼝USART1模式;
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
Cortex NVIC和SysTick时钟
HAL库在stm32f0xx_hal_cortex.c中给了处理NVIC和SysTick的APIs,这些包含的APIs有:
HAL_NVIC_SetPriority()
HAL_NVIC_EnableIRQ()/HAL_NVIC_DisableIRQ()
HAL_NVIC_SystemReset()
HAL_SYSTICK_IRQHandler()
HAL_NVIC_GetPendingIRQ() / HAL_NVIC_SetPendingIRQ ()/ HAL_NVIC_ClearPendingIRQ()
HAL_SYSTICK_Config()
HAL_SYSTICK_CLKSourceConfig()
HAL_SYSTICK_Callback()
PWR:
PWR HAL驱动程序处理电源管理,所有的STM32系列共享的功能如下:
PVD配置,启动/禁⽤和中断处理:
HAL_PWR_PVDConfig()
HAL_PWR_EnablePVD() / HAL_PWR_DisablePVD()
HAL_PWR_PVD_IRQHandler()
HAL_PWR_PVDCallback()
Wakeup唤醒引脚配置:
HAL_PWR_EnableWakeUpPin() / HAL_PWR_DisableWakeUpPin()
低功耗模式配置:
HAL_PWR_EnterSLEEPMode()
HAL_PWR_EnterSTOPMode()
HAL_PWR_EnterSTANDBYMode()
备份的域配置:
HAL_PWR_EnableBkUpAccess()/ HAL_PWR_DisableBkUpAccess()
EXTI:
EXTI不被视为独⽴外围设备,⽽是其他外围设备使⽤的服务,因此没有EXTI的API;
但每个外围HAL驱动程序实现关联的EXTI配置,EXTI功能在其头⽂件中表现为宏;
连接到GPIO的前16条EXTI线在GPIO驱动中进⾏管理,GPIO_InitTypeDef结构允许将IO配置为外部中断IT或外部事件EVENT;其内部连接到PVD、RTC、USB和COMP的EXTI线路通过库定义好的宏配置外设HAL驱动;
EXTI中断API:将EXTI中断线连接到内部外设;
PPP_EXTI_LINE_FUNCTION
外设中断使能:
__HAL_PPP_EXTI_ENABLE_IT
__HAL_PPP_EXTI_DISABLE_IT
获取EXTI中断状态:
__HAL_PPP_EXTI_GET_FLAG
__HAL_PPP_EXTI_CLEAR_FLAG
⽣成EXTI中断事件:
__HAL_PPP_EXTI_GENERATE_SWIT
开启EXTI中断线事件:
__HAL_PPP_EXTI_ENABLE_EVENT
__HAL_PPP_EXTI_DISABLE_EVENT
如果选择了EXTI模式,则⽤户必须从stm32f0xx_it.c⽂件中调⽤HAL_PPP_FUNCTION_IRQHandler()来实现
HAL_PPP_FUNCTIONCallback()回调函数;
DMA:
DMA HAL驱动程序允许启⽤和配置外设连接到DMA通道(内部FLASH和SRAM除外),对于给定的HAL_DMA_Init()则可以配置以下参数:
传输⽅向
源和⽬标的数据格式
循环、正常或外设流模式
通道优先级
源和⽬标的递增模式
FIFO模式或其阈值
源和⽬标的突发模式
有两种可以定义的模式:
轮询模式
使⽤HAL_DMA_Start()来配置源和⽬标地址以及要传输的数据长度,来启动DMA;
使⽤HAL_DMA_PollForTransfer()来获取当前传输的结果,可以根据这个判断来配置应⽤程序的超时设置;
中断模式
使⽤HAL_NVIC_SetPriority()来配置DMA的中断优先级
使⽤HAL_NVIC_EnableIRQ()来使能DMAIRQ处理函数
使⽤HAL_DMA_Start_IT()来配置DMA的源和⽬标地址以及要传输的数据长度来使能DMA传输;
使⽤HAL_DMA_IRQHandler()⼦程序来在DMA_IRQHandler()中调⽤;
当数据传输完成时,执⾏HAL_DMA_IRQHandler()函数并且可以通过定制XferCpltCallback和XferErrorCallback来调⽤⽤户函数;获取状态来确保进⾏有效的DMA管理:
HAL_DMA_GetState()获取DMA状态;
HAL_DMA_GetError()获取DMA错误标志;
HAL_DMA_Abort()终⽌当前操作;
最常⽤的DMA中断
DMA通道使能:
__HAL_DMA_ENABLE: enables the specified DMA Channels.
__HAL_DMA_DISABLE: disables the specified DMA Channels.
获取/清除DMA中断标志:
__HAL_DMA_GET_FLAG: gets the DMA Channels pending flags.
__HAL_DMA_CLEAR_FLAG: clears the DMA Channels pending flags.
DMA中断使能:
__HAL_DMA_ENABLE_IT: enables the specified DMA Channels interrupts.
__HAL_DMA_DISABLE_IT: disables the specified DMA Channels interrupts.
检查DMA中断源是否开启:
__HAL_DMA_GET_IT_SOURCE: checks whether the specified DMA channel interrupt has occurred or not.
在DMA模式下使⽤外设,应在HAL_PPP_MspInit()回调中完成DMA的初始化,同时将DMA句柄与PPP句柄相关联;
只有在内存到内存传输的情况下,⽤户应⽤程序才需要初始化DMA通道回调;但是,当使⽤外设到内存的传输时,这些回调会通过调⽤使⽤DMA的进程API函数⾃动初始化;
如何使⽤HAL驱动:
HAL使⽤模型:
HAL初始化:
HAL全局初始化:除了外设初始化和去初始化,还在stm32f0xx_hal.c提供了以下的API来初始化HAL内核:
HAL_Init()
初始化数据和指令缓存、预取队列集;
设置Systick时钟每1s发⽣⼀次中断(基于HSI时钟);
调⽤HAL_MspInit()⽤户回调函数(该函数也被设定为弱空函数)执⾏系统级初始化(时钟、GPIO、DMA、中断);
HAL_DeInit()
复位所有的外设;
调⽤HAL_MspDeInit()⽤户回调函数;
HAL_GetTick()
⽤来获取SysTick的计数值来处理外设驱动的超时;
HAL_Delay()
使⽤SysTick来定时1ms;
使⽤HAL_Delay()必须要注意,此函数基于SysTickISR中递增的变量提供准确的延时,如果外设调⽤HAL_Delay()来实现延时,则SysTick 中断必须具有⽐外设中断更⾼的优先级,否则将阻⽌调⽤ISR;
系统时钟初始化:
系统时钟的配置可以在main代码前完成,也可以⽤户⾃⼰在代码中定义;
static void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
/* Enable HSE Oscillator and Activate PLL with HSE as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
{
Error_Handler();
}
/* Select PLL as system clock source and configure the HCLK, PCLK1 clocks dividers
*/
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK |
RCC_CLOCKTYPE_PCLK1);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1)!= HAL_OK)
{
Error_Handler();
}
}
HAL MSP初始化过程
通过HAL_PPP_Init()初始化外设,同时也会通过HAL_PPP_MspInit()初始化硬件资源;
MSP回调执⾏与各个外设不同的附加硬件资源:RCC、GPIO、NVIC、DMA;
并且所有带句柄Handler的HAL驱动程序都包含两个函数:
void __weak HAL_PPP_MspInit(PPP_HandleTypeDef *hppp)
void __weak HAL_PPP_MspDeInit(PPP_HandleTypeDef *hppp)
MSP回调在每个外设驱动程序中被声明成空函数,⽤户可以通过使⽤它来设置低级初始化或省略使⽤⾃⼰的初始化例程;
HAL MSP回调函数在stm32f0xx_hal_msp.c中实现,stm32f0xx_hal_msp_template.c⽂件在HAL⽂件夹中,这个⽂件由STM32CubeMX⼯具⽣成并进⼀步修改;
在⽂件stm32f0xx_hal_msp.c中包含以下函数:前两个是全局初始化、后两个是外设初始化;
当⼀个或多个外设需要在运⾏时去初始化DeInit并且给定外设的低级资源需要被另⼀个外设释放和使⽤时,在HAL_PPP_MspDeInit()和HAL_PPP_MspInit()中定义,但是HAL_MspInit()和HAL_MspDeInit()可以保持不变;
HAL IO操作
具有内部数据处理(如发送、接收、写⼊和读取)的HAL外设功能通常都具有三种数据处理模式:
轮询模式
中断模式
DMA模式
轮询模式:当阻塞模式下的数据处理完成后,HAL返回进程状态;
当返回HAL_OK状态时,认为操作已经完成,否则返回错误信息;也可以通过HAL_PPP_GetState()来获取更多的状态;
数据在循环内部处理,超时(ms毫秒)以防⽌进程挂起;
具体实现如下:
判断接收的数据是否有误;
判断数据读取是否超时;
所有的数据处理⽆误后返回完成标志位;
HAL_StatusTypeDef HAL_PPP_Transmit ( PPP_HandleTypeDef * phandle, uint8_t pData,
int16_tSize,uint32_tTimeout)
{
if((pData == NULL ) || (Size == 0))
{
return HAL_ERROR;
}
(…) while (data processing is running)
{
if( timeout reached )
{
return HAL_TIMEOUT;
}
}
(…)
return HAL_OK; }
中断模式:
HAL在开始数据处理并启⽤适当的中断来返回过程状态,操作的结果由声明为弱函数的回调指⽰,回调也可以由⽤户定义以实时通知过程完成,也可以使⽤HAL_PPP_GetState()获取进程状态;
在中断模式中有四个函数:
HAL_PPP_Process_IT():启动IT过程;
HAL_PPP_IRQHandler():全局的外设中断;
__weak HAL_PPP_ProcessCpltCallback ():⽤于在处理流程完成后的回调函数;
__weak HAL_PPP_ProcessErrorCallback():⽤于在出现错误信息后的回调函数;
要在中断模式下使⽤进程,需要在⽤户⽂件中调⽤HAL_PPP_Process_IT(),需要在stm32f0xx_it.c中调⽤HAL_PPP_IRQHandler()中断函数;
HAL_PPP_ProcessCpltCallback()初始被声明为弱函数,⽤户可以在应⽤层重新定义该函数;
具体应⽤举例:
main.c file:
UART_HandleTypeDef UartHandle;
int main(void)
{
/* Set User Parameters */
UartHandle.Init.BaudRate = 9600;
UartHandle.Init.WordLength = UART_DATABITS_8;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
UartHandle.Init.Instance = USART1;
HAL_UART_Init(&UartHandle);
HAL_UART_SendIT(&UartHandle, TxBuffer, sizeof(TxBuffer));
while (1);
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
}
stm32f0xx_it.cfile:
extern UART_HandleTypeDef UartHandle;
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&UartHandle);
}
DMA模式:
在DMA模式下,HAL功能在通过DMA开始数据处理之后以及启⽤适当的DMA中断后返回过程状态;操作的结束由声明为弱函数的回调指⽰,并且可以由⽤户定制以实时通知过程完成;⽤户还可以通过HAL_PPP_GetState()函数获取进程状态;
对于DMA模式,在驱动程序中声明了三个函数:
HAL_PPP_Process_DMA():开启DMA
HAL_PPP_DMA_IRQHandler():PPP外设中断函数定义
__weak HAL_PPP_ProcessCpltCallback():数据处理完成后调⽤的回调函数
__weak HAL_PPP_ErrorCpltCallback():发⽣错误了调⽤的回调函数
要使⽤DMA模式,需要在⽤户⽂件中调⽤HAL_PPP_Process_DMA(),并将HAL_PPP_DMA_IRQHandler()中断处理函数放
在stm32f0xx_it.c中;
其初始化在HAL_PPP_MspInit()回调函数中实现,⽤户还应将DMA句柄Handler与PPP外设句柄Handler相关联;
要在外设中添加DMA句柄Handler如下:
typedef struct
{
PPP_TypeDef *Instance; /* Register base address */
PPP_InitTypeDef Init; /* PPP communication parameters */
HAL_StateTypeDef State; /* PPP communication state */
(…)
DMA_HandleTypeDef *hdma; /* associated DMA handle */
} PPP_HandleTypeDef;
DMA初始化流程如下:(以USART为例)
int main(void)
{
/* Set User Parameters */
UartHandle.Init.BaudRate = 9600;
UartHandle.Init.WordLength = UART_DATABITS_8;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
UartHandle.Init.Instance = UART1;
HAL_UART_Init(&UartHandle);
(..)
}
void HAL_USART_MspInit (UART_HandleTypeDef * huart)
{
static DMA_HandleTypeDef hdma_tx;
static DMA_HandleTypeDef hdma_rx;
(…)
__HAL_LINKDMA(UartHandle, DMA_Handle_tx, hdma_tx);
__HAL_LINKDMA(UartHandle, DMA_Handle_rx, hdma_rx);
(…)
}
HAL_PPP_ProcessCpltCallback()函数在驱动程序中声明为弱函数,这意味着⽤户可以在应⽤程序代码中再次声明它,⽽不⽤修改驱动程序中的功能;
在main.c中:
UART_HandleTypeDef UartHandle;
int main(void)
{
/* Set User Paramaters */
UartHandle.Init.BaudRate = 9600;
UartHandle.Init.WordLength = UART_DATABITS_8;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX; UartHandle.Init.Instance = USART1;
HAL_UART_Init(&UartHandle);
HAL_UART_Send_DMA(&UartHandle, TxBuffer, sizeof(TxBuffer));
while (1);
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *phuart)
{
}
void HAL_UART_TxErrorCallback(UART_HandleTypeDef *phuart)
{
}
在stm32f0xx_it.c中:
extern UART_HandleTypeDef UartHandle;
void DMAx_IRQHandler(void)
{
HAL_DMA_IRQHandler(&UartHandle.DMA_Handle_tx);
}
HAL_USART_TxCpltCallback()和HAL_USART_ErrorCallback()应该通过使⽤以下语句在HAL_PPP_Process_DMA()函数中链接到DMA传输完成回调和DMA传输错误回调:
HAL_PPP_Process_DMA (PPP_HandleTypeDef *hppp, Params….)
{
(…)
hppp->DMA_Handle->XferCpltCallback = HAL_UART_TxCpltCallback ;
hppp->DMA_Handle->XferErrorCallback = HAL_UART_ErrorCallback ;
(…)
}
超时和错误管理:
超时管理:
超时通常⽤于以轮询模式运⾏的API;它定义了阻塞过程应该等待直到返回错误的延迟;下⾯提供了⼀个⽰例:
HAL_StatusTypeDef HAL_DMA_PollForTransfer(DMA_HandleTypeDef *hdma, uint32_t CompleteLevel, uint32_t Timeout)
超时定义的值⼤⼩:
PS:HAL_MAX_DELAY is defined in the stm32fxxx_hal_def.h as 0xFFFFFFFF.
在某些情况下,固定超时⽤于系统外围设备或内部HAL驱动程序进程,在这些情况下,超时具有相同的含义并以相同的⽅式使⽤;
获取当前计数值并赋予超时上限;
在处理程序完成后再次获取计数值,判断是否超时,返回超时状态;
#define LOCAL_PROCESS_TIMEOUT 100
HAL_StatusTypeDef HAL_PPP_Process(PPP_HandleTypeDef)
{
(…)
timeout = HAL_GetTick() + LOCAL_PROCESS_TIMEOUT;
(…)
while(ProcessOngoing)
{
(…)
if(HAL_GetTick() >= timeout)
{
/* Process unlocked */
__HAL_UNLOCK(hppp);
hppp->State= HAL_PPP_STATE_TIMEOUT;
return HAL_PPP_STATE_TIMEOUT;
}
}
(…)
}
以下⽰例显式轮询函数中如何使⽤超时:
HAL_PPP_StateTypeDef HAL_PPP_Poll (PPP_HandleTypeDef *hppp, uint32_t Timeout)
{
(…)
timeout = HAL_GetTick() + Timeout;
(…)
while(ProcessOngoing)
{
(…)
if(Timeout != HAL_MAX_DELAY)
{
if(HAL_GetTick() >= timeout)
{
/* Process unlocked */
__HAL_UNLOCK(hppp);
hppp->State= HAL_PPP_STATE_TIMEOUT;
return hppp->State;
}
}
(…)
}
错误管理:
通过检查以下的参数来确定错误;
有效参数:使⽤的参数要是有效的,并且已经定义的,否则系统很可能陷⼊未定义状态,在使⽤之前检查这些参数;
HAL_StatusTypeDef HAL_PPP_Process(PPP_HandleTypeDef* hppp, uint32_t *pdata, uint32
Size)
{
if ((pData == NULL ) || (Size == 0))
{
return HAL_ERROR;
}
}
有效句柄:PPP外围句柄是最重要的参数,因为它保留了PPP驱动程序的重要参数;始终在HAL_PPP_Init()函数的开头检查它;
HAL_StatusTypeDef HAL_PPP_Init(PPP_HandleTypeDef* hppp)
{
if (hppp == NULL) //the handle should be already allocated
{
return HAL_ERROR;
}
}
超时错误:发⽣超时错误时使⽤以下语句:while(正在进⾏);
{
timeout = HAL_GetTick() + Timeout; while (data processing is running)
{
if(timeout) { return HAL_TIMEOUT;}
}
当外设发⽣错误时,HAL_PPP_Process ()返回⼀个HAL_ERROR错误状态,HAL PPP外设驱动程序实现HAL_PPP_GetError ()允许检索错误;
HAL_PPP_ErrorTypeDef HAL_PPP_GetError (PPP_HandleTypeDef *hppp);
在所有的外设句柄中,定义了HAL_PPP_ErrorTypeDef来存储最后⼀个错误代码;
typedef struct
{
PPP_TypeDef * Instance; /* PPP registers base address */
PPP_InitTypeDef Init; /* PPP initialization parameters */
HAL_LockTypeDef Lock; /* PPP locking object */
__IO HAL_PPP_StateTypeDef State; /* PPP state */
__IO HAL_PPP_ErrorTypeDef ErrorCode; /* PPP Error code */
(…)
/* PPP specific parameters */
}
PPP_HandleTypeDef;
在返回错误之前,始终更新错误状态和外围设备的全局状态;
PPP->State = HAL_PPP_READY; /* Set the peripheral ready */
PP->ErrorCode = HAL_ERRORCODE ; /* Set the error code */
_HAL_UNLOCK(PPP) ; /* Unlock the PPP resources */
return HAL_ERROR; /*return with HAL error */
HAL_PPP_GetError()必须在错误回调中的中断模式下使⽤:
void HAL_PPP_ProcessCpltCallback(PPP_HandleTypeDef *hspi)
{
ErrorCode = HAL_PPP_GetError (hppp); /* retreive error code */
}
运⾏时间检查:
HAL通过检查所有HAL驱动程序函数的输⼊值来实现运⾏时故障检测;运⾏时检查是通过使⽤assert_parammacro实现的;该宏⽤于具有输⼊参数的所有HAL驱动程序函数;它允许验证输⼊值是否在参数允许值范围内;
要启⽤运⾏时检查,请使⽤assert_parammacro,并在stm32f0xx_hal_conf.hfile中取消注释定义USE_FULL_ASSERT;
void HAL_UART_Init(UART_HandleTypeDef *huart)
{
(..) /* Check the parameters */
assert_param(IS_UART_INSTANCE(huart->Instance));
assert_param(IS_UART_BAUDRATE(huart->Init.BaudRate));
assert_param(IS_UART_WORD_LENGTH(huart->Init.WordLength));
assert_param(IS_UART_STOPBITS(huart->Init.StopBits));
assert_param(IS_UART_PARITY(huart->Init.Parity));
assert_param(IS_UART_MODE(huart->Init.Mode));
assert_param(IS_UART_HARDWARE_FLOW_CONTROL(huart->Init.HwFlowCtl));
(..)
/** @defgroup UART_Word_Length *
@{
*/
#define UART_WORDLENGTH_8B ((uint32_t)0x00000000)
#define UART_WORDLENGTH_9B ((uint32_t)USART_CR1_M)
#define IS_UART_WORD_LENGTH(LENGTH) (((LENGTH) == UART_WORDLENGTH_8B) ||
\ ((LENGTH) == UART_WORDLENGTH_9B))
如果传递给assert_param宏的表达式为false,则调⽤theassert_failed函数并返回源⽂件的名称和失败的调⽤的源⾏号;如果表达式为true,则不返回任何值;assert_parammacro在stm32f0xx_hal_conf.h中实现:
/* Exported macro ------------------------------------------------------------*/
#ifdef USE_FULL_ASSERT
/**
* @brief The assert_param macro is used for function's parameters check.
* @param expr: If expr is false, it calls assert_failed function
* which reports the name of the source file and the source
* line number of the call that failed.
* If expr is true, it returns no value.
* @retval None */
#define assert_param(expr) ((expr)?(void)0:assert_failed((uint8_t *)__FILE__,
__LINE__))
/* Exported functions --------------------------------------*/
void assert_failed(uint8_t* file, uint32_t line);
#else
#define assert_param(expr)((void)0)
#endif /* USE_FULL_ASSERT */
assert_failed函数在main.c⽂件或任何其他⽤户C⽂件中实现:
#ifdef USE_FULL_ASSERT /**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None */
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
由于引⼊了开销运⾏时检查,因此建议在应⽤程序代码开发和调试期间使⽤它,并将其从最终应⽤程序中删除以改进代码⼤⼩和速度;。

相关文档
最新文档