在 STM32 上使用串口接收字符串时,如果多次进入中断,通常是由于 接收中断(RXNE)频繁触发 导致的。为了避免数据丢失或处理混乱,可以采用 缓冲区 + DMA/中断管理 的方式。以下是几种常见解决方案:
启用串口接收中断
在初始化时开启接收中断(HAL_UART_Receive_IT()),并设置一个缓冲区。
c
uint8_t rx_buf[128]; // 接收缓冲区 HAL_UART_Receive_IT(&huart1, rx_buf, 1); // 每次接收 1 字节触发中断
在中断回调函数中处理数据
重写 HAL_UART_RxCpltCallback(),将数据存入环形缓冲区(FIFO)并重新启动接收。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
ring_buffer_push(rx_buf); // 存入自定义环形缓冲区
HAL_UART_Receive_IT(&huart1, rx_buf, 1); // 重新启用接收
} }在主循环中处理完整字符串
检查环形缓冲区是否包含完整字符串(如以 \n 结尾),然后处理。
配置 DMA 接收
使用 DMA 自动接收数据到缓冲区,避免频繁中断。
c
uint8_t dma_buf[256]; HAL_UART_Receive_DMA(&huart1, dma_buf, sizeof(dma_buf));
启用空闲中断(IDLE)
在串口初始化时启用空闲中断:
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
在中断中处理数据
当总线空闲(IDLE)时,表示一帧数据接收完成:
void USART1_IRQHandler(void) { if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart1); uint16_t len = sizeof(dma_buf) - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); process_data(dma_buf, len); // 处理数据 HAL_UART_Receive_DMA(&huart1, dma_buf, sizeof(dma_buf)); // 重启 DMA } HAL_UART_IRQHandler(&huart1); }
如果使用 LL 库,可以直接操作寄存器:
void USART1_IRQHandler(void) {
if (LL_USART_IsActiveFlag_RXNE(USART1)) {
uint8_t data = LL_USART_ReceiveData8(USART1);
ring_buffer_push(data); // 存入缓冲区
} }数据覆盖
如果中断处理太慢,新数据会覆盖旧数据。解决方法:增大缓冲区或使用 DMA。
字符串截断
通过超时或特定字符(如 \n)判断字符串结束。
性能优化
DMA + 空闲中断是方案,适合高速数据流。
uint8_t rx_data[256]; uint32_t rx_len = 0;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
rx_len++;
HAL_UART_Receive_IT(&huart1, &rx_data[rx_len], 1);
} }
void USART1_IRQHandler(void) {
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
process_complete_string(rx_data, rx_len);
rx_len = 0;
}
HAL_UART_IRQHandler(&huart1); }
免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。