3 DMA传送延迟管理
在开发基于微控制器的固件应用程序时,用户必须确保不会出现缓冲区欠载或过载的情况,因此,准确知晓每次传输的DMA延迟是非常必要的,这有助于验证内部系统是否能够支持应用程序所需的全部数据带宽。
3.1 DMA传输时间
3.1.1 默认DMA传输时序
如第2.2.2节所述,从外设到内存的DMA传输需要两次总线访问:
-
第一次访问:通过外设端口触发,由外设的请求引起,包括以下步骤:
- DMA外设端口请求的仲裁过程
- 计算外设的地址
- 从外设读取数据到DMA的FIFO(DMA的源)
-
第二次访问:通过内存端口触发,可以是当FIFO达到阈值时(使用FIFO模式)或在外设读取完成后立即进行(使用直接模式),包括以下步骤:
- DMA内存端口请求的仲裁过程
- 计算内存的地址
- 将数据写入SRAM(DMA的目标)
当从内存传输数据到外设时,同样需要两次访问:
-
第一次访问:DMA预测外设的访问需求,从内存中读取数据并存储到FIFO中,确保一旦触发DMA外设请求,就能立即进行数据传输。这包括:
- DMA内存端口请求的仲裁过程
- 计算内存的地址
- 从内存读取数据到DMA的FIFO(DMA的源)
-
第二次访问:当外设请求被触发时,DMA外设端口会生成一次数据传输。这包括:
- DMA外设端口请求的仲裁过程
- 计算外设的地址
- 将加载的数据写入到外设的地址(DMA的目标)
通常,DMA流TS的总传输时间由以下公式确定: (外设访问/传输时间) +(内存访问/传输时间) 其中: 是DMA外设端口访问和传输的总时长,计算公式为:
表4 外设端口的访问/传输时间与所采用的DMA路径的关系
描述 | 通过总线矩阵 | 到AHB的DMA直接路径 | 到APB外设 |
---|---|---|---|
DMA外设端口仲裁时间 | 1个AHB周期 | 1个AHB周期 | 1个AHB周期 |
: 外设地址计算时间 | 1个AHB周期 | 1个AHB周期 | 1个AHB周期 |
: 总线矩阵仲裁时间(无并发时) | 1个AHB周期 | 1个AHB周期 | 不适用 |
: 有效数据传输时间 | 1个AHB周期 | 2个APB周期 | 2个APB周期 |
: 总线同步时间 | 不适用 | 1个AHB周期 | 1个AHB周期 |
1. 的默认值设为零。
2. 对于FMC,根据所使用的外部存储器,可能会增加一个额外的周期。根据外部存储器的时序要求,可能会增加额外的AHB周期。
3. 在进行突发传输时,有效数据传输时间依赖于突发的长度(例如,INC4配置下为4个AHB周期)。
TSM 是 DMA 内存端口访问和传输的总时间,它等于:
其中:
- 是内存地址计算时间。
- 是内存端口请求仲裁时间。
- 是总线矩阵仲裁时间(如果有并发访问请求)。
- 是数据写入 SRAM(静态随机存取存储器)的时间。
表5 内存端口的访问/传输时间
描述 | 延迟时长 |
---|---|
: DMA内存端口仲裁时间 | 1个AHB周期 |
: 内存地址计算时间 | 1个AHB周期 |
: 总线矩阵仲裁时间(无并发访问时) | 1个AHB周期 |
1. 的 默认值设为零。
2. 对于连续的SRAM访问(在没有其他主设备在此期间访问同一SRAM的情况下),等于0个周期。
3.1.2 DMA传输时间与并发访问的影响
如果多个主设备尝试同时对同一从设备进行访问,可能会导致DMA服务时间在外设和内存的最坏情况访问/传输时间基础上增加额外的延迟。以下是影响DMA流服务总延迟时间的几个关键因素:
- 当多个主设备同时对同一个AHB目标进行访问时,DMA的延迟将受到影响。如第2.1.2节所述,只有在总线矩阵仲裁器批准DMA的访问请求后,DMA传输才能启动。
- 如果多个主设备(包括DMA和CPU)同时尝试访问同一个AHB至APB桥接,由于AHB至APB桥接的仲裁机制,DMA传输的时间将会延长,这一点在第2.3.2节中有详细说明。
在设计使用DMA传输的系统时,这些因素必须被考虑在内,以确保系统能够满足实时性能要求和数据带宽需求。了解并预测这些延迟对于优化系统的整体性能和响应速度至关重要。
3.2 示例
3.2.1 从ADC到SRAM的DMA数据传输
该示例适用于ARM内核微控制器。
模拟到数字转换器(ADC)被设置为连续三重交错模式。在此模式下,ADC以最大速率(36 MHz)持续转换单一模拟输入通道。ADC的预分频器设置为2,采样时间设定为1.5个周期,而交错模式下连续两次ADC采样间的间隔设定为5个周期。
DMA2的流0负责将ADC转换后的数值传输到SRAM的缓冲区中。DMA2通过直接路径访问ADC,但对于SRAM的访问则是通过总线矩阵来实现的。
在本示例中,从ADC的DMA触发信号(ADC EOC)到将ADC数值写入SRAM的总DMA延迟时间,若AHB/APB预分频器设为1,则为9个AHB周期;若预分频器设为2,则为11个AHB周期。
注释:在使用FIFO(先进先出队列)时,一旦FIFO达到用户所配置的阈值,就会触发DMA内存端口的访问。
表6 DMA外设端口(ADC)传输延迟
描述 | 延迟(当AHB/APB预分频器等于1) | 延迟(当AHB/APB预分频器等于2) |
---|---|---|
DMA外设端口仲裁时间 | 1个AHB周期 | 1个AHB周期 |
: 外设地址计算时间 | 1个AHB周期 | 1个AHB周期 |
: 总线矩阵仲裁 | 不适用 | 不适用 |
: 有效数据传输时间 | 2个AHB周期 | 4个AHB周期 |
: 总线同步时间 | 1个AHB周期 | 1个AHB周期 |
: 外设端口的DMA传输总时间 | 5个AHB周期 | 7个AHB周期 |
1. DMA2通过直接路径访问ADC,因此不涉及总线矩阵仲裁。
以本例为参考,ADC的DMA触发信号(即ADC的EOC,即转换结束标志)至将ADC的数值写入SRAM的整个过程中的总DMA延迟时间,若AHB/APB预分频器设为1,则为9个AHB周期;若预分频器设为2,则为11个AHB周期。
注释:在使用FIFO模式时,一旦FIFO的填充水平达到用户所设定的阈值,就会激发DMA内存端口的访问操作。
3.2.2 SPI全双工DMA传输示例
示例使用的微控制器,且基于SPI1外设,配置了两个DMA请求:
- DMA2_Stream2用于SPI1接收(SPI1_RX):此流被设定为最高优先级,目的是及时处理SPI1接收到的数据,并将这些数据从SPI1_DR寄存器传输到SRAM缓冲区。
- DMA2_Stream3用于SPI1发送(SPI1_TX):此流负责将数据从SRAM缓冲区传输到SPI1_DR寄存器。
AHB时钟频率与APB2时钟频率相等(84 MHz),SPI1被配置为以最大速率(42 MHz)运行。DMA2_Stream2(用于SPI1接收)会在DMA2_Stream3(用于SPI1发送)之前触发,后者在两个AHB周期之后触发。
在这种配置下,CPU将无限期地轮询I2C1_DR寄存器。由于I2C1外设位于APB1总线上,而SPI1外设位于APB2总线上,系统的传输路径如下:
- DMA2通过直接路径访问APB2(不经过总线矩阵),
- CPU通过总线矩阵访问APB1。
这个设置旨在展示,即使CPU在APB1上进行轮询,DMA的传输时序也不会受到影响。以下图表概括了SPI全双工DMA传输的时间安排,包括发送和接收模式下的DMA时序,以及每个操作的时间调度:
根据图16的描述:
- CPU在APB1上的轮询操作不会对APB2上的DMA传输延迟产生影响。
- 在DMA2_Stream2(SPI1_RX)传输过程中,到了第八个AHB时钟周期,不需要进行总线矩阵仲裁,因为假设最后一个访问SRAM的主设备是DMA2,所以不需要重新进行仲裁。
- 在DMA2_Stream3(SPI1_TX)传输过程中,该DMA流会提前从SRAM中读取数据并将其写入FIFO(先进先出队列),然后一旦触发,DMA外设端口(目标是SPI1)便开始工作。
- 对于DMA2_Stream3,在其总线同步周期内,DMA外设仲裁阶段(耗时1个AHB周期)得以执行。
4 DMA控制器编程时的技巧
4.1 关闭DMA的软件步骤
要关闭连接到DMA流的外设,必须执行以下步骤:
- 关闭连接到该外设的DMA流。
- 等待DMA_SxCR寄存器中的EN位被清零。 只有完成这些步骤后,才能安全地禁用外设。应将外设控制寄存器中的DMA请求使能位清零,以确保清除任何来自外设的待处理请求。 注意:在这两种情况下,传输完成中断标志(TCIF)将在DMA_LISR或DMA_HISR中设置,以指示由于流被禁用而导致传输结束。
4.2 启用新传输前清除DMA标志
在启用新传输之前,用户必须确保DMA_LISR或DMA_HISR中的传输完成中断标志(TCIF)已被清除。 作为一般建议,建议在开始新传输之前清除DMA_LIFCR和DMA_HIFCR寄存器中的所有标志。
4.3 启用DMA的软件步骤
启用DMA时,应遵循以下软件步骤:
- 配置适当的DMA流。
- 启用所使用的DMA流(设置DMA_SxCR寄存器中的EN位)。
- 启用所使用的外设。应在外设控制寄存器中设置DMA请求使能位。 注意:如果用户在对应的DMA流准备好之前就启用了所使用的外设,可能会因为DMA还未能为外设提供所需的第一个数据而导致FIFO错误中断标志(FEIF)被设置。
4.4 当NDTR为0时的内存到内存传输
当配置DMA流以执行普通模式下的内存到内存传输时,一旦NDTR值降至0,传输完成标志就会被设置。此时,如果用户启用了该流的使能位,内存到内存传输将自动使用上一次NDTR的值重新触发。
4.5 外设突发传输时的PINC/MINC为0
当DMA突发特性配合外设地址递增(PINC)或内存地址递增(MINC)禁用时,可以访问支持突发传输的内部或外部(如FSMC)外设。这种模式确保在DMA流进行事务期间不会被其他DMA流中断。
4.6 双重映射的DMA请求
当用户配置两个或更多DMA流以响应同一外设请求时,软件应确保在启用新的DMA流之前,当前DMA流已完全禁用。
4.7 最佳DMA吞吐量配置
在使用STM32F4xx系列微控制器并DMA服务高速外设时,如果AHB频率降低,建议将堆栈和堆放在CCM(CPU可以通过D-bus直接寻址)中,而不是放在SRAM上,以避免CPU和DMA在访问SRAM内存时产生额外的并发冲突。
4.8 DMA传输的暂停
用户可以随时暂停DMA传输,以便稍后重新启动或在传输结束前明确禁用。 有两种情况:
- 如果流禁用了传输,并且不打算从停止点重新启动,则除了清除DMA_SxCR寄存器中的EN位以禁用流,并等待EN位被清零之外,无需采取其他特别措施。作为结果:
——DMA_SxNDTR寄存器包含了流停止时剩余的数据项数量,使软件能够确定在流被中断之前传输了多少数据项。
- 如果流暂停了传输,以便稍后通过重新启用流来恢复,则软件在禁用流(将EN位设为“0”)后必须读取DMA_SxNDTR寄存器,以了解已经收集的数据项数量。然后:
——必须更新外设和/或内存地址,以调整地址指针。
——SxNDTR寄存器必须更新为剩余要传输的数据项数量(即流被禁用时读取的值)。
——之后,可以重新启用流以从停止的点开始恢复传输。 注意:在这两种情况下,传输完成中断标志(TCIF)将在DMA_LISR或DMA_HISR中设置,以指示由于流中断而导致的传输结束。
4.9 利用DMA2控制器及系统架构的灵活性
本节旨在展示如何利用STM32架构和DMA控制器提供的灵活性。以这种灵活性为例,我们将探讨如何反向操作DMA2的AHB外设端口和内存端口,并确保正确管理外设数据传输。为了实现这一点并接管对DMA2常规行为的控制,我们需要审视DMA2的工作模式。
由于DMA2的两个端口都连接到AHB总线矩阵,并且与AHB从设备有着对称的连接,这种架构允许根据软件配置,在外设端口和内存端口上沿一个或另一个方向传输数据。
4.9.1 在DMA2 AHB端口上反向传输的考虑
软件具有根据需求配置DMA2流传输模式的灵活性。根据配置,一个DMA2 AHB端口可能被设置为读取方向,而另一个设置为写入方向。
表8 DMA AHB端口方向与传输模式配置的对应关系
传输模式 | DMA2 AHB内存端口 | DMA2 AHB外设端口 |
---|---|---|
从内存到外设 | 读取(Read) | 写入(Write) |
从外设到内存 | 写入(Write) | 读取(Read) |
内存之间传输 | 写入(Write) | 读取(Read) |
正如第2.2.2节“DMA传输状态”中所讨论的,外设端口上的传输是由外设的请求来触发的,而内存端口上的传输可以由FIFO阈值(在使用FIFO模式时)触发,或者在外设读取完成后立即触发(在使用直接模式时)。
在使用DMA2内存端口来管理外设时,我们需要特别注意以下几个方面:预触发传输、缓冲区传输,以及对最后数据的管理。
预触发传输:
如在第2.2.2节“DMA传输状态”中所述,当配置为内存到外设的传输模式(通过内存端口进行数据读取)时,DMA会提前读取数据,一旦DMA流被启动,就会立即进行数据读取。在直接模式下,会有一个数据被预先读取并缓冲,而在使用DMA FIFO模式时,可以预先缓冲多达4个32位宽的数据字。
在通过DMA2内存端口管理外设的读取操作时,软件必须确保在启动DMA之前先启动外设,这样可以保证首次DMA访问的有效性。
图17 展示了在外设触发的情况下,DMA在外设端口和内存端口上的访问模式。
最后数据读取的管理
每个DMA流都有一个4×32位宽字的FIFO,用于在AHB端口间缓冲数据。在通过DMA的内存端口管理外设的读取操作时,软件必须确保额外读取4个宽字的数据,以保证最后的有效数据能够从DMA的FIFO中成功传输出去。
禁用直接模式时的缓冲传输处理: 当在外设的间接模式下(即FIFO模式被启用)通过内存端口向其写入数据时,软件需要确保对该端口的访问是由预设的FIFO阈值触发。一旦达到设定的阈值,数据便会从FIFO传送至内存端口的目标地址。
在向寄存器(例如,不具备FIFO功能的GPIO)写入数据时,DMA FIFO中的数据将会被连续不断地写入到目标地址。
最为关键的一点是,在将外设管理的职责从外设端口转移到内存端口时,软件需要重新评估传输大小和地址递增的配置。
如在第1.1.5节“传输大小”中所述,传输大小由外设端的传输宽度(字节、半字、字)以及待传输的数据项数量(即DMA_SxNTDR寄存器中编程的数值)共同决定。根据DMA的新配置,在端口角色互换后,可能需要对DMA_SxNTDR寄存器中的数值进行调整。
4.9.2 通过DMA2 AHB端口反转Quad-SPI传输的示例
在此示例中,DMA_S7M0AR被设定为指向Quad-SPI的数据寄存器地址,而DMA_S7PAR则被配置为指向数据缓冲区的地址(比如SRAM中的缓冲区)。在DMA_S7CR寄存器中,当向Quad-SPI写入数据时,应将DMA2流的方向设置为外设到内存的传输模式。反之,从Quad-SPI读取数据时,应将其设置为内存到外设的传输模式。
写操作代码
/* Program M0AR with QUADSPI data register address */
DMA2_Stream7->M0AR = (uint32_t)&QUADSPI->DR;
/* Program PAR with Buffer address */
DMA2_Stream7->PAR = (uint32_t)&u32Buffer[0];
/* Write number of data items to transfer */
DMA2_Stream7->NDTR = 0x100;
/* Configure DMA : MSIZE=PSIZE=0x02 (Word), CHSEL=0x03
(QUADSPI), PINC=1, DIR=0x00 */
DMA2_Stream7->CR = DMA_SxCR_PSIZE_1 |
DMA_SxCR_MSIZE_1 | 3ul<<25 | DMA_SxCR_PINC;
/* Enable DMA request generation */
QUADSPI->CR |= QUADSPI_CR_DMAEN;
/* Write the DLR Register */
QUADSPI->DLR = (0x100* 4)-1;
/* Write to QUADSPI DCR */
QUADSPI->CCR = QUADSPI_CCR_IMODE_0|
QUADSPI_CCR_ADMODE_0| QUADSPI_CCR_DMODE|
QUADSPI_CCR_ADSIZE| QUAD_IN_FAST_PROG_CMD;
/* Write the AR Register */
QUADSPI->AR = 0x00ul;
/* Enable the selected DMA2_Stream7 by setting EN bit */
DMA2_Stream7->CR |= (uint32_t)DMA_SxCR_EN;
/* Wait for the end of Transfer */
while((QUADSPI->SR & QUADSPI_SR_TCF) !=
QUADSPI_SR_TCF);
读操作代码
/* Program M0AR with QUADSPI data register address */
DMA2_Stream7->M0AR = (uint32_t)&QUADSPI->DR;
/* Program PAR with Buffer address */
DMA2_Stream7->PAR = (uint32_t)&u32Buffer[0];
/* Write number of data items to transfer */
DMA2_Stream7->NDTR = 0x100;
/* Configure DMA : MSIZE=PSIZE=0x02 (Word), CHSEL=0x03
(QUADSPI), PINC=1, DIR=0x01 */
DMA2_Stream7->CR = DMA_SxCR_PSIZE_1 |
DMA_SxCR_MSIZE_1 | 3ul<<25 | DMA_SxCR_PINC|
DMA_SxCR_DIR_0;
/* Enable DMA request generation */
QUADSPI->CR |= QUADSPI_CR_DMAEN;
/* Write the DLR Register */
QUADSPI->DLR = ((0x100+4)* 4)-1;
/* Write to QUADSPI DCR */
QUADSPI->CCR = QUADSPI_CCR_IMODE_0|
QUADSPI_CCR_ADMODE_0| QUADSPI_CCR_DMODE|
QUADSPI_CCR_ADSIZE|
QUADSPI_CCR_FMODE_0|QUAD_OUT_FAST_READ_CMD;
/* Write the AR Register */
QUADSPI->AR = 0x00ul;
/* Enable the selected DMA2_Stream7 by setting EN bit */
DMA2_Stream7->CR |= (uint32_t)DMA_SxCR_EN;
/* Wait for the end of Transfer */
while((DMA2_Stream7->CR & DMA_SxCR_EN) ==
DMA_SxCR_EN);
4.10 微控制器DMA传输与缓存维护以防止数据不一致性
当软件使用可缓存的内存区域作为DMA的源或目的缓冲区时,必须在启动DMA操作前执行缓存清洗操作,以确保所有数据都被确实写入到子系统内存中。在DMA传输完成之后,如果需要从外设读取数据,软件必须在读取更新的内存区域之前执行缓存失效操作。
为了提高数据传输的可靠性,建议为DMA缓冲区使用不可缓存的内存区域。软件可以通过内存保护单元(MPU)配置一个不可缓存的内存块,作为CPU和DMA之间的共享内存使用。
5 总结
DMA控制器旨在满足多种嵌入式应用场景的需求,主要通过以下特点实现:
- 为固件提供选择灵活性,可以在16个流和16个通道(每个DMA控制器8个通道)之间选择最合适的组合,
- 利用双AHB端口架构减少了DMA传输的总体延迟时间,并通过直接路径连接到APB桥,避免了DMA在服务低速APB外设时CPU在AHB1访问上的停滞,
- DMA中FIFO的实现增加了固件配置源和目标之间不同数据尺寸的灵活性,并在使用增量突发传输模式时加快了数据传输速度。
通过这些设计,DMA控制器能够提高数据传输的效率和性能,同时减少CPU的负载,使得系统能够更加高效地运行,尤其是在需要处理大量数据或高速数据流的场合。