打印
[应用相关]

STM32F4 HAL库学习笔记之串口通讯

[复制链接]
450|32
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
步骤
GPIO和串口时钟使能;
初始化GPIO,复用;
设置串口参数及中断;
使能串口;
编写中断服务函数。
函数使用
串口初始化
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
串口初始化函数,这个函数有一个入口参数huart(句柄), UART_HandleTypeDef 结构体指针类型,UART_HandleTypeDef 中包含了另外的结构体和参数,用到了句柄用来专门登记各应用对象在内存中的地址变化。

使用特权

评论回复
沙发
花间一壶酒sd|  楼主 | 2023-9-30 23:46 | 只看该作者
typedef struct
{
         USART_TypeDef *Instance;
        UART_InitTypeDef Init;
         uint8_t *pTxBuffPtr;
         uint16_t TxXferSize;
         uint16_t TxXferCount;
         uint8_t *pRxBuffPtr;
         uint16_t RxXferSize;
         uint16_t RxXferCount;
         DMA_HandleTypeDef *hdmatx;
         DMA_HandleTypeDef *hdmarx;
         HAL_LockTypeDef Lock;
         __IO HAL_UART_StateTypeDef State;
         __IO uint32_t ErrorCode;
}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);

使用特权

评论回复
5
花间一壶酒sd|  楼主 | 2023-9-30 23:46 | 只看该作者
这句前面是看用户有没有自定义回调函数,判断是否执行这个。自定义下面会写。在这里面可以执行GPIO和中断相关配置。打开这个函数里面是

使用特权

评论回复
6
花间一壶酒sd|  楼主 | 2023-9-30 23:47 | 只看该作者
__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
          UNUSED(huart);
          /* NOTE: This function should not be modified, when the callback is needed,
                   the HAL_UART_MspInit could be implemented in the user file
}

使用特权

评论回复
7
花间一壶酒sd|  楼主 | 2023-9-30 23:47 | 只看该作者
_weak是定义的一个弱函数,如果用户重新定义了这个函数,那么会优先执行用户定义的函数,可以重新定义这个函数配置与MCU级别相关的硬件初始化,这也是HAL库的优点。
所以HAL库中的初始化流程为:先初始化与MCU无关的串口协议,再初始化与MCU相关的硬件配置,这样在移植过程中只需要修改回调函数中的参数,即可完成适配。

使用特权

评论回复
8
花间一壶酒sd|  楼主 | 2023-9-30 23:47 | 只看该作者
回调函数:
函数A调用函数B的时候,通过参数将函数C的指针传递给了函数B(也就是函数B的入口参数写为函数C的指针)。在函数B执行过程中调用了函数C,这个动过叫做回调。在这个过程中先是被当作指针传入又被回调的函数C就是回调函数。
很多资料中说HAL_UART_MspInit(huart)是回调函数,其实这里只是写成了回调函数的样式,如果用户没有自定义回调函数,这个就可以看做是普通调用。

使用特权

评论回复
9
花间一壶酒sd|  楼主 | 2023-9-30 23:47 | 只看该作者
用户自定义回调函数:
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
            UART_InitCallbacksToDefault(huart);
            if (huart->MspInitCallback == NULL)
            {
              huart->MspInitCallback = HAL_UART_MspInit;
            }
            huart->MspInitCallback(huart);
            #else
            HAL_UART_MspInit(huart);
#endif

使用特权

评论回复
10
花间一壶酒sd|  楼主 | 2023-9-30 23:47 | 只看该作者
如果定义了USE_HAL_UART_REGISTER_CALLBACKS=1,那么用户可以自定义串口的初始化函数

使用特权

评论回复
11
花间一壶酒sd|  楼主 | 2023-9-30 23:47 | 只看该作者
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
void UART_InitCallbacksToDefault(UART_HandleTypeDef *huart)
{
    /* Init the UART Callback settings */
    huart->TxHalfCpltCallback        = HAL_UART_TxHalfCpltCallback;        /* Legacy weak TxHalfCpltCallback        */
    huart->TxCpltCallback            = HAL_UART_TxCpltCallback;            /* Legacy weak TxCpltCallback            */
    huart->RxHalfCpltCallback        = HAL_UART_RxHalfCpltCallback;        /* Legacy weak RxHalfCpltCallback        */
    huart->RxCpltCallback            = HAL_UART_RxCpltCallback;            /* Legacy weak RxCpltCallback            */
    huart->ErrorCallback             = HAL_UART_ErrorCallback;             /* Legacy weak ErrorCallback             */
    huart->AbortCpltCallback         = HAL_UART_AbortCpltCallback;         /* Legacy weak AbortCpltCallback         */
    huart->AbortTransmitCpltCallback = HAL_UART_AbortTransmitCpltCallback; /* Legacy weak AbortTransmitCpltCallback */
    huart->AbortReceiveCpltCallback  = HAL_UART_AbortReceiveCpltCallback;  /* Legacy weak AbortReceiveCpltCallback  */

}

使用特权

评论回复
12
花间一壶酒sd|  楼主 | 2023-9-30 23:48 | 只看该作者
这种状态就是回调函数了。
最后调用HAL_UART_Receive_IT函数开启接收中断,并设置接收缓冲以及最大接收数量。

使用特权

评论回复
13
花间一壶酒sd|  楼主 | 2023-9-30 23:48 | 只看该作者
串口中断
要使用中断,首先需要配置中断,由于NVIC属于MCU级别,所以放在HAL_UART_Msplnit中初始化,与库函数不同,HAL库中使用了两个函数配置中断:

使用特权

评论回复
14
花间一壶酒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)//配置抢占优先级、子优先级。

使用特权

评论回复
15
花间一壶酒sd|  楼主 | 2023-9-30 23:48 | 只看该作者
串口接收
编写中断服务函数
USARTX_IRQHandler

使用特权

评论回复
16
花间一壶酒sd|  楼主 | 2023-9-30 23:48 | 只看该作者
HAL库中定义了一个串口中断处理通用函数:

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);

使用特权

评论回复
17
花间一壶酒sd|  楼主 | 2023-9-30 23:48 | 只看该作者
打开这个函数,找到如下部分:
if (errorflags == RESET)
{
        /* UART in mode Receiver -------------------------------------------------*/
           if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
           {
                      UART_Receive_IT(huart);
                return;
             }
  }

使用特权

评论回复
18
花间一壶酒sd|  楼主 | 2023-9-30 23:48 | 只看该作者
isrflags 宏定义串口状态寄存器,cr1its控制状态寄存器,调用上面的通用函数后,会判断串口状态,是否进入UART_Receive_IT(huart),在这个函数中开始启动接收工作并调用串口数据处理函数:

使用特权

评论回复
19
花间一壶酒sd|  楼主 | 2023-9-30 23:49 | 只看该作者
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

使用特权

评论回复
20
花间一壶酒sd|  楼主 | 2023-9-30 23:49 | 只看该作者
这个函数和上面一样是一个回调函数(用户可自定义)库中该函数的定义为弱函数,在这里进行串口数据处理,HAL库中所有的串口数据处理在这里面进行,所以这里面会判断是哪一个串口的数据再进行判断,剩下的流程就和用库函数处理是一样的了。

使用特权

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

本版积分规则

81

主题

1122

帖子

2

粉丝