HAL库串口函数解析
串口句柄 UART_HandleTypeDef
UART_HandleTypeDef UART1_Handler;
定义在stm32f1xx_hal_uart.h的138行左右,这个结构体,作为对 UART 的抽象来使用的。它记录了 UART 相关参数和状态,并提供回调函数的接口,以便用户可以根据需要执行各种操作。
在整个串口的操作中几乎都要用到。
typedef struct __UART_HandleTypeDef
{
USART_TypeDef *Instance; /*!< UART 寄存器基地址 */
UART_InitTypeDef Init; /*!< UART 通信参数 */
uint8_t *pTxBuffPtr; /*!< 指向 UART Tx 传输缓冲区的指针 */
uint16_t TxXferSize; /*!< UART Tx 传输大小 */
__IO uint16_t TxXferCount; /*!< UART Tx 传输计数器 */
uint8_t *pRxBuffPtr; /*!< 指向 UART Rx 传输缓冲区的指针 */
uint16_t RxXferSize; /*!< UART Rx 传输大小 */
__IO uint16_t RxXferCount; /*!< UART Rx 传输计数器 */
DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA 句柄参数 */
DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA 句柄参数 */
HAL_LockTypeDef Lock; /*!< 锁定对象 */
__IO HAL_UART_StateTypeDef gState; /*!< 与全局句柄管理和 Tx 操作相关的 UART 状态信息。
此参数可以是 @ref HAL_UART_StateTypeDef 的值 */
__IO HAL_UART_StateTypeDef RxState; /*!< 与 Rx 操作相关的 UART 状态信息。
此参数可以是 @ref HAL_UART_StateTypeDef 的值 */
__IO uint32_t ErrorCode; /*!< UART 错误代码 */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx 半完成回调 */
void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx 完成回调 */
void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx 半完成回调 */
void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx 完成回调 */
void (* ErrorCallback)(struct __UART_HandleTypeDef *huart); /*!< UART 错误回调 */
void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART 中止完成回调 */
void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART 中止传输完成回调 */
void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART 中止接收完成回调 */
void (* WakeupCallback)(struct __UART_HandleTypeDef *huart); /*!< UART 唤醒回调 */
void (* MspInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp 初始化回调 */
void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp 去初始化回调 */
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
} UART_HandleTypeDef;
串口服务函数 HAL_UART_IRQHandler
HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
解析:
当串口接收或发送数据时,会产生相应的中断,该中断处理函数会根据标志位(isrflags
)和控制寄存器(cr1its
和cr3its
)的状态来判断中断原因并进行相应的处理,如调用接收函数、传输函数或结束传输函数,并设置错误标志位(huart->ErrorCode
)以便后续处理。
具体而言,
当没有发生错误时,==如果接收缓冲区非空且接收中断使能,则调用接收函数UART_Receive_IT==;如果处于发送模式,调用发送函数
当发生错误时,根据错误类型设置错误标志位,并根据是否使用DMA模式接收数据以及错误类型采取不同的处理方式,如中止传输、关闭DMA请求等。
在上述过程中,还要考虑如何调用错误处理回调函数(huart->ErrorCallback
)或者弱错误回调函数(HAL_UART_ErrorCallback()
)以及是否开启了注册回调函数的功能(USE_HAL_UART_REGISTER_CALLBACKS
)。
函数具体解析:
/**
* @brief 处理UART中断请求
* @param huart 指向包含指定UART模块的配置信息的UART_HandleTypeDef结构体的指针
* @retval None
*/
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t isrflags = READ_REG(huart->Instance->SR);
uint32_t cr1its = READ_REG(huart->Instance->CR1);
uint32_t cr3its = READ_REG(huart->Instance->CR3);
uint32_t errorflags = 0x00U;
uint32_t dmarequest = 0x00U;
/* 如果没有错误发生 */
errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
if (errorflags == RESET)
{
/* UART在接收模式下 -------------------------------------------------*/
if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{
UART_Receive_IT(huart);
return;
}
}
/* 如果发生了一些错误 */
if ((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
{
/* UART奇偶校验错误中断发生 ----------------------------------*/
if (((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_PE;
}
/* UART噪声错误中断发生 -----------------------------------*/
if (((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_NE;
}
/* UART帧错误中断发生 -----------------------------------*/
if (((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_FE;
}
/* UART溢出中断发生 --------------------------------------*/
if (((isrflags & USART_SR_ORE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_ORE;
}
/* 如果需要,调用UART错误回调函数 --------------------------*/
if (huart->ErrorCode != HAL_UART_ERROR_NONE)
{
/* UART在接收模式下 -----------------------------------------------*/
if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{
UART_Receive_IT(huart);
}
/* 如果发生溢出错误,或者在DMA模式接收中发生任何错误,
将错误视为阻塞 */
dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)
{
/* 阻塞错误:传输被中止
设置UART状态为就绪,以便能够重新启动该过程,
禁用Rx中断,并禁用Rx DMA请求(如果正在进行) */
UART_EndRxTransfer(huart);
/* 如果启用了UART DMA Rx请求,则禁用它 */
if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
{
CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
/* 中止UART DMA Rx通道 */
if (huart->hdmarx != NULL)
{
/* 设置UART DMA中止回调:
将在DMA中止过程结束时调用HAL_UART_ErrorCallback() */
huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;
if (HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
{
/* 在出现错误的情况下直接调用XferAbortCallback函数 */
huart->hdmarx->XferAbortCallback(huart->hdmarx);
}
}
else
{
/* 调用用户错误回调函数 */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*调用已注册的错误回调函数*/
huart->ErrorCallback(huart);
#else
/*调用旧版的弱错误回调函数*/
HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
}
}
else
{
/* 调用用户错误回调函数 */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*调用已注册的错误回调函数*/
huart->ErrorCallback(huart);
#else
/*调用旧版的弱错误回调函数*/
HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
}
}
else
{
/* 非阻塞错误:传输可以继续进行。
通过用户错误回调通知用户错误 */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*调用已注册的错误回调函数*/
huart->ErrorCallback(huart);
#else
/*调用旧版的弱错误回调函数*/
HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
huart->ErrorCode = HAL_UART_ERROR_NONE;
}
}
return;
} /* 如果发生了一些错误 */
/* UART在发送模式下 ------------------------------------------------*/
if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
/* UART在发送模式下结束 --------------------------------------------*/
if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
{
UART_EndTransmit_IT(huart);
return;
}
}
串口中断使能 HAL_UART_Receive_IT
该函数负责开启中断
/**
* @brief 以非阻塞模式接收一定量的数据。
* @note 当UART奇偶校验未启用(PCE = 0)且字长配置为9位(M1-M0 = 01)时,
* 接收到的数据将被处理为一组u16。在这种情况下,Size必须指示
* 通过pData可用的u16数量。
* @param huart 指向包含指定UART模块的配置信息的UART_HandleTypeDef结构的指针。
* @param pData 指向数据缓冲区(u8或u16数据元素)的指针。
* @param Size 要接收的数据元素(u8或u16)的数量。
* @retval HAL状态
*/
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/* 检查是否已经有一个Rx进程正在进行 */
if (huart->RxState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* 进程锁定 */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* 进程解锁 */
__HAL_UNLOCK(huart);
/* 启用UART奇偶校验错误中断 */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);
/* 启用UART错误中断:(帧错误,噪声错误,溢出错误) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
/* 启用UART数据寄存器非空中断 */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
串口接收函数 UART_Receive_IT
UART_Receive_IT 是真正在接收数据的函数,==但在最后会关闭中断==
同时该函数会调用HAL_UART_RxCpltCallback回调函数,去实现当数据接收完需要实现的业务。
static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
uint16_t *tmp; // 定义一个指向uint16_t类型的指针变量tmp
/* 检查是否正在进行Rx进程 */
if (huart->RxState == HAL_UART_STATE_BUSY_RX)
{
if (huart->Init.WordLength == UART_WORDLENGTH_9B) // 如果数据位长度为9位
{
tmp = (uint16_t *) huart->pRxBuffPtr; // 将指针变量tmp指向接收缓存区指针所指向的位置
if (huart->Init.Parity == UART_PARITY_NONE) // 如果无奇偶校验
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF); // 将数据存入tmp指向的位置
huart->pRxBuffPtr += 2U; // 接收缓存区指针移动2个字节
}
else // 否则
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF); // 将数据存入tmp指向的位置
huart->pRxBuffPtr += 1U; // 接收缓存区指针移动1个字节
}
}
else // 如果数据位长度不为9位
{
if (huart->Init.Parity == UART_PARITY_NONE) // 如果无奇偶校验
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF); // 将数据存入接收缓存区指针所指向的位置
}
else // 否则
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F); // 将数据存入接收缓存区指针所指向的位置
}
}
if (--huart->RxXferCount == 0U) // 如果接收计数器减为0
{
/* 关闭UART数据寄存器非空中断 */
__HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);
/* 关闭UART奇偶校验错误中断 */
__HAL_UART_DISABLE_IT(huart, UART_IT_PE);
/* 关闭UART错误中断(帧错误、噪声错误、溢出错误) */
__HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
/* Rx进程已完成,将huart->RxState恢复为Ready状态 */
huart->RxState = HAL_UART_STATE_READY;
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/* 调用已注册的Rx完成回调函数 */
huart->RxCpltCallback(huart);
#else
/* 调用旧版的Rx完成回调函数 */
HAL_UART_RxCpltCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
return HAL_OK; // 返回状态为OK
}
return HAL_OK; // 返回状态为OK
}
else // 如果Rx进程未在进行
{
return HAL_BUSY; // 返回状态为BUSY
}
}
串口发送函数
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint8_t *pdata8bits; //定义8位数据指针
uint16_t *pdata16bits; //定义16位数据指针
uint32_t tickstart = 0U; //定义计时变量
/* 检查是否有正在进行的发送过程 */
if (huart->gState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR; //如果数据为空,返回错误
}
/* 进入关键区,锁定中断 */
__HAL_LOCK(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE; //将错误码设置为无错
huart->gState = HAL_UART_STATE_BUSY_TX; //将UART状态设置为忙碌
/* 初始化计时变量 */
tickstart = HAL_GetTick();
huart->TxXferSize = Size; //将发送数据大小赋值给TxXferSize
huart->TxXferCount = Size; //将发送数据大小赋值给TxXferCount
/*在进行9位/无奇偶校验传输时,pData需要被处理为uint16_t指针*/
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
pdata8bits = NULL; //如果是9位/无奇偶校验,将8位数据指针设置为空
pdata16bits = (uint16_t *) pData; //将pData强制转换为16位数据指针
}
else
{
pdata8bits = pData; //如果不是9位/无奇偶校验,将pData强制转换为8位数据指针
pdata16bits = NULL; //将16位数据指针设置为空
}
/* 解锁中断 */
__HAL_UNLOCK(huart);
/* 在发送所有数据之前,不断检查发送缓冲区是否为空 */
while (huart->TxXferCount > 0U)
{
/* 如果发送缓冲区为空则等待直到超时 */
if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT; //如果超时,则返回超时错误
}
/* 向数据寄存器中写入数据 */
if (pdata8bits == NULL)
{
huart->Instance->DR = (uint16_t)(*pdata16bits & 0x01FFU); //将16位数据写入数据寄存器
pdata16bits++; //指向下一个16位数据
}
else
{
huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU); //将8位数据写入数据寄存器
pdata8bits++; //指向下一个8位数据
}
huart->TxXferCount--; //减少发送数量
}
/* 等待发送完成标志位被置位 */
if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT; //如果超时,则返回超时错误
}
/* 在发送完成后,将UART状态设置为READY */
huart->gState = HAL_UART_STATE_READY;
return HAL_OK; //返回发送完成状态
}
else
{
return HAL_BUSY; //如果UART状态不是READY,则返回忙碌状态
}
}
Last updated