在使用HAL库的过程中发现在串口中断的回显功能(将串口接收到的数据再发送出来),总是执行几次后程序卡死,无法正常回显。经过一系列查找后发现原因是STM32 HAL库在处理接收的时候会锁一下串口一会,导致变成某个短时间的“半双工”。尝试网上的教程太过繁琐,而且cubemx生成代码后又会将修改部分覆盖,很是麻烦。知道出现问题的原因后直接参考标准库在usart.c和usart.h中添加以下代码,完美解决问题。
usart.h中添加如下代码
/* USER CODE BEGIN Includes */
#include "stm32f1xx_hal.h"
#include "stdio.h"
/*宏定义*/
#define USART_IT_PE ((uint16_t)0x0028)
#define USART_IT_TXE ((uint16_t)0x0727)
#define USART_IT_TC ((uint16_t)0x0626)
#define USART_IT_RXNE ((uint16_t)0x0525)
#define USART_IT_IDLE ((uint16_t)0x0424)
#define USART_IT_LBD ((uint16_t)0x0846)
#define USART_IT_CTS ((uint16_t)0x096A)
#define USART_IT_ERR ((uint16_t)0x0060)
#define USART_IT_ORE ((uint16_t)0x0360)
#define USART_IT_NE ((uint16_t)0x0260)
#define USART_IT_FE ((uint16_t)0x0160)
#define IT_Mask ((uint16_t)0x001F) /*!< USART Interrupt Mask */
/* USER CODE END Includes */
/* USER CODE BEGIN Prototypes */
/*函数申明*/
void USART_SendData(USART_TypeDef* USARTx, uint8_t *pData);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
/* USER CODE END Prototypes */
在usart.c中复制标准库函数原型
/**
* @brief Transmits single data through the USARTx peripheral.
* @param USARTx: Select the USART or the UART peripheral.
* This parameter can be one of the following values:
* USART1, USART2, USART3, UART4 or UART5.
* @param Data: the data to transmit.
* @retval None
*/
void USART_SendData(USART_TypeDef* USARTx, uint8_t *pData)
{
/* Transmit Data */
USARTx->DR = *pData;
while (!(USARTx->SR & UART_FLAG_TC)){};
}
/**
* 函数功能: 重定向c库函数printf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t *)&ch);
return ch;
}
/**
* 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fgetc(FILE *f)
{
uint8_t ch = 0;
USART_ReceiveData(USART1);
return ch;
}
/**
* @brief Checks whether the specified USART interrupt has occurred or not.
* @param USARTx: Select the USART or the UART peripheral.
* This parameter can be one of the following values:
* USART1, USART2, USART3, UART4 or UART5.
* @param USART_IT: specifies the USART interrupt source to check.
* This parameter can be one of the following values:
* @arg USART_IT_CTS: CTS change interrupt (not available for UART4 and UART5)
* @arg USART_IT_LBD: LIN Break detection interrupt
* @arg USART_IT_TXE: Tansmit Data Register empty interrupt
* @arg USART_IT_TC: Transmission complete interrupt
* @arg USART_IT_RXNE: Receive Data register not empty interrupt
* @arg USART_IT_IDLE: Idle line detection interrupt
* @arg USART_IT_ORE: OverRun Error interrupt
* @arg USART_IT_NE: Noise Error interrupt
* @arg USART_IT_FE: Framing Error interrupt
* @arg USART_IT_PE: Parity Error interrupt
* @retval The new state of USART_IT (SET or RESET).
*/
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
{
uint32_t bitpos = 0x00, itmask = 0x00, usartreg = 0x00;
ITStatus bitstatus = RESET;
/* Check the parameters */
assert_param(IS_USART_ALL_PERIPH(USARTx));
assert_param(IS_USART_GET_IT(USART_IT));
/* The CTS interrupt is not available for UART4 and UART5 */
if (USART_IT == UART_FLAG_CTS)
{
assert_param(IS_USART_123_PERIPH(USARTx));
}
/* Get the USART register index */
usartreg = (((uint8_t)USART_IT) >> 0x05);
/* Get the interrupt position */
itmask = USART_IT & IT_Mask;
itmask = (uint32_t)0x01 << itmask;
if (usartreg == 0x01) /* The IT is in CR1 register */
{
itmask &= USARTx->CR1;
}
else if (usartreg == 0x02) /* The IT is in CR2 register */
{
itmask &= USARTx->CR2;
}
else /* The IT is in CR3 register */
{
itmask &= USARTx->CR3;
}
bitpos = USART_IT >> 0x08;
bitpos = (uint32_t)0x01 << bitpos;
bitpos &= USARTx->SR;
if ((itmask != (uint16_t)RESET)&&(bitpos != (uint16_t)RESET))
{
bitstatus = SET;
}
else
{
bitstatus = RESET;
}
return bitstatus;
}
/**
* @brief Returns the most recent received data by the USARTx peripheral.
* @param USARTx: Select the USART or the UART peripheral.
* This parameter can be one of the following values:
* USART1, USART2, USART3, UART4 or UART5.
* @retval The received data.
*/
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
{
/* Check the parameters */
assert_param(IS_USART_ALL_PERIPH(USARTx));
/* Receive Data */
return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);
}
/**
* @brief Enables or disables the specified USART interrupts.
* @param USARTx: Select the USART or the UART peripheral.
* This parameter can be one of the following values:
* USART1, USART2, USART3, UART4 or UART5.
* @param USART_IT: specifies the USART interrupt sources to be enabled or disabled.
* This parameter can be one of the following values:
* @arg USART_IT_CTS: CTS change interrupt (not available for UART4 and UART5)
* @arg USART_IT_LBD: LIN Break detection interrupt
* @arg USART_IT_TXE: Transmit Data Register empty interrupt
* @arg USART_IT_TC: Transmission complete interrupt
* @arg USART_IT_RXNE: Receive Data register not empty interrupt
* @arg USART_IT_IDLE: Idle line detection interrupt
* @arg USART_IT_PE: Parity Error interrupt
* @arg USART_IT_ERR: Error interrupt(Frame error, noise error, overrun error)
* @param NewState: new state of the specified USARTx interrupts.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
{
uint32_t usartreg = 0x00, itpos = 0x00, itmask = 0x00;
uint32_t usartxbase = 0x00;
/* Check the parameters */
assert_param(IS_USART_ALL_PERIPH(USARTx));
assert_param(IS_USART_CONFIG_IT(USART_IT));
assert_param(IS_FUNCTIONAL_STATE(NewState));
/* The CTS interrupt is not available for UART4 and UART5 */
if (USART_IT == UART_FLAG_CTS)
{
assert_param(IS_USART_123_PERIPH(USARTx));
}
usartxbase = (uint32_t)USARTx;
/* Get the USART register index */
usartreg = (((uint8_t)USART_IT) >> 0x05);
/* Get the interrupt position */
itpos = USART_IT & IT_Mask;
itmask = (((uint32_t)0x01) << itpos);
if (usartreg == 0x01) /* The IT is in CR1 register */
{
usartxbase += 0x0C;
}
else if (usartreg == 0x02) /* The IT is in CR2 register */
{
usartxbase += 0x10;
}
else /* The IT is in CR3 register */
{
usartxbase += 0x14;
}
if (NewState != DISABLE)
{
*(__IO uint32_t*)usartxbase |= itmask;
}
else
{
*(__IO uint32_t*)usartxbase &= ~itmask;
}
}
/* USER CODE END 1 */
串口中断函数如下
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
char c;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
/* 把数据读出来存入环形缓冲区 */
c = USART_ReceiveData(USART1);
}
printf("%c",c);
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1); //此处不用修改,即使删除了,cubemx生成代码后又会将此处覆盖
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
添加完上述代码后,在串口初始化后使能相应的中断即可
测试数据如下