先分析以下源码,记录一下学习成果,如果你急切想解决标题所述问题,可以直接跳到文本。
如果你在HAL库下调试中断接收代码,那么下面这个API函数
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
你肯定不会感到陌生。此API用于接收指定长度字符串,当接收到Size个字符后,会触发中断,调用回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)`
它是一个_weak类型的函数,我们重写该函数执行所需功能即可。
先来分析一下中断接收函数(看注释)
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;
}
/*!!!!!!!!!!!!!!!!!!!!*/
//调用时上锁,有操作系统基础的童鞋可能比较容易理解锁的含义
//通俗点就是,现在huart这个串口已经上锁(被占用),其它外设/设备想要使用它时,需要先等锁huart这个锁被解开
__HAL_LOCK(huart);
/* Set Reception type to Standard reception */
huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;
if (!(IS_LPUART_INSTANCE(huart->Instance)))
{
/* Check that USART RTOEN bit is set */
if (READ_BIT(huart->Instance->CR2, USART_CR2_RTOEN) != 0U)
{
/* Enable the UART Receiver Timeout Interrupt */
SET_BIT(huart->Instance->CR1, USART_CR1_RTOIE);
}
}
/*!!!!!!!!!!!!!!!!!!!!!*/
/*调用UART_Start_Receive_IT(huart, pData, Size)*/
return (UART_Start_Receive_IT(huart, pData, Size));
}
else
{
return HAL_BUSY;
}
}
接着分析UART_Start_Receive_IT(huart, pData, Size),该函数引用了
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)的参数
HAL_StatusTypeDef UART_Start_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/*设置一些参数*/
huart->pRxBuffPtr = pData;/*串口接收存储目标地址*/
huart->RxXferSize = Size; /*串口接收字符的数目*/
huart->RxXferCount = Size; /*用于记录还剩余多少个字符要接收,当==0时会触发中断*/
huart->RxISR = NULL; /*中断回调函数*/
/* Computation of UART mask to apply to RDR register */
UART_MASK_COMPUTATION(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
/*使能错误中断,发送串口接收错误时(溢出等),会调用相应回调函数*/
SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Configure Rx interrupt processing */
/*设置相应的中断处理函数*/
if ((huart->FifoMode == UART_FIFOMODE_ENABLE) && (Size >= huart->NbRxDataToProcess))
{
/* Set the Rx ISR function pointer according to the data word length */
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
/*16位字长接收函数*/
huart->RxISR = UART_RxISR_16BIT_FIFOEN;
}
else
{
/*8位字长接收函数*/
huart->RxISR = UART_RxISR_8BIT_FIFOEN;
}
/*!!!!!!!!!!!!!!!*/
//解锁,UART_Start_Receive_IT()中对它上了锁
__HAL_UNLOCK(huart);
/* Enable the UART Parity Error interrupt and RX FIFO Threshold interrupt */
SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
SET_BIT(huart->Instance->CR3, USART_CR3_RXFTIE);
}
//大同小异
else
{
/* Set the Rx ISR function pointer according to the data word length */
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
huart->RxISR = UART_RxISR_16BIT;
}
else
{
/*!!!!!!!!!*/
/*记住这一句!!,等下回用到*/
huart->RxISR = UART_RxISR_8BIT;
}
__HAL_UNLOCK(huart);
/* Enable the UART Parity Error interrupt and Data Register Not Empty interrupt */
SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE_RXFNEIE);
}
return HAL_OK;
}
以上函数设置了中断使能,当发生中断时,调用USART1_IRQHandler()
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/*!!!!!!!!!!!!!!!*/
/*BUG1*/
//串口中断里面,不能使用printf(),HAL_UART_Transmit()等函数,否则就会导致中断接收异常,或者卡死!!!!!!!
// printf("USART1_IRQHandler\r\n");/*注释掉*/
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* HAL_UART_IRQHandler(&huart1);串口中断公共函数已经失能中断*/
/*因此要重新开启,为下一次接收做准备*/
HAL_UART_Receive_IT(&huart1, (uint8_t *)&ch,1);
/* USER CODE END USART1_IRQn 1 */
}
void USART1_IRQHandler(void)
内部又调用了 HAL_UART_IRQHandler(&huart1);—>主要功能是清除一些中断标志位,和调用中断回调函数。
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
//printf("HAL_UART_IRQHandler\r\n");
uint32_t isrflags = READ_REG(huart->Instance->ISR);
uint32_t cr1its = READ_REG(huart->Instance->CR1);
uint32_t cr3its = READ_REG(huart->Instance->CR3);
uint32_t errorflags;
uint32_t errorcode;
/* If no error occurs */
/*没有发生错误*/
errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE | USART_ISR_RTOF));
if (errorflags == 0U)
{
/* UART in mode Receiver ---------------------------------------------------*/
if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)
&& (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)
|| ((cr3its & USART_CR3_RXFTIE) != 0U)))
{
if (huart->RxISR != NULL)
{
/*调用UART_Start_Receive_IT()时,已经设置了对应的RxISR函数指针,如下注释所示*/
/* huart->RxISR = UART_RxISR_8BIT;
*/
/*调用该函数,接收定长数据,清除中断标志位,并调用回调函数*/
huart->RxISR(huart);//看以下注释
}
return;
}
}
/* If some errors occur */
if ((errorflags != 0U)
&& ((((cr3its & (USART_CR3_RXFTIE | USART_CR3_EIE)) != 0U)
|| ((cr1its & (USART_CR1_RXNEIE_RXFNEIE | USART_CR1_PEIE | USART_CR1_RTOIE)) != 0U))))
{
/*巴拉巴拉,干一堆其它事情,详细可以研究源码*/
.........
}
}
static void UART_RxISR_8BIT(UART_HandleTypeDef *huart)
{
uint16_t uhMask = huart->Mask;
uint16_t uhdata;
/* Check that a Rx process is ongoing */
if (huart->RxState == HAL_UART_STATE_BUSY_RX)
{
/*从RDR寄存器中接收一个字节数据*/
uhdata = (uint16_t) READ_REG(huart->Instance->RDR);
/*保存到目标地址中*/
*huart->pRxBuffPtr = (uint8_t)(uhdata & (uint8_t)uhMask);
/*地址++,为下一次接收做准备*/
huart->pRxBuffPtr++;
/*剩余字符数--,当==0时使能中断*/
huart->RxXferCount--;
if (huart->RxXferCount == 0U)
{
/* Disable the UART Parity Error Interrupt and RXNE interrupts */
/*!!!!!!!!!!!!!!!!!!!!!!!!!*/
/*关闭UART奇偶校验错误中断和RXNE中断*/
CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE_RXFNEIE | USART_CR1_PEIE));
/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
/*禁用UART错误中断(帧错误,噪声错误,溢出错误)*/
CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Rx process is completed, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
/* Clear RxISR function pointer */
/*清空*/
huart->RxISR = NULL;
/* Check current reception Mode :
If Reception till IDLE event has been selected : */
if (huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)
{
/* Disable IDLE interrupt */
CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*Call registered Rx Event callback*/
huart->RxEventCallback(huart, huart->RxXferSize);
#else
/*Call legacy weak Rx Event callback*/
HAL_UARTEx_RxEventCallback(huart, huart->RxXferSize);
#endif /* (USE_HAL_UART_REGISTER_CALLBACKS) */
}
else
{
/* Standard reception API called */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*Call registered Rx complete callback*/
/*!!!!!!!!!!!!!!!!!!!!!!*/
/*_weak函数被重写,所以调用RxCpltCallback()回调函数*/
huart->RxCpltCallback(huart);
#else
/*Call legacy weak Rx complete callback*/
HAL_UART_RxCpltCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
}
huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;
}
}
else
{
/* Clear RXNE interrupt flag */
__HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);
}
}
HAL库的串口中断接收处理逻辑有点复杂,函数调用太多
回到标题所述问题:
产生中断接收出错,或者只能接收到一两个字符的情况,主要是原因是,在中断函数中(包括:HAL_UART_RxCpltCallback(),void USART1_IRQHandler(void)
, HAL_UART_IRQHandler(&huart1);
等中断相关函数),调用了printf(),HAL_UART_Transmit()等串口发送函数,可能你也是和我一样想在中断里打印一些信息,调试代码,糟心的是恰恰进入了死胡同!!!!
分析HAL_UART_Transmit()函数的源码就会发现,在串口发送函数中也会上锁,__HAL_LOCK(huart);从而导致程序卡死或者发生错误!!。
因此只需要把这些地方的串口打印函数全部注释掉即可,或者是将有可能在中断接收期间串口发送的函数注释掉。
卡了很久,记录下来分享一下
如果对你有帮助,点个赞让我知道一下。
如有错漏,请大佬指正