为什么不总是使用 DMA 来支持 STM32 上的 UART 中断?

电器工程 UART DMA stm32cubemx stm32f103c8t6
2022-01-20 17:37:42

上个月我花了很多时间让 UART(用于 MIDI)使用中断与 STM(STM32F103C8T6)一起工作,但没有取得很大成功。

然而,今天晚上使用 DMA,它运行得非常快。

既然我读到 DMA 更快并且减轻了 CPU 的负担,为什么不总是使用 DMA 来支持中断呢?特别是因为在 STM32 上似乎存在相当多的问题。

我正在使用 STM32CubeMx/HAL。

4个回答

虽然 DMA 减轻了 CPU 的负担,因此可以减少在同一内核上运行的其他中断驱动的应用程序的延迟,但也有与之相关的成本:

  • DMA 通道数量有限,并且这些通道与不同外设交互的方式也受到限制。同一通道上的另一个外设可能更适合 DMA 使用。

    例如,如果您每 5 毫秒进行一次批量 I2C 传输,这似乎比偶尔到达 UART2 的调试命令更适合 DMA。

  • 设置和维护 DMA 本身就是一项成本(通常,设置 DMA 被认为比设置正常的按字符中断驱动的传输更复杂,这是由于内存管理、涉及更多外围设备、DMA 使用中断本身以及您需要解析 DMA 之外的前几个字符的可能性无论如何,请参见下文。)

  • DMA 可能会使用额外的功率,因为​​它是需要时钟的内核的另一个域。另一方面,如果内核支持,您可以在 DMA 传输过程中暂停 CPU。

  • DMA 需要使用内存缓冲区(除非您正在执行外设到外设 DMA),因此存在一些与之相关的内存成本。

    (在使用每个字符的中断时,内存成本可能存在,但如果消息在中断内立即被解释,它也可能会小得多或根本消失。)

  • DMA 会产生延迟,因为 CPU 仅在传输完成/半完成时才会收到通知(请参阅其他答案)。

  • 除了将数据流入/流出环形缓冲区时,您需要提前知道您将接收/发送多少数据。

    • 这可能意味着需要使用按字符中断处理消息的第一个字符:例如,当与 XBee 交互时,您首先读取数据包类型和大小,然后触发 DMA 传输到分配的缓冲区。

    • 对于其他协议,如果它们只使用消息结束分隔符,这可能根本不可能:例如,'\n'用作分隔符的基于文本的协议。(除非 DMA 外设支持字符匹配。)

如您所见,这里需要考虑很多权衡。有些与硬件限制有关(通道数量、与其他外围设备的冲突、字符匹配),有些基于使用的协议(分隔符、已知长度、内存缓冲区)。

为了补充一些轶事证据,我在一个爱好项目中面临所有这些权衡,该项目使用了许多不同的外围设备和非常不同的协议。有一些权衡取舍,主要基于“我要传输多少数据以及我要多久传输一次?”的问题。这基本上可以让您粗略估计简单的中断驱动传输对 CPU 的影响。因此,我将上述每 5 毫秒的 I2C 传输优先于使用相同 DMA 通道的每几秒的 UART 传输。另一方面,另一个 UART 传输发生得更频繁,数据更多,优先于另一个很少发生的 I2C 传输。这都是取舍。

当然,使用 DMA 也有优势,但这不是您所要求的。

使用 DMA 通常意味着您不再对每个字符进行中断,而是仅在接收(或传输)字符的“缓冲区满”之后。这增加了处理这些字符的延迟——直到接收到缓冲区中的最后一个字符后才处理第一个字符。

这种延迟可能是一件坏事,尤其是在 MIDI 等对延迟敏感的应用程序中,这里和那里的几毫秒可能会增加现场表演的严重可玩性问题。

DMA 不能替代中断——它们通常一起使用!例如,如果您使用 DMA 通过 UART 发送数据,您仍然需要一个中断来告诉您发送何时完成。

使用 DMA 引入了一些有趣的问题和挑战,超出了 UART 外设使用的所有其他考虑因素。我给你举几个例子:假设你的 uC 和其他设备一起坐在 RS485(或其他)总线上。总线上有很多消息,有些是针对您的 uC 的,有些则不是。另外假设这些总线邻居都使用不同的数据协议,这意味着消息长度不同。

只有在使用 DMA 时才会出现的一些问题是:

  • 我什么时候打断?
    • DMA 仅在传输了预设数量的数据时才真正喜欢中断。
    • 如果您从未收到足够的数据来触发 DMA 中断,您会怎么做?
  • 如果 DMA 中断时您只收到部分消息怎么办?
  • 您的 RX 缓冲区是什么样的?它们是线性的还是圆形的?
    • DMA 可以是一个不守规矩的循环缓冲区参与者,因为它只服从地址边界,但在循环缓冲区系统中吹过其他指针没有问题。

无论如何,只是供人思考。