[应用相关] STM32F4 HAL库学习笔记之串口通讯

[复制链接]
1567|32
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:46 | 显示全部楼层 |阅读模式
步骤
GPIO和串口时钟使能;
初始化GPIO,复用;
设置串口参数及中断;
使能串口;
编写中断服务函数。
函数使用
串口初始化
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
串口初始化函数,这个函数有一个入口参数huart(句柄), UART_HandleTypeDef 结构体指针类型,UART_HandleTypeDef 中包含了另外的结构体和参数,用到了句柄用来专门登记各应用对象在内存中的地址变化。

 楼主| 花间一壶酒sd 发表于 2023-9-30 23:46 | 显示全部楼层
  1. typedef struct
  2. {
  3.          USART_TypeDef *Instance;
  4.         UART_InitTypeDef Init;
  5.          uint8_t *pTxBuffPtr;
  6.          uint16_t TxXferSize;
  7.          uint16_t TxXferCount;
  8.          uint8_t *pRxBuffPtr;
  9.          uint16_t RxXferSize;
  10.          uint16_t RxXferCount;
  11.          DMA_HandleTypeDef *hdmatx;
  12.          DMA_HandleTypeDef *hdmarx;
  13.          HAL_LockTypeDef Lock;
  14.          __IO HAL_UART_StateTypeDef State;
  15.          __IO uint32_t ErrorCode;
  16. }UART_HandleTypeDef;
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:46 | 显示全部楼层
UART_HandleTypeDef 中配置串口相关设置,在HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)调用时会使能响应串口,不需要另外单独使能。
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:46 | 显示全部楼层
执行HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)时会先调用MSP初始化回调函数进行MCU初始化,打开这个函数,在356行有

HAL_UART_MspInit(huart);
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:46 | 显示全部楼层
这句前面是看用户有没有自定义回调函数,判断是否执行这个。自定义下面会写。在这里面可以执行GPIO和中断相关配置。打开这个函数里面是
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:47 | 显示全部楼层
  1. __weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)
  2. {
  3.           UNUSED(huart);
  4.           /* NOTE: This function should not be modified, when the callback is needed,
  5.                    the HAL_UART_MspInit could be implemented in the user file
  6. }
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:47 | 显示全部楼层
_weak是定义的一个弱函数,如果用户重新定义了这个函数,那么会优先执行用户定义的函数,可以重新定义这个函数配置与MCU级别相关的硬件初始化,这也是HAL库的优点。
所以HAL库中的初始化流程为:先初始化与MCU无关的串口协议,再初始化与MCU相关的硬件配置,这样在移植过程中只需要修改回调函数中的参数,即可完成适配。
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:47 | 显示全部楼层
回调函数:
函数A调用函数B的时候,通过参数将函数C的指针传递给了函数B(也就是函数B的入口参数写为函数C的指针)。在函数B执行过程中调用了函数C,这个动过叫做回调。在这个过程中先是被当作指针传入又被回调的函数C就是回调函数。
很多资料中说HAL_UART_MspInit(huart)是回调函数,其实这里只是写成了回调函数的样式,如果用户没有自定义回调函数,这个就可以看做是普通调用。
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:47 | 显示全部楼层
用户自定义回调函数:
  1. #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  2.             UART_InitCallbacksToDefault(huart);
  3.             if (huart->MspInitCallback == NULL)
  4.             {
  5.               huart->MspInitCallback = HAL_UART_MspInit;
  6.             }
  7.             huart->MspInitCallback(huart);
  8.             #else
  9.             HAL_UART_MspInit(huart);
  10. #endif
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:47 | 显示全部楼层
如果定义了USE_HAL_UART_REGISTER_CALLBACKS=1,那么用户可以自定义串口的初始化函数
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:47 | 显示全部楼层
  1. #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  2. void UART_InitCallbacksToDefault(UART_HandleTypeDef *huart)
  3. {
  4.     /* Init the UART Callback settings */
  5.     huart->TxHalfCpltCallback        = HAL_UART_TxHalfCpltCallback;        /* Legacy weak TxHalfCpltCallback        */
  6.     huart->TxCpltCallback            = HAL_UART_TxCpltCallback;            /* Legacy weak TxCpltCallback            */
  7.     huart->RxHalfCpltCallback        = HAL_UART_RxHalfCpltCallback;        /* Legacy weak RxHalfCpltCallback        */
  8.     huart->RxCpltCallback            = HAL_UART_RxCpltCallback;            /* Legacy weak RxCpltCallback            */
  9.     huart->ErrorCallback             = HAL_UART_ErrorCallback;             /* Legacy weak ErrorCallback             */
  10.     huart->AbortCpltCallback         = HAL_UART_AbortCpltCallback;         /* Legacy weak AbortCpltCallback         */
  11.     huart->AbortTransmitCpltCallback = HAL_UART_AbortTransmitCpltCallback; /* Legacy weak AbortTransmitCpltCallback */
  12.     huart->AbortReceiveCpltCallback  = HAL_UART_AbortReceiveCpltCallback;  /* Legacy weak AbortReceiveCpltCallback  */

  13. }
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:48 | 显示全部楼层
这种状态就是回调函数了。
最后调用HAL_UART_Receive_IT函数开启接收中断,并设置接收缓冲以及最大接收数量。
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:48 | 显示全部楼层
串口中断
要使用中断,首先需要配置中断,由于NVIC属于MCU级别,所以放在HAL_UART_Msplnit中初始化,与库函数不同,HAL库中使用了两个函数配置中断:
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:48 | 显示全部楼层
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)//使能中断
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)//配置抢占优先级、子优先级。
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:48 | 显示全部楼层
串口接收
编写中断服务函数
USARTX_IRQHandler
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:48 | 显示全部楼层
HAL库中定义了一个串口中断处理通用函数:

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:48 | 显示全部楼层
打开这个函数,找到如下部分:
  1. if (errorflags == RESET)
  2. {
  3.         /* UART in mode Receiver -------------------------------------------------*/
  4.            if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
  5.            {
  6.                       UART_Receive_IT(huart);
  7.                 return;
  8.              }
  9.   }
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:48 | 显示全部楼层
isrflags 宏定义串口状态寄存器,cr1its控制状态寄存器,调用上面的通用函数后,会判断串口状态,是否进入UART_Receive_IT(huart),在这个函数中开始启动接收工作并调用串口数据处理函数:
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:49 | 显示全部楼层
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
 楼主| 花间一壶酒sd 发表于 2023-9-30 23:49 | 显示全部楼层
这个函数和上面一样是一个回调函数(用户可自定义)库中该函数的定义为弱函数,在这里进行串口数据处理,HAL库中所有的串口数据处理在这里面进行,所以这里面会判断是哪一个串口的数据再进行判断,剩下的流程就和用库函数处理是一样的了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

101

主题

1219

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部