两个USART中断出现溢出错误

9
在STM32F2上使用两个运行速度为115200波特率的USART,一个用于与无线电模块通信,另一个用于从PC接收串行数据。时钟速度为120MHz。
当同时从两个USART接收数据时,可能会在其中一个USART上出现溢出错误。进行一些简单的笔算,应该有足够的时间来处理两个USART的数据,因为中断只是简单地将字节复制到循环缓冲区中。
从理论和测量结果来看,将字节推入缓冲区的中断代码应该/确实以2-4µS的顺序运行,在115200波特率下,我们有大约70us的时间来处理每个字符。
为什么我们会看到偶尔在其中一个USART上出现ORE?
更新-附加信息:
  1. 我们的代码中没有其他ISR在此时触发。
  2. 我们正在运行Keil RTX,并配置systick中断每10mS触发一次。
  3. 我们此时没有禁用任何中断。
  4. 根据这本书(Cortex-M处理器系列的设计指南),中断延迟约为12个周期(并不致命)
考虑到以上所有因素,70us至少比我们清除中断所需的时间多十倍,因此我不确定是否很容易解释。我应该得出结论,一定还有其他因素我没有考虑吗?
MDK-ARM版本为4.70
RTOS使用systick中断,因此无法计时,其他ISR每个字节需要2-3µS。

1
你没有提供足够的信息来解释为什么你会出现溢出的情况。可能的原因有:在其他地方禁用了中断?优先级更高的中断处理程序速度太慢?代码中存在错误?但是由于缺乏足够的信息,无法确定具体原因。 - janm
感谢您的评论,我已经在问题中添加了更多信息,不确定我还能提供什么其他信息。 - Jonathan Moreton
RTX的哪个版本(或ARM-MDK)? Cortex-M3的早期版本存在错误,并且不完全支持中断优先级分组。这个问题可能没有代码就无法回答。 - Clifford
1
我不会轻易放弃未解决的问题,因为症状可能会在以后的其他地方出现。然而,在我的最近几个项目中,我总是使用DMA来创建循环缓冲区,该缓冲区存储从USART接收到的数据。实际硬件(DMA控制器)不会错过截止日期(除非它也超载了,但除非您将其用于其他用途,否则以那种速率使用2个USART应该很容易)。 - swineone
你可能不能完全禁用中断,但是你是否将 PRIMASK 设置得足够高以禁用你的 UART 中断(可能是无意中)?我知道 FreeRTOS 在 CM3 上会这样做,以便让你仍然拥有不使用操作系统的“快速”中断,但它们需要设置在特定优先级以上。 - rjp
显示剩余3条评论
3个回答

3
几个月前我在 Cortex M4 (SAM4S) 上遇到了类似的问题。我有一个函数,基于定时器中断每秒调用 100 次。
同时,我配置了一个 UART 在字符接收时触发中断。预期通过 UART 接收的数据为 64 字节长的数据包,并且每个字符都触发中断,导致延迟很高,导致我的 100 Hz 更新函数以大约 20 Hz 的速度执行。在这个特定的 120 MHz 处理器上,100 Hz 是相对较慢的,但是每个字符中断会导致巨大的延迟。
我决定将 UART 配置为使用 PDC(外设 DMA 控制器),并且我的问题立即消失了。
DMA 允许 UART 在缓冲区未满之前将数据存储在内存中,从而无需中断处理器,从而减少了大量开销。
在我的情况下,我告诉 PDC 将 UART 数据存储到一个缓冲区(字节数组)中,并指定长度。当 UART 通过 PDC 填充缓冲区时,PDC 发出中断。
在 PDC ISR:
  1. 给 PDC 新的空缓冲区
  2. 重新启动 UART PDC(因此可以在 isr 中做其他事情时收集数据)
  3. 将完整的缓冲区复制到环形缓冲区
  4. 退出 ISR
正如 swineone 建议的那样,实现 DMA,你会喜欢上它。

0

我曾经遇到过类似的问题。简单的解决方法是将过采样改为8位,这会使USART时钟更精确。并且要明智地选择你的MCU时钟!

huart1.Init.OverSampling = UART_OVERSAMPLING_8;

此外,添加USART错误处理程序和机制以检查您的数据是否有效,例如CRC16。以下是适用于STM32F0xx系列的示例,我假设在整个系列中它应该非常类似。
void UART_flush(void) {
  // Flush UART RX buffer if RXNE is set
  if READ_BIT(huart1.Instance->ISR, USART_ISR_RXNE) {
    SET_BIT(huart1.Instance->RQR, UART_RXDATA_FLUSH_REQUEST);
  }

  // Not available on F030xx devices!
  // SET_BIT(huart1.Instance->RQR, UART_TXDATA_FLUSH_REQUEST);

  // Clear All Errors (if needed)
  if (READ_BIT(huart1.Instance->ISR, USART_ISR_ORE | USART_ISR_FE | USART_ISR_NE)) {
    SET_BIT(huart1.Instance->ICR, USART_ICR_ORECF | USART_ICR_FECF | USART_ICR_NCF);
  }
}

// USART Error Handler
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {
  if(huart->Instance==USART1) {
    // See if we have any errors
    if (READ_BIT(huart1.Instance->ISR, USART_ISR_ORE | USART_ISR_FE | USART_ISR_NE | USART_ISR_RXNE)) {
        // Flush errors
        UART_flush();

        // Raise Error Handler
        _Error_Handler(__FILE__, __LINE__);
    }
  }
}

使用DMA可能也有帮助。我的问题与USART时钟容差有关,即使实现了DMA也可能导致溢出错误。由于这是USART硬件问题。无论如何,希望这能帮助到某个人!干杯!


0

最近我遇到了这个问题,所以我实现了一个UART_ErrorCallback函数,它之前还没有被实现(只有_weak版本)。 就像这样:

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
     if(huart == &huart1)
     {
         HAL_UART_DeInit(&huart1);
         MX_USART1_UART_Init(); //my initialization code
      ...

这样就解决了溢出问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接