概述
上一篇 说了 STM32CubeMX之串口的使用 (阻塞模式) ,这一章来说说串口中断模式收发数据。
文章目录
- 概述
- 一. 在STM32CubeMX 图形化中开启串口中断
- 二. 串口中断相关函数介绍
- 三. 串口中断函数使用实例
- 四. HAL库中的串口相关源码介绍
环境:
- 开发板:STM32F4探索者(正点原子)
一. 在STM32CubeMX 图形化中开启串口中断
在 前一篇 STM32CubeMX之串口的使用 (阻塞模式) 的文章的基础上,打开串口中断,如下图所示:
然后就可以生成工程了
二. 串口中断相关函数介绍
串口中断函数
- 如串口1中断函数:
USART1_IRQHandler()
发送接收函数
- 串口中断模式发送:
HAL_UART_Transmit_IT()
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
- 串口实例的指针
- 想要发送的数据的指针,如数组的首地址
- 想要发送数据的个数
- 串口中断模式接收:
HAL_UART_Receive_IT()
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
-
串口实例的指针
-
接收数据缓冲块的首地址,如数组的首地址
-
想要接收数据的个数
相关回调函数
- 串口中断模式发送完成回调:
HAL_UART_TxCpltCallback
- 串口中断模式接收完成回调:
HAL_UART_RxCpltCallback
三. 串口中断函数使用实例
- 在
stm32f4xx_it.c
中,先看一下串口中断函数有没有添加上,如下图所示:
现在就可以使用中断相关发送接收函数了
在这里为了方便测试,我添加了一个如下结构体并进行了初始化:
- 发送数据
在主函数中,5s 进行一次发送
发送成功产生回调,该函数在main.c
中
然后在主程序中查询到发送成功,打印 send done
-
接收数据
在进入循环的之前,就说明串口要进行10个字节的数据接收
接收10个字节成功产生回调,该函数在
main.c
中然后在主函数中,查询是否接收成功
最后运行程序,可以在串口调试助手上显示
注意:
若定长串口中断接收数据,数据溢出,将会产生数据溢出错误,中断不再接收数据,如下图:
错误回调函数如下:
//错误回调 void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { if( rxtx_it_usart.huart1 == huart) { printf("error %d\r\n",huart->ErrorCode); } }
以上例子,代码已上传
四. HAL库中的串口相关源码介绍
串口中断函数中的处理函数
HAL_UART_IRQHandler
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) { 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))) { if (((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET)) { huart->ErrorCode |= HAL_UART_ERROR_PE; } if (((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET)) { huart->ErrorCode |= HAL_UART_ERROR_NE; } if (((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET)) { huart->ErrorCode |= HAL_UART_ERROR_FE; } if (((isrflags & USART_SR_ORE) != RESET) && (((cr1its & USART_CR1_RXNEIE) != RESET) || ((cr3its & USART_CR3_EIE) != RESET))) { huart->ErrorCode |= HAL_UART_ERROR_ORE; } if (huart->ErrorCode != HAL_UART_ERROR_NONE) { if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET)) { UART_Receive_IT(huart); // printf("rcv agin error %d\r\n",huart->ErrorCode); } dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR); if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest) { UART_EndRxTransfer(huart); if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)) { CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR); if (huart->hdmarx != NULL) { huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError; if (HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK) { huart->hdmarx->XferAbortCallback(huart->hdmarx); } } else { #if (USE_HAL_UART_REGISTER_CALLBACKS == 1) huart->ErrorCallback(huart); #else HAL_UART_ErrorCallback(huart); #endif } } else { #if (USE_HAL_UART_REGISTER_CALLBACKS == 1) huart->ErrorCallback(huart); #else HAL_UART_ErrorCallback(huart); //printf("dma over error \r\n"); #endif } } else { #if (USE_HAL_UART_REGISTER_CALLBACKS == 1) huart->ErrorCallback(huart); #else HAL_UART_ErrorCallback(huart); //printf("usart over error \r\n"); #endif huart->ErrorCode = HAL_UART_ERROR_NONE; } } return; } if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET)) { UART_Transmit_IT(huart); return; } if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET)) { UART_EndTransmit_IT(huart); return; } }
主要是分为四个部分:
-
先读寄存器状态
uint32_t isrflags = READ_REG(huart->Instance->SR); uint32_t cr1its = READ_REG(huart->Instance->CR1); uint32_t cr3its = READ_REG(huart->Instance->CR3);
-
如果没有错误状态产生,且是接收中断,就进行数据接收
if (errorflags == RESET) { if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET)) { UART_Receive_IT(huart); return; } }
-
错误处理
//当有parity error,noise error,frame error,Over-Run 错误产生的时候,通过以下回调来处理 HAL_UART_ErrorCallback(huart);
注意:
通过查看
HAL
库,会发现该函数的定义用了关键字__weak
(弱符号声明) ,如下:__weak void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { UNUSED(huart); }
意味着,它可以由用户自定义函数,如果用户未自定义,否则就使用上述代码
-
串口数据发送
-
数据发送
if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET)) { UART_Transmit_IT(huart); return; }
-
结束数据发送
if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET)) { UART_EndTransmit_IT(huart); return; }
-
-
串口中断发送数据函数 HAL_UART_Transmit_IT()
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
if (huart->gState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
__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;
__HAL_UNLOCK(huart);
__HAL_UART_ENABLE_IT(huart, UART_IT_TXE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
-
对一些状态进行初始化,解释如下
huart->pTxBuffPtr = pData; //指向我们传递进来的数组 huart->TxXferSize = Size; //要发送数据的个数 huart->TxXferCount = Size; //用来计数,未接收数据的个数,发送一个数据就自减,减为0时,发送完成 huart->ErrorCode = HAL_UART_ERROR_NONE; huart->gState = HAL_UART_STATE_BUSY_TX;
-
使能当数据寄存器为空时,发生中断
__HAL_UART_ENABLE_IT(huart, UART_IT_TXE);
串口中断接收函数 HAL_UART_Receive_IT
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
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);
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
-
对一些状态进行初始化,解释如下
huart->pRxBuffPtr = pData; //指针指向接收数据缓冲区,我们传送进来的数组首地址 huart->RxXferSize = Size; //要接收数据的个数 huart->RxXferCount = Size; //用来计数,未接收数据的个数,接收一个数据就自减,减为0时,接收完成 huart->ErrorCode = HAL_UART_ERROR_NONE; huart->RxState = HAL_UART_STATE_BUSY_RX;
-
使能 校验错误中断,帧错误,噪声错误中断,数据溢出中断
__HAL_UART_ENABLE_IT(huart, UART_IT_PE); __HAL_UART_ENABLE_IT(huart, UART_IT_ERR); __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
后续还会继续分享串口的其他基础知识和使用,感兴趣的小伙伴记得关注我!
-----------------------------------------------结束--------------------------------------------------------
文章有价值,请各位看官点个赞,关注我或者点右边打个赏吧