9. 初识HAL固件库

您所在的位置:网站首页 固件库函数和CMSIS的关系 9. 初识HAL固件库

9. 初识HAL固件库

2024-07-14 00:25| 来源: 网络整理| 查看: 265

9.2.2. 初识库函数¶

所谓库函数,就是STM32的库文件中为我们编写好的函数接口,我们只要调用这些库函数, 就可以对STM32进行配置,达到控制目的。我们可以不知道库函数是如何实现的, 但我们调用函数必须要知道函数的功能、可传入的参数及其意义、和函数的返回值。

于是,有读者就问那么多函数我怎么记呀?我的回答是:会查就行了,哪个人记得了那么多。 所以我们学会查阅库帮助文档 是很有必要的。

打开库帮助文档《STM32H753xx_User_Manual.chm》见图 库帮助文档

层层打开文档的目录标签:

标签目录:Modules\STM32H7xx_HAL_Driver

可看到STM32H7xx_HAL_Driver标签下有很多外设驱动文件的名字HAL、ADC、BKP、CAN等标签。

我们试着查看GPIO的“位设置函数GPIO_SetBits”看看,打开标签:

标签目录:Modules\STM32H4xx_StdPeriph_Driver\GPIO\GPIO Exported Functions\IO operation functions\HAL_GPIO_WritePin, 见图 库帮助文档的函数说明 。

利用这个文档,我们即使没有去看它的具体源代码,也知道要怎么利用它了。

如HAL_GPIO_WritePin,函数的原型为void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)。 它的功能是:输入一个类型为GPIO_TypeDef的指针GPIOx参数,选定要控制的GPIO端口;输入GPIO_PIN_x宏,其中x指端口的引脚号, 指定要控制的引脚;输入GPIO_PIN_RESET或者GPIO_PIN_SET设置IO口的电平高低。

其中输入的参数 GPIOx为ST的HAL库中定义的自定义数据类型,这两个传入参数均为结构体指针。初学时, 我们并不知道如GPIO_TypeDef这样的类型是什么意思, 可以点击函数原型中带下划线的 GPIO_TypeDef 就可以查看这个类型的声明了。

就这样初步了解了一下库函数,读者就可以发现STM32的库是写得很优美的。每个函数和数据类型都符合见名知义的原则, 当然,这样的名称写起来特别长,而且对于我们来说要输入这么长的英文,很容易出错,所以在开发软件的时候, 在用到库函数的地方,直接把库帮助文档中的函数名称复制粘贴到工程文件就可以了。 而且,配合MDK软件的代码自动补全功能,可以减少输入量。

有的用户觉得使用库文档麻烦,也可以直接查阅STM32 HAL库的源码,库帮助文档的说明都是根据源码生成的, 所以直接看源码也可以了解函数功能。

下面我们以USART为例,介绍一下HAL的外设结构体。请读者注意,由于我们使用USART/UART这个外设, 需要把包含串口外设的宏定义打开,见 代码清单:HAL-5 串口外设头文件宏定义。

代码清单:HAL-5 串口外设头文件宏定义(stm32h7xx_hal_conf.h文件)¶ 1 2#define HAL_UART_MODULE_ENABLED #define HAL_USART_MODULE_ENABLED

关于外设的初始化结构体有两个,一个是xxx_HandleTypeDef(xxx为某一个外设,例如USART_HandleTypeDef), 称为外设管理结构体,其结构体成员如下:

(1) Instance:外设寄存器基地址指针,所有参数都是指定基地址后才能正确写入寄存器。所有外设的地址,STM32都已经为我们封装好了, 如 代码清单:HAL-6 外设寄存器基地址(以USART为例见文件stm32h743xx.h)。这是USART2、USART3、USART6的外设基地址。

代码清单:HAL-2 外设寄存器基地址(以USART为例见文件stm32h743xx.h)¶ 1 2 3#define USART2 ((USART_TypeDef *) USART2_BASE) #define USART3 ((USART_TypeDef *) USART3_BASE) #define USART6 ((USART_TypeDef *) USART6_BASE)

(2) Init:外设的初始化结构体,一般都是用来配置外设的工作方式。 如串口的波特率,起始位、数据位、停止位的长度。

