上个月我花了很多时间让 UART(用于 MIDI)使用中断与 STM(STM32F103C8T6)一起工作,但没有取得很大成功。
然而,今天晚上使用 DMA,它运行得非常快。
既然我读到 DMA 更快并且减轻了 CPU 的负担,为什么不总是使用 DMA 来支持中断呢?特别是因为在 STM32 上似乎存在相当多的问题。
我正在使用 STM32CubeMx/HAL。
上个月我花了很多时间让 UART(用于 MIDI)使用中断与 STM(STM32F103C8T6)一起工作,但没有取得很大成功。
然而,今天晚上使用 DMA,它运行得非常快。
既然我读到 DMA 更快并且减轻了 CPU 的负担,为什么不总是使用 DMA 来支持中断呢?特别是因为在 STM32 上似乎存在相当多的问题。
我正在使用 STM32CubeMx/HAL。
虽然 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 时才会出现的一些问题是:
无论如何,只是供人思考。