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


9

上个月,我花了大量时间让UART(用于MIDI)与使用中断的STM(STM32F103C8T6)一起使用,但收效甚微。

但是,今天晚上使用DMA的速度非常快。

由于据我所知,DMA速度更快并且可以减轻CPU负担,为什么不总是使用DMA来支持中断呢?特别是由于在STM32上似乎存在很多问题。

我正在使用STM32CubeMx / HAL。


2
为什么不?那要么是一种观点问题,要么是寻找一种可能的技术原因的猜测,或者是以太宽泛的方式提出的,因此不是这里的问题。举一个随机的例子,DMA在声明数据时将意味着更多的延迟,尤其是因为除非您允许它收集多个字符,否则您没有任何实际的好处。通常这可能很好,有时可能不是。
克里斯·斯特拉顿

6
如果让中断工作花了数周的时间,那是因为您以错误的方式处理任务;使DMA正常工作可能会花费更长的时间-这实际上是一项更复杂的任务,因此,与较简单的任务相比,更复杂的任务的表面上的难易程度大概取决于您用于指导每个任务的资源,而不是机制本身。
克里斯·斯特拉顿

5
永远不要假设dma释放了CPU,有时是的,cpu继续运行,有时没有冻结处理器来保持dma引擎的总线。用arm实现来做到这一点很简单,所以不能仅仅说所有的arm都是这种方式,而所有的x86都是那样或那样,这不是那么简单,您必须始终检查系统设计并可能要进行一些改动。您拥有的芯片很可能会释放臂核,这只是对dma的一种评论。就您的问题而言,您无法跟上是没有道理的,如果您不能进行轮询,则dma + int可能是完整的解决方案。
old_timer

5
在STM32F串行端口上,中断非常简单。您为什么不对代码发布问题,以便我们中的一些人可以尝试找出问题所在?在不了解潜在问题是什么之前,对其进行破解永远不是一个好主意。
乔恩(Jon)

7
以我(并非如此)的愚见,这是使用可怕的,肿的Cube的缺点之一。从头开始编写软件,您将确切地了解UART的工作原理(因为必须这样做),您将更好地了解外设,从长远来看,它将节省大量时间。
DiBosco '17

Answers:


24

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

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

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

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

  • DMA可能会使用额外的功率,因为它是内核的另一个域,需要时钟控制。另一方面,如果内核支持,则可以在进行DMA传输时挂起CPU。

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

    (使用逐个字符的中断时,内存开销可能也会存在,但是如果立即在中断内部解释消息,则内存开销可能会更小或消失。)

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

  • 除了将数据流传输到环形缓冲区中或从环形缓冲区中传输数据之外,您需要事先知道将要接收/发送多少数据

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

    • 对于其他协议,如果它们仅使用消息结尾定界符,则根​​本不可能实现:例如,'\n'用作定界符的基于文本的协议。(除非DMA外设支持字符匹配。)

如您所见,这里有很多折衷考虑。一些与硬件限制有关(通道数,与其他外设冲突,字符匹配),一些与所使用的协议有关(分隔符,已知长度,内存缓冲区)。

为了增加一些轶事证据,我在一个业余项目中面对了所有这些折衷,该项目使用了许多不同的外围设备,并且协议非常不同。需要做出一些权衡,主要是基于“我要传输多少数据,我要多久执行一次?”这一问题。本质上,这使您可以粗略估计简单的中断驱动的传输对CPU的影响。因此,相对于每隔几秒钟使用相同DMA通道的UART传输,我每5ms优先进行上述I2C传输。另一个UART传输发生的频率更高,而具有更多数据,则优先于另一个I2C传输,这种传输很少发生。这都是权衡。

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


感谢您的详细回答。MIDI将是最关键的部分,因此我认为DMA适合它(尽管速度很低:31250波特)。我有足够的DMA通道,稍后将在使用4个USART时使用另一个STM32。我不需要挂起CPU,因为它将具有5V USB电源,并且我需要在消息之间进行处理(以在主循环中处理消息)。我有一个256字节的读取和256字节的发送缓冲区。如有需要,我可以稍后增加。STM32f103c8t6具有20 KB RAM,最终的STM I将使用192 KB。
Michel Keijzers '17

