原理性说明:
//参考网址:https://blog.csdn.net/u010561799/article/details/89526266
在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器,另一个是程序看不到的移位寄存器,对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空(单字节),另一个是TC=发送结束(多字节)。
当USART_DR中的1字节数据传送到移位寄存器后,TXE被置位,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,程序可以把下一个要发送的1字节数据(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位(共8位)发送结束,所有多个字节均发送结束时(最后1字节中送出停止位后)硬件会将TC标志置位。
另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的(故通常程序串口初始化时不打开此中断,否则频繁进入TXE中断)。而发送完成TCIE和IDLEIE则必须等发送1次数据后,才会有此中断产生,故一开始打开TCIE和IDLEIE中断没有关系。当然一开始必须要打开RXNEIE中断(接收数据),TXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为'1'时产生中断,而TCIE允许在TC标志为'1'时产生中断。
至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。
TXE--写寄存器DR清零
RXNE--读寄存器DR清零,也可软件手动清零
TC-- 读/写寄存器DR清零,也可软件手动清零
IDLE--需要软件清除,__HAL_UART_CLEAR_IDLEFLAG[HAL]
串口收发数据都是以1个字节为单位,如果是阻塞轮询模式,是while多少字节数,如果是中断,则每收发1个字节,则进入1次中断;
HAL库代码分析:
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{//阻塞串口发送函数
uint16_t* tmp;
uint32_t tickstart = 0U;
/* Check that a Tx process is not already ongoing */
if(huart->gState == HAL_UART_STATE_READY)
{
if((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->gState = HAL_UART_STATE_BUSY_TX;
/* Init tickstart for timeout managment */
tickstart = HAL_GetTick();
huart->TxXferSize = Size;//发送的数据量(多少字节)
huart->TxXferCount = Size;//还剩余的要发送的数据量
while(huart->TxXferCount > 0U)//阻塞模式,while循环
{
huart->TxXferCount--;//每发完1个字节,剩余要发送数据量减1
if(huart->Init.WordLength == UART_WORDLENGTH_9B)
{//带有校验
if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT;
}
tmp = (uint16_t*) pData;
huart->Instance->DR = (*tmp & (uint16_t)0x01FF);
if(huart->Init.Parity == UART_PARITY_NONE)
{//校验正确
pData +=2U;
}
else
{
pData +=1U;
}
}
else
{//不带校验
if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
{//上面函数作用是保证发送寄存器里的数据为空(UART_FLAG_TXE置位)
return HAL_TIMEOUT;
}
huart->Instance->DR = (*pData++ & (uint8_t)0xFF);//发送寄存器写入数据,UART_FLAG_TXE复位【待数据全部移到发送移位寄存器时,UART_FLAG_TXE置位,故前面超时等待】
}
}
if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
{//上面函数作用是保证全部传输完成,UART_FLAG_TC置1,否则也认为传输失败,双重保证!
return HAL_TIMEOUT;
}
/* At end of Tx process, restore huart->gState to Ready */
huart->gState = HAL_UART_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(huart);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{//串口中断方式发送数据
/* Check that a Tx process is not already ongoing */
if(huart->gState == HAL_UART_STATE_READY)
{
if((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pTxBuffPtr = pData;//指向待发送缓冲区首地址
huart->TxXferSize = Size;//发送的数据量(多少字节)
huart->TxXferCount = Size;//还剩余的要发送的数据量
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->gState = HAL_UART_STATE_BUSY_TX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Transmit data register empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_TXE);//此处仅仅打开TXE中断,【此时还未开始发送数据,发送缓冲区TDR为空,将进入到串口中断函数-stm32f1xx_it.c的void USARTx_IRQHandler(void) x=1,2,3...】
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
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;
/* If no error occurs */
errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
if(errorflags == RESET)
{//无任何串口错误时
/* UART in mode Receiver -------------------------------------------------*/
if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{//接收中断标志置位且中断使能
UART_Receive_IT(huart);//串口中断接收处理函数
return;
}
}
/* If some errors occur */
if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
{
/* UART parity error interrupt occurred ----------------------------------*/
if(((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))
{//校验出错且校验中断使能
huart->ErrorCode |= HAL_UART_ERROR_PE;
}
/* UART noise error interrupt occurred -----------------------------------*/
if(((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_NE;
}
/* UART frame error interrupt occurred -----------------------------------*/
if(((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_FE;
}
/* UART Over-Run interrupt occurred --------------------------------------*/
if(((isrflags & USART_SR_ORE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_ORE;
}
/* Call UART Error Call back function if need be --------------------------*/
if(huart->ErrorCode != HAL_UART_ERROR_NONE)
{
/* UART in mode Receiver -----------------------------------------------*/
if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{
UART_Receive_IT(huart);
}
/* If Overrun error occurs, or if any error occurs in DMA mode reception,
consider error as blocking */
dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
if(((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)
{
/* Blocking error : transfer is aborted
Set the UART state ready to be able to start again the process,
Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
UART_EndRxTransfer(huart);
/* Disable the UART DMA Rx request if enabled */
if(HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
{
CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
/* Abort the UART DMA Rx channel */
if(huart->hdmarx != NULL)
{
/* Set the UART DMA Abort callback :
will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;
if(HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
{
/* Call Directly XferAbortCallback function in case of error */
huart->hdmarx->XferAbortCallback(huart->hdmarx);
}
}
else
{
/* Call user error callback */
HAL_UART_ErrorCallback(huart);//异常回调函数可增加提示
}
}
else
{
/* Call user error callback */
HAL_UART_ErrorCallback(huart);
}
}
else
{
/* Non Blocking error : transfer could go on.
Error is notified to user through user error callback */
HAL_UART_ErrorCallback(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
}
}
return;
} /* End if some error occurs */
/* UART in mode Transmitter ------------------------------------------------*/
if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{//接回上面提到的TXEIE中断被触发,接着执行到此处【每发1个字节执行到此处】,下面有UART_Transmit_IT详细分析
UART_Transmit_IT(huart);
return;
}
/* UART in mode Transmitter end --------------------------------------------*/
if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
{//如果上面的多个字节都发送完毕,则此刻TC标志被置位,如果中断使能,将进入到下面函数执行
UART_EndTransmit_IT(huart);//__HAL_UART_DISABLE_IT(huart, UART_IT_TC);关闭TC中断,还有个回调空白函数-发送完成函数HAL_UART_TxCpltCallback供用户增加想要的功能
return;
}
}
static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
{
uint16_t* tmp;
/* Check that a Tx process is ongoing */
if(huart->gState == HAL_UART_STATE_BUSY_TX)
{
if(huart->Init.WordLength == UART_WORDLENGTH_9B)
{//带有校验处理
tmp = (uint16_t*) huart->pTxBuffPtr;
huart->Instance->DR = (uint16_t)(*tmp & (uint16_t)0x01FF);
if(huart->Init.Parity == UART_PARITY_NONE)
{
huart->pTxBuffPtr += 2U;
}
else
{
huart->pTxBuffPtr += 1U;
}
}
else
{//不带校验发送
huart->Instance->DR = (uint8_t)(*huart->pTxBuffPtr++ & (uint8_t)0x00FF);//TDR发送寄存器写入数据,TXE标志被清零,待发送寄存器中的1字节数据被全部转移到发送移位寄存器中,此刻发送寄存器又为空,TXE中断再次被触发,由再次进入到串口中断函数,进入到此处执行,故而实现数据连续不间断发送【pTxBuffPtr++指向待发送下1字节数据】
}
if(--huart->TxXferCount == 0U)
{//待发送的字节数为0时,表示数据发送完毕
/* Disable the UART Transmit Complete Interrupt */
__HAL_UART_DISABLE_IT(huart, UART_IT_TXE);//必须关闭TXE中断,否则会一直进入,毕竟没数据发送时,TDR一直为空
/* Enable the UART Transmit Complete Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_TC);//打开发送完成中断,因为数据发送完毕,TC标志将置位,将再次进入到串口中断函数里执行,此时执行到前面的UART_EndTransmit_IT函数
}
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{//串口阻塞接收函数【中断未打开】
uint16_t* tmp;
uint32_t tickstart = 0U;
/* Check that a Rx process is not already ongoing */
if(huart->RxState == HAL_UART_STATE_READY)
{
if((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Init tickstart for timeout managment */
tickstart = HAL_GetTick();
huart->RxXferSize = Size;//接收的最大数据量字节数 【因是阻塞模式,故事先知道接收多少字节数据】
huart->RxXferCount = Size;//还剩余的要接收的数据量字节数
/* Check the remain data to be received */
while(huart->RxXferCount > 0U)
{
huart->RxXferCount--;//每接收完1字节,则减1
if(huart->Init.WordLength == UART_WORDLENGTH_9B)
{//带有校验
if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT;
}
tmp = (uint16_t*)pData;
if(huart->Init.Parity == UART_PARITY_NONE)
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);
pData +=2U;
}
else
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
pData +=1U;
}
}
else
{//不带校验的接收处理
if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
{//等待RXNE标志置位,等待时间Timeout【特别说明,如果传递的Size大于实际收到的数据,则肯定会超时】,如果被置位,表示接收寄存器RDR收到数据
return HAL_TIMEOUT;//如果未被置位,则超时退出
}
if(huart->Init.Parity == UART_PARITY_NONE)
{
*pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);//从DR读取数据,该操作会将RXNE标志复位,前面置位,故而形成循环,保证数据正确接收
}
else
{
*pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
}
}
}
/* At end of Rx process, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(huart);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{//串口中断接收函数
/* Check that a Rx process is not already ongoing */
if(huart->RxState == HAL_UART_STATE_READY)
{
if((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__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;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Parity Error Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);//校验错误中断使能
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);//错误中断使能-帧错误,噪错误,满溢错误
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);//接收中断使能【如果串口收到数据,则进入到串口处理函数HAL_UART_IRQHandler】
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
如果串口收到数据,则进入到串口处理函数HAL_UART_IRQHandler的下面部分执行
if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{//打开串口中断,且有数据收到-RXNE被置位
UART_Receive_IT(huart);
return;
}
static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
uint16_t* tmp;
/* Check that a Rx process is ongoing */
if(huart->RxState == HAL_UART_STATE_BUSY_RX)
{
if(huart->Init.WordLength == UART_WORDLENGTH_9B)
{//带有校验
tmp = (uint16_t*) huart->pRxBuffPtr;
if(huart->Init.Parity == UART_PARITY_NONE)
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);
huart->pRxBuffPtr += 2U;
}
else
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
huart->pRxBuffPtr += 1U;
}
}
else
{//不带校验
if(huart->Init.Parity == UART_PARITY_NONE)
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);//接收数据寄存器RDR被读取【保存数据到接收buf,并指向下一个】,RXNE标志复位。【当下1字节数据8位全部都从接收移位寄存器转移到接收数据寄存器RDR后,RXNE再次被置位,将再次触发RXNE中断,再次到此处执行,如此循环直到收完全部数据】
}
else
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
}
}
if(--huart->RxXferCount == 0U)
{//收完全部数据后
/* Disable the IRDA Data Register not empty Interrupt */
__HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);//关闭RXNE中断
/* Disable the UART Parity Error Interrupt */
__HAL_UART_DISABLE_IT(huart, UART_IT_PE);//关闭校验错误中断
/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_DISABLE_IT(huart, UART_IT_ERR);//关闭错误中断
/* Rx process is completed, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
HAL_UART_RxCpltCallback(huart);//接收完成中断回调函数,用户可以再这里做操作
return HAL_OK;
}
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}