以上的两个结构体成员,是我们在初始化时,必须要进行配置的。

(3) pTxBuffPtr、TxXferSize、TxXferCount、pRxBuffPtr、RxXferSize、RxXferCount、Mask、State、ErrorCode:这些参数则不需要用户关心。 在调用HAL库函数的时候,会根据实际情况,进行赋值,如 代码清单:HAL-3 HAL库函数调用。

代码清单:HAL-7 HAL库函数调用(文件stm32h7xx_hal_usart.c)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23HAL_StatusTypeDef HAL_USART_Init(USART_HandleTypeDef *husart) { if (husart == NULL) { return HAL_ERROR; } assert_param(IS_USART_INSTANCE(husart->Instance)); if (husart->State == HAL_USART_STATE_RESET) { husart->Lock = HAL_UNLOCKED; HAL_USART_MspInit(husart); } husart->State = HAL_USART_STATE_BUSY; __HAL_USART_DISABLE(husart); if (USART_SetConfig(husart) == HAL_ERROR) { return HAL_ERROR; } CLEAR_BIT(husart->Instance->CR2, USART_CR2_LINEN); CLEAR_BIT(husart->Instance->CR3, (USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN)); if (husart->Init.SlaveMode) { CLEAR_BIT(husart->Instance->CR2, USART_CR2_CLKEN); } __HAL_USART_ENABLE(husart); return (USART_CheckIdleState(husart)); }

上述代码的第11行,当外设处于工作状态时,则HAL库会将HAL_USART_STATE_BUSY赋给State。还有其他的状态值,如HAL_USART_STATE_RESET、 HAL_USART_STATE_READY、HAL_USART_STATE_ERROR等等。

(4) Lock:外设的锁资源。通常都在对外设配置前锁上进程锁,设置完毕后释放进程锁。 如 代码清单:HAL-7 HAL库函数调用的第8行代码。

(5) Hdmatx、hdmarx:负责与外设相关的DMA配置,如使用哪一个DMA,DMA的工作模式等等。 用户可以通过该方式来配置相应的DMA传输。下面我们介绍另一种方式。