而且您给我一个很好的想法,如何进行改进。到目前为止,我总是读取1个字节,并在收到完整(MIDI)消息时不断进行检查。但是我可以读取第一个字节,并且主要取决于该大小,其余部分可以要求。这花了我另一个小的缓冲区,但是没关系。
Michel Keijzers

用DMA读取单个字节效率很低。为了降低等待时间和提高效率,最好使用每个字符的中断,直到知道大小为止,然后再切换到DMA。
乔纳斯·谢弗(JonasSchäfer)

好吧,我在使用中断(没有DMA)时遇到很多问题,我想我将使用1字节DMA接收,然后我知道我期望多少字节并发出DMA请求以获取更多字节。
Michel Keijzers '17

6
这可能是一个错误-您应该在没有 DMA的情况下修复简单的中断代码。
克里斯·斯特拉顿

10

使用DMA通常意味着您不再需要对每个字符进行中断,而只是在接收(或发送)字符的“缓冲区已满”之后才中断。这会增加处理这些字符的等待时间-在接收到缓冲区中的最后一个字符之后才处理第一个字符。

这种延迟可能是一件坏事,尤其是在诸如MIDI之类的对延迟敏感的应用程序中,此处几毫秒可能会增加现场表演的严重可播放性问题。


我要做的是一次接收1个字节(因此是1个字节的“ DMA”缓冲区),并且在该字节的每个DMA回调之后,将其存储在我手动处理的环形缓冲区中。在我的主循环中,我打算检查完整的MIDI消息并进行处理。
Michel Keijzers,2017年

3
DMA通常用于获取多个字节,并且仅在收到所有字节后才中断。使用DMA 时仅在一个字节后中断是正常的,所以令我感到奇怪的是:为此使用DMA的额外复杂性有什么意义?
史蒂夫·梅尔尼科夫

5
@MichelKeijzers然后,您所做的几乎与在纯中断驱动的实现中所做的完全相同。因此,在这种情况下使用DMA没有任何好处,您的原始问题可能无法通过DMA解决,而是通过重写(ISR,设置)代码来解决。
JimmyB

@JimmyB ...谢谢...但是由于以下Jonas的回答,由于消息很长,我将对读取这么多字节进行改进。我在收到第一个字节后就知道了(在大多数情况下)。使用DMA而非中断将带来更多好处。
Michel Keijzers '17

8

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


确实,也许仅在STM32上,(直接的非DMA)中断机制与直接DMA相比有点笨拙。
Michel Keijzers '17

2
@duskwuff不是真的;您可以轮询以查看DMA何时完成,并且您可能很想这样做,因为使用 DMA的主要原因之一是不必费心串行端口,直到您的程序处于可以对接收到的数据进行操作的状态为止数据。或对于传出DMA,您仅可以轮询以查看是否可以向发送缓冲区添加更多内容。
克里斯·斯特拉顿

1
@MichelKeijzers:IDK是特定的芯片,但是DMA的替代方案通常不是字面上的中断,而是编程的IO(使用CPU指令在I / O寄存器中读取/写入数据)。在中断处理程序中,通常会先读取一次,然后再读取一次,以防在读取第一个字符时输入字符,尤其是在不会触发另一个中断的情况下。或读取直到内部缓冲区为空(如果有这样的缓冲区)。显然,您需要为PIO提供更多的中断,并以不同的方式设置它们。
彼得·科德斯

@ChrisStratton好点……到目前为止,我还没有检查是否可以传输,我只是传输一些内容,而不是检查是否可以。如果可能,可能会再试一次。
Michel Keijzers

@PeterCordes似乎STM32有足够的DMA中断,我每次只读取1个字节。即使是最简单的STM32(F103c8t6),也具有足够的DMA端口/中断。
Michel Keijzers,2017年

5

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

