打印
[应用相关]

HAL驱动程序概述

[复制链接]
楼主: xiaoqi000
手机看帖
扫描二维码
随时随地手机跟帖
21
xiaoqi000|  楼主 | 2024-3-31 22:24 | 只看该作者 回帖奖励 |倒序浏览
配置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);

使用特权

评论回复
22
xiaoqi000|  楼主 | 2024-3-31 22:24 | 只看该作者
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()

使用特权

评论回复
23
xiaoqi000|  楼主 | 2024-3-31 22:24 | 只看该作者
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()

使用特权

评论回复
24
xiaoqi000|  楼主 | 2024-3-31 22:24 | 只看该作者
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()回调函数;

使用特权

评论回复
25
xiaoqi000|  楼主 | 2024-3-31 22:25 | 只看该作者
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函数自动初始化;

使用特权

评论回复
26
xiaoqi000|  楼主 | 2024-3-31 22:25 | 只看该作者
如何使用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;

使用特权

评论回复
27
xiaoqi000|  楼主 | 2024-3-31 22:25 | 只看该作者
系统时钟初始化:
       系统时钟的配置可以在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();
}
}

使用特权

评论回复
28
xiaoqi000|  楼主 | 2024-3-31 22:25 | 只看该作者
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()可以保持不变;

使用特权

评论回复
29
xiaoqi000|  楼主 | 2024-3-31 22:25 | 只看该作者
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; }

使用特权

评论回复
30
xiaoqi000|  楼主 | 2024-3-31 22:25 | 只看该作者
中断模式:

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()初始被声明为弱函数,用户可以在应用层重新定义该函数;

使用特权

评论回复
31
xiaoqi000|  楼主 | 2024-3-31 22:26 | 只看该作者
具体应用举例:

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);
}

使用特权

评论回复
32
xiaoqi000|  楼主 | 2024-3-31 22:26 | 只看该作者
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相关联;

使用特权

评论回复
33
xiaoqi000|  楼主 | 2024-3-31 22:26 | 只看该作者
要在外设中添加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()函数在驱动程序中声明为弱函数,这意味着用户可以在应用程序代码中再次声明它,而不用修改驱动程序中的功能;

使用特权

评论回复
34
xiaoqi000|  楼主 | 2024-3-31 22:26 | 只看该作者
在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)
{
}

使用特权

评论回复
35
xiaoqi000|  楼主 | 2024-3-31 22:26 | 只看该作者
在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 ;
(…)
}

使用特权

评论回复
36
xiaoqi000|  楼主 | 2024-3-31 22:26 | 只看该作者
超时和错误管理:
超时管理:
超时通常用于以轮询模式运行的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;
}
}
(…)
}

使用特权

评论回复
37
xiaoqi000|  楼主 | 2024-3-31 22:26 | 只看该作者
以下示例显式轮询函数中如何使用超时:

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;
    }
  }
  (…)
}

使用特权

评论回复
38
xiaoqi000|  楼主 | 2024-3-31 22:27 | 只看该作者
错误管理:
通过检查以下的参数来确定错误;

       有效参数:使用的参数要是有效的,并且已经定义的,否则系统很可能陷入未定义状态,在使用之前检查这些参数;

HAL_StatusTypeDef HAL_PPP_Process(PPP_HandleTypeDef* hppp, uint32_t *pdata, uint32
Size)
{
if ((pData == NULL ) || (Size == 0))
{
return HAL_ERROR;
}
}

使用特权

评论回复
39
xiaoqi000|  楼主 | 2024-3-31 22:27 | 只看该作者
有效句柄: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;
}
}

使用特权

评论回复
40
xiaoqi000|  楼主 | 2024-3-31 22:27 | 只看该作者
 超时错误:发生超时错误时使用以下语句: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 */
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则