代码清单:HAL-8 DMA的相应配置¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39void USART_DMA_Config(void) { /*开启DMA时钟*/ DEBUG_USART_DMA_CLK_ENABLE(); DMA_Handle.Instance = DEBUG_USART_DMA_STREAM; /*配置usart1 tx对应dma*/ DMA_Handle.Init.Request = DMA_REQUEST_USART1_TX; /*方向:从内存到外设*/ DMA_Handle.Init.Direction= DMA_MEMORY_TO_PERIPH; /*外设地址不增*/ DMA_Handle.Init.PeriphInc = DMA_PINC_DISABLE; /*内存地址自增*/ DMA_Handle.Init.MemInc = DMA_MINC_ENABLE; /*外设数据单位*/ DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; /*内存数据单位 8bit*/ DMA_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; /*DMA模式:不断循环*/ DMA_Handle.Init.Mode = DMA_CIRCULAR; /*优先级:中*/ DMA_Handle.Init.Priority = DMA_PRIORITY_MEDIUM; /*禁用FIFO*/ DMA_Handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE; DMA_Handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; /*存储器突发传输 16个节拍*/ DMA_Handle.Init.MemBurst = DMA_MBURST_SINGLE; /*外设突发传输 1个节拍*/ DMA_Handle.Init.PeriphBurst = DMA_PBURST_SINGLE; /*配置DMA2的数据流7*/ // /* Deinitialize the stream for new transfer */ HAL_DMA_DeInit(&DMA_Handle); /* Configure the DMA stream */ HAL_DMA_Init(&DMA_Handle); /* Associate the DMA handle */ __HAL_LINKDMA(&UartHandle, hdmatx, DMA_Handle); }

代码清单:HAL-8 DMA的相应配置中的第4~34行,都是有关于DMA的配置,具体结构体的变量可以参考DMA的章节。最后调用__HAL_LINKDMA函数, 实际上就是将DMA_Handle的配置,赋值给UartHandle的结构体成员hdmatx。

具体的内容,见 代码清单:HAL-9 外设管理结构体的结构体成员。

代码清单:HAL-9 外设管理结构体的结构体成员¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16typedef struct { USART_TypeDef *Instance; USART_InitTypeDef Init; uint8_t *pTxBuffPtr; uint16_t TxXferSize; __IO uint16_t TxXferCount; uint8_t *pRxBuffPtr; uint16_t RxXferSize; __IO uint16_t RxXferCount; uint16_t Mask; DMA_HandleTypeDef *hdmatx; DMA_HandleTypeDef *hdmarx; HAL_LockTypeDef Lock; __IO HAL_USART_StateTypeDef State; __IO uint32_t ErrorCode; } USART_HandleTypeDef;

串口外设的中断服务函数,见 代码清单:HAL-10 HAL的串口中断服务函数。

代码清单:HAL-10 HAL的串口中断服务函数¶ 1 2 3 4 5 6 7 8void HAL_USART_IRQHandler(USART_HandleTypeDef *husart); void HAL_USART_TxHalfCpltCallback(USART_HandleTypeDef *husart); void HAL_USART_TxCpltCallback(USART_HandleTypeDef *husart); void HAL_USART_RxCpltCallback(USART_HandleTypeDef *husart); void HAL_USART_RxHalfCpltCallback(USART_HandleTypeDef *husart); void HAL_USART_TxRxCpltCallback(USART_HandleTypeDef *husart); void HAL_USART_ErrorCallback(USART_HandleTypeDef *husart); void HAL_USART_AbortCpltCallback (USART_HandleTypeDef *husart);

代码清单:HAL-10 HAL的串口中断服务函数中的第1行,是HAL库封装的外设中断服务函数,当用户使用该外设的中断时,需要在stm32h7xx_it.c文件中调用该函数。 具体内容见 代码清单:HAL-11 HAL_USART_IRQHandler函数(文件stm32h7xx_hal_usart.c)。该函数主要检测不同的外设标志位,根据不同的标志位, 调用不同的回调函数。例如当USART_FLAG_TXE为1时,则会调用HAL_USART_TxCpltCallback这个函数。HAL_USART_TxCpltCallback函数默认是一个空函数, 见 代码清单:HAL-11 HAL_USART_IRQHandler函数(文件stm32h7xx_hal_usart.c)。

代码清单:HAL-11 HAL_USART_IRQHandler函数(文件stm32h7xx_hal_usart.c)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28void HAL_USART_IRQHandler(USART_HandleTypeDef *husart) { uint32_t isrflags = READ_REG(husart->Instance->ISR); uint32_t cr1its = READ_REG(husart->Instance->CR1); uint32_t cr3its = READ_REG(husart->Instance->CR3); uint32_t errorflags; /* If no error occurs */ errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE | USART_ISR_UDR)); if (errorflags == RESET) { /* USART in mode Receiver ---------------------------------------------------*/ if (((isrflags & USART_ISR_RXNE) != RESET) && (((cr1its & USART_CR1_RXNEIE) != RESET) || ((cr3its & USART_CR3_RXFTIE) != RESET))) { if (husart->State == HAL_USART_STATE_BUSY_RX) { USART_Receive_IT(husart); } else { USART_TransmitReceive_IT(husart); } return; } } /* 省略部分代码*/ /* USART TX FIFO Empty -----------------------------------------------------*/ if (((isrflags & USART_ISR_TXFE) != RESET) && ((cr1its & USART_CR1_TXFEIE) != RESET)) { CLEAR_BIT(husart->Instance->CR1, USART_CR1_TXFEIE); } 代码清单:HAL-12 HAL_USART_TxCpltCallback函数¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14/** * @brief Tx Transfer completed callback. * @param husart: USART handle. * @retval None */ __weak void HAL_USART_TxCpltCallback(USART_HandleTypeDef *husart) { /* Prevent unused argument(s) compilation warning */ UNUSED(husart); /* NOTE : This function should not be modified, when the callback is needed, the HAL_USART_TxCpltCallback can be implemented in the user file. */ }

该函数是弱定义函数,用户可以自己重新定义一个回调函数HAL_USART_TxCpltCallback,来实现相应的操作。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3