仅在使用DMA时出现的一些问题是:

  • 我什么时候打扰?
    • DMA仅在传输了预定数量的数据后才真正喜欢中断。
    • 如果您从未收到足够的数据来触发DMA中断,该怎么办?
  • 如果在DMA中断时仅收到部分消息怎么办?
  • 您的RX缓冲区是什么样的?它们是线性的还是圆形的?
    • 从某种意义上讲,DMA只能服从地址边界,而绕过循环缓冲区系统中的其他指针也没有问题,因此它可能是不规则的循环缓冲区参与者。

无论如何,只是值得深思。


感谢您的考虑。目前,我总是收到1个字节并将其存储在环形缓冲区中,因为实际上我的消息(MIDI)可以具有不同的长度,而且我不知道下一个会得到什么。在主循环中,我检查是否有完整的消息来处理它们(如果完成,则将它们从环形缓冲区中删除)。因此,我总是会收到足够的数据(除非我会错过字节,否则我必须检查一下)。我的RX缓冲区只有1个字节,但是我将其复制到环形/圆形缓冲区。我没有检查它是否已满(需要添加)。
Michel Keijzers '17

嘿,不用担心。我确定您的应用程序将被良好地编程。就像其他人提到的那样,DMA很棒,但并不是免费的。如果您不使用它就可以逃脱,它会给系统带来一些额外的注意事项。
pgvoorhees

我希望我还是个初学者。
Michel Keijzers '17

3

在接收端(我记得),DMA终止于字符匹配或终端计数。一些协议和许多交互式应用程序不容易融入此模型,您确实需要逐个字符地处理事情。如果通信链路不可靠,则DMA技术也可能会很脆弱,丢失流中的单个字符很容易使DMA状态机混乱。


我确实逐字节接收并手动将其复制到环形缓冲区中,以便稍后进行处理。
Michel Keijzers '17

1

我现在已经在几个项目上使用了STM32CubeMx / HAL,发现它生成的UART处理软件在接收端有一定的缺点。

传输时,通常需要发送一个数据块或一行文本。在这种情况下,您可以预先知道数据传输的时间,因此使用DMA是显而易见的解决方案。传输完成后,您将收到一个中断,并且可以使用UART TX complete回调函数向您的主代码指示传输已完成,您可以发送另一个数据块。

当涉及到数据接收时,意法半导体提供的所有功能都假定您知道发送设备在开始发送之前将给您多少字符。通常这是未知的。中断功能将接收到的数据放入缓冲区,并且仅在接收到预定义数量的字符时才指示有可用数据。如果您尝试通过设置顺序的单个字符传输来使用DMA或中断功能来接收数据,则每次传输的建立时间都将意味着您将以最慢的数据速率(波特率会降低)以外的任何速率丢失字符。开始丢失数据将取决于您的处理器时钟速度),并且将过度加载处理器,从而没有任何其他处理的指令周期

为了解决这个问题,我编写了自己的中断处理程序函数,该函数将数据存储在一个小的本地循环缓冲区中,并设置一个由主代码读取的计数(RTOS计数信号量),以指示已准备好接收数据。然后,主代码可以在空闲时从该缓冲区收集数据,如果本地缓冲区在收集数据之前不溢出,则收集数据是否有延迟并不重要。


我做的完全一样(我认为)。我一次读取1个字节,并将其存储在循环缓冲区中,并且打算在主循环中检查完整的消息。可以增强一点。
Michel Keijzers '17

您是否认为我可能会遇到这样的问题,即每次设置DMA都会使我的处理器/丢失的字符过载31,250波特?
米歇尔·凯伊泽斯

1
只要您将DMA设置为一次传输多个字符,这将不是问题。我有4个运行115200和更高版本的UART,以及使用DMA的I2C,都没有问题。UART传输全都是〜20字节或更长。问题是使用DMA在UART(80MHz,9600baud的L4处理器)上进行接收。
uɐɪ

目前,我一次将其设置为1个字节,但是我可以对其进行改进(通过执行第一个字节,然后n来检查还需要多少个字节)。
Michel Keijzers '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.