问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

STM32串口中断中使用printf打印遇到的问题及分析

创作时间:
作者:
@小白创作中心

STM32串口中断中使用printf打印遇到的问题及分析

引用
CSDN
1.
https://blog.csdn.net/xieliru/article/details/141572115

在STM32单片机的串口中断处理中,使用printf函数进行数据打印可能会导致数据接收异常。本文详细分析了这一问题的原因,并提供了具体的解决方案和改进建议。

问题描述

在串口中断函数中使用printf函数打印接收到的数据时,发现只收到了一个字节的数据,而预期应该是16字节数据。以下是相关的串口中断代码:

void DEBUG_USART_IRQHandler(void) //里面不要用printf函数,会导致每次只收到一个字节
{
    HAL_UART_Receive(&UartHandle, (uint8_t *)UART1_RxBuff, 2, 400); //收不到这么多长度就会等待足额时间,如果收到这个数量数据就直接返回;但数据长度超过了也不好,比如长度设为5会导致收到的是后面的5个byte
    __HAL_UART_ENABLE_IT(&UartHandle,UART_IT_RXNE); //必须有,否则就会只收到一次
    printf("%d %d %d\r\n", UART1_RxBuff[0], UART1_RxBuff[1],UART1_RxBuff[2]);
}

结论与快速解决方法

不要在单片机中断函数中加入printf函数,如果加入会导致不可预知的错误。解决方法是将printf函数放到中断函数外面。

在嵌入式软件及驱动开发中,需要牢记一条铁律:中断服务程序里不能调用printf和malloc函数。

原因分析

  1. 阻塞行为
  • printf函数通常是阻塞的,特别是在使用newlib-nano等标准C库时。在中断服务例程中使用printf可能会导致中断服务时间过长,从而阻塞其他中断或主循环的执行。
  • 如果printf的缓冲区没有足够快地被清空(例如,通过UART发送),它可能会填满并导致进一步的阻塞。
  1. 重入性和中断安全
  • printf及其相关的标准C库函数可能不是重入安全的,这意味着它们不能安全地在中断服务例程中调用,特别是当它们访问共享资源(如UART)时。这可能导致数据损坏或不可预测的行为。
  1. 优先级反转
  • 如果printf在中断服务例程中执行,并且它涉及比当前中断更高优先级的任务(例如,通过操作系统任务调度),则可能导致优先级反转,这会影响系统的实时性能。
  1. UART缓冲区冲突
  • 如果printf和串口中断都使用相同的UART缓冲区(尽管这通常不是printf的默认行为,但取决于你的配置),则可能会发生冲突,导致数据丢失或覆盖。

改进建议

  1. 避免在ISR中使用printf
  • 将printf调用移出ISR。可以使用一个简单的环形缓冲区或FIFO来在ISR中存储接收到的数据,并在主循环或另一个低优先级任务中处理这些数据。
  1. 使用HAL库函数
  • 如果需要在ISR中处理接收到的数据,请使用HAL库提供的非阻塞或中断驱动的UART接收函数,如HAL_UART_Receive_IT。
  1. 实现自定义的日志系统
  • 如果需要记录调试信息,可以实现一个自定义的、轻量级的日志系统,该系统使用环形缓冲区或FIFO来存储日志消息,并在主循环中异步地将其发送到UART。
  1. 使用DMA进行UART传输
  • 如果可能,使用DMA(直接内存访问)来加速UART的发送和接收过程。这可以显著减少CPU的负载,并允许在传输过程中执行其他任务。
  1. 检查中断优先级
  • 确保UART中断的优先级设置得当,以避免优先级反转或其他中断冲突。
  1. 调试和测试
  • 使用调试工具(如JTAG/SWD调试器)和串口调试助手来监视UART通信,并验证数据的完整性和正确性。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号