【STM32H7教程】第62章 STM32H7的MDMA,DMA2D和通用DMA性能比较

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第62章       STM32H7的MDMA,DMA2D和通用DMA性能比较

本章节为大家比较MDMA,DMA2D和通用DMA的性能,方便大家在实际应用中选择合适的DMA方式。

62.1 初学者重要提示

62.2 测试条件说明

62.3 MDMA性能测试程序设计

62.4 DMA2D性能测试程序设计

62.5 通用DMA性能测试程序设计

62.6 MDMA、DMA2D和通用DMA性能比较

62.7 MDMA驱动移植和使用

62.8 实验例程设计框架

62.9 实验例程说明(MDK)

62.10 实验例程说明(IAR)

62.11 总结

62.1 初学者重要提示

  1.   学习本章节前,务必优先学习第61章,需要对MDMA的基础知识有个认识。
  2.   官方各种MDMA例子简易分析,方便大家更好的了解MDMA应用场景:http://www.armbbs.cn/forum.php?mod=viewthread&tid=88905
  3.   合理配置STM32H7的MDMA突发传输次数和源数据以及目的数据位宽可以再提升一点性能http://www.armbbs.cn/forum.php?mod=viewthread&tid=94071

62.2 测试条件说明

MDMA,DMA2D和每个都测试了四种情况

  •   64位带宽的AXI SRAM内部做64KB数据传输。
  •   32位带宽的D2域SRAM1内部64KB数据传输。
  •   AXI SRAM向SDRAM传输64KB的数据传输。
  •   32位带宽的SDRAM内部做64KB数据传输。

MDMA:

在D1域,支持64位带宽的DMA数据传输。

DMA2D:

在D1域,主要用图形2D加速。

DMA1和DMA2:

在D2域,支持32位带宽的DMA数据传输。

62.3 MDMA性能测试程序设计

这里将MDMA的程序设计分为以下几部分,逐一为大家做个说明:

62.3.1 第1步,MDMA初始化

程序代码如下,采用块传输,源地址和目的地址都是64bit数据传输,并设置16beat突发,也就是连续传输16组64bit数据。

1.    __HAL_RCC_MDMA_CLK_ENABLE();  
2.    
3.    MDMA_Handle.Instance = MDMA_Channel0;  
4.    
5.    MDMA_Handle.Init.Request              = MDMA_REQUEST_SW;         /* 软件触发 */
6.    MDMA_Handle.Init.TransferTriggerMode  = MDMA_BLOCK_TRANSFER;     /* 块传输 */
7.    MDMA_Handle.Init.Priority             = MDMA_PRIORITY_HIGH;      /* 优先级高*/
8.    MDMA_Handle.Init.Endianness           = MDMA_LITTLE_ENDIANNESS_PRESERVE; /* 小端 */
9.    MDMA_Handle.Init.SourceInc            = MDMA_SRC_INC_DOUBLEWORD;      /* 源地址自增,双字,即8字节 */
10.    MDMA_Handle.Init.DestinationInc       = MDMA_DEST_INC_DOUBLEWORD;     /* 目的地址自增,双字,即8字节 */
11.    MDMA_Handle.Init.SourceDataSize       = MDMA_SRC_DATASIZE_DOUBLEWORD; /* 源地址数据宽度双字,即8字节 */
12.    MDMA_Handle.Init.DestDataSize       = MDMA_DEST_DATASIZE_DOUBLEWORD;/* 目的地址数据宽度双字,即8字节 */
13.    MDMA_Handle.Init.DataAlignment      = MDMA_DATAALIGN_PACKENABLE;    /* 小端,右对齐 */                    
14.    MDMA_Handle.Init.SourceBurst        = MDMA_SOURCE_BURST_16BEATS;    /* 源数据突发传输 */
15.    MDMA_Handle.Init.DestBurst          = MDMA_DEST_BURST_16BEATS;      /* 目的数据突发传输 */
16.    
17.    MDMA_Handle.Init.BufferTransferLength = 128;    /* 每次传输128个字节 */
18.    
19.    MDMA_Handle.Init.SourceBlockAddressOffset  = 0; /* 用于block传输,地址偏移0 */
20.    MDMA_Handle.Init.DestBlockAddressOffset    = 0; /* 用于block传输,地址偏移0 */
21.    
22.    /* 初始化MDMA */
23.    if(HAL_MDMA_Init(&MDMA_Handle) != HAL_OK)
24.    {
25.         Error_Handler(__FILE__, __LINE__);
26.    }

下面将程序设计中几个关键地方做个阐释:

  •   第1行,务必优先初始化MDMA时钟,测试发现没有使能时钟的情况下就配置MDMA很容易失败。
  •   第14-15行,突发传输的配置非常考究,每次突发传输的总数据大小不能超过128字节。
    •   对于源地址就是SourceBurst * SourceDataSize <=  BufferTransferLength。
    •  对于目的地址就是DestBurst*DestDataSize <= BufferTransferLength。

比如当前的程序配置:

SourceBurst * SourceDataSize = 16*8 =128字节

DestBurst*DestDataSize = 16*8 =128字节

这里要特别注意一点,如果实际应用中最好小于BufferTransferLength,防止不稳定。

62.3.2 第2步,MDMA中断配置

MDMA的中断设置比较简单,代码如下,注册了MDMA的传输完成回调:

HAL_MDMA_RegisterCallback(&MDMA_Handle, HAL_MDMA_XFER_CPLT_CB_ID, MDMA_TransferCompleteCallback);
HAL_NVIC_SetPriority(MDMA_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(MDMA_IRQn);  

void MDMA_IRQHandler(void)
{
    HAL_MDMA_IRQHandler(&MDMA_Handle);
}
static void MDMA_TransferCompleteCallback(MDMA_HandleTypeDef *hmdma)
{
    TransferCompleteDetected = 1;
}

在传输完成回调里面设置了一个变量标志TransferCompleteDetected,方便指示传输完成。

62.3.3 第3步,AXI SRAM内部互传64KB数据

通过下面的程序实现将地址0x2400 0000开始的64KB数据复制到地址0x2400 0000 + 64*1024里面:

TransferCompleteDetected = 0;
HAL_MDMA_Start_IT(&MDMA_Handle, 
                  (uint32_t)0x24000000, 
                  (uint32_t)(0x24000000 + 64*1024), 
                  64*1024, 
                  1);


start = DWT_CYCCNT;
while(TransferCompleteDetected == 0) {}
end = DWT_CYCCNT;
cnt = end - start;

//64*1024/(cnt/400/1000/1000)/1024/1024 = 64*1000*1000*400/1024/cnt = 25000000/cnt
printf("MDMA---AXI SRAM内部互传64KB数据耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);

通过时钟周期计数器测量执行时间,单位2.5ns。

62.3.4 第4步,D2域SRAM1内部互传64KB数据

通过下面的程序实现将地址0x3000 0000开始的64KB数据复制到地址0x3000 0000 + 64*1024里面:

TransferCompleteDetected = 0;
HAL_MDMA_Start_IT(&MDMA_Handle, 
                  (uint32_t)0x30000000, 
                  (uint32_t)(0x30000000 + 64*1024), 
                  64*1024, 
                  1);

start = DWT_CYCCNT;
while(TransferCompleteDetected == 0) {}
end = DWT_CYCCNT;
cnt = end - start;

printf("MDMA---D2域SRAM1内部互传64KB数据耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);

通过时钟周期计数器测量执行时间,单位2.5ns。

62.3.5 第5步,AXI SRAM传输64KB数据到SDRAM

通过下面的程序实现将地址0x2400 0000开始的64KB数据复制到地址0xC000 0000里面:

TransferCompleteDetected = 0;
HAL_MDMA_Start_IT(&MDMA_Handle, 
                  (uint32_t)0x24000000, 
                  (uint32_t)0xC0000000, 
                  64*1024, 
                  1);

start = DWT_CYCCNT;
while(TransferCompleteDetected == 0) {}
end = DWT_CYCCNT;
cnt = end - start;

printf("MDMA---AXI SRAM传输64KB数据到SDRAM耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);

通过时钟周期计数器测量执行时间,单位2.5n。

62.3.6 第6步,SDRAM内部互传64KB数据

通过下面的程序实现将地址0xC000 0000开始的64KB数据复制到地址0xC000 0000 + 64*1024里面:

TransferCompleteDetected = 0;
HAL_MDMA_Start_IT(&MDMA_Handle, 
                  (uint32_t)0xC0000000, 
                  (uint32_t)(0xC0000000 + 64*1024), 
                  64*1024, 
                  1);


start = DWT_CYCCNT;
while(TransferCompleteDetected == 0) {}
end = DWT_CYCCNT;
cnt = end - start;

printf("MDMA---SDRAM内部互传64KB数据耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);

通过时钟周期计数器测量执行时间,单位2.5n。

62.4 DMA2D性能测试程序设计

这里将DMA2D的程序设计分为以下几部分,逐一为大家做个说明:

62.4.1 第1步,DMA2D初始化

配置DMA2D采用存储器到存储器模式,前景区和输出区都采用ARGB8888格式,传输64*256次,每次4字节,即64*256*4 = 64KB数据。

__HAL_RCC_DMA2D_CLK_ENABLE();  

/* DMA2D采用存储器到存储器模式, 这种模式是前景层作为DMA2D输入 */  
DMA2D->CR      = 0x00000000UL;
DMA2D->FGOR    = 0;
DMA2D->OOR     = 0;

/* 前景层和输出区域都采用的ARGB8888颜色格式 */
DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_ARGB8888;
DMA2D->OPFCCR  = LTDC_PIXEL_FORMAT_ARGB8888;

DMA2D->NLR     = (uint32_t)(64 << 16) | (uint16_t)256;

62.4.2 第2步,AXI SRAM内部互传64KB数据

通过下面的程序实现将地址0x2400 0000开始的64KB数据复制到地址0x2400 0000 + 64*1024里面:

/* AXI SRAM的64KB数据传输测试 ***********************************************/
DMA2D->FGMAR = (uint32_t)0x24000000;
DMA2D->OMAR  = (uint32_t)(0x24000000 + 64*1024);
DMA2D->CR   |= DMA2D_CR_START;   

start = DWT_CYCCNT;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START) {} 
end = DWT_CYCCNT;
cnt = end - start;

printf("DMA2D---AXI SRAM内部互传64KB数据耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);

通过时钟周期计数器测量执行时间,单位2.5ns。

62.4.3 第3步,D2域SRAM1内部互传64KB数据

通过下面的程序实现将地址0x3000 0000开始的64KB数据复制到地址0x3000 0000 + 64*1024里面:

/* D2域SRAM1的64KB数据传输测试 ***********************************************/
DMA2D->FGMAR = (uint32_t)0x30000000;
DMA2D->OMAR  = (uint32_t)(0x30000000 + 64*1024);
DMA2D->CR   |= DMA2D_CR_START;  

start = DWT_CYCCNT;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START) {} 
end = DWT_CYCCNT;
cnt = end - start;
    
printf("DMA2D---D2域SRAM1内部互传64KB数据耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);

通过时钟周期计数器测量执行时间,单位2.5ns。

62.4.4 第4步,AXI SRAM传输64KB数据到SDRAM

通过下面的程序实现将地址0x2400 0000开始的64KB数据复制到地址0xC000 0000里面:

/* AXI SRAM向SDRAM的64KB数据传输测试 ***********************************************/
DMA2D->FGMAR = (uint32_t)0x24000000;
DMA2D->OMAR  = (uint32_t)0xC0000000;
DMA2D->CR   |= DMA2D_CR_START;  

start = DWT_CYCCNT;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START) {} 
end = DWT_CYCCNT;
cnt = end - start;
    
printf("DMA2D---AXI SRAM传输64KB数据到SDRAM耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);    

通过时钟周期计数器测量执行时间,单位2.5n。

62.4.5 第5步,SDRAM内部互传64KB数据

通过下面的程序实现将地址0xC000 0000开始的64KB数据复制到地址0xC000 0000 + 64*1024里面:

/* SDRAM的64KB数据传输测试 ***********************************************/
DMA2D->FGMAR = (uint32_t)0xC0000000;
DMA2D->OMAR  = (uint32_t)(0xC0000000 + 64*1024);
DMA2D->CR   |= DMA2D_CR_START;

start = DWT_CYCCNT;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START) {} 
end = DWT_CYCCNT;
cnt = end - start;    
    
printf("DMA2D---SDRAM内部互传64KB数据耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);    

通过时钟周期计数器测量执行时间,单位2.5n

62.5 通用DMA性能测试程序设计

这里将DMA1的程序设计分为以下几部分,逐一为大家做个说明:

62.5.1 第1步,DMA1初始化

程序代码如下,采用存储区到存储区传输方式,源地址和目的地址都是32bit数据传输,并设置4beat突发,也就是连续传输4组32bit数据。

1.    __HAL_RCC_DMA1_CLK_ENABLE();
2.    
3.    DMA_Handle.Instance                 = DMA1_Stream1;
4.    DMA_Handle.Init.Request             = DMA_REQUEST_MEM2MEM;  
5.    DMA_Handle.Init.Direction           = DMA_MEMORY_TO_MEMORY;
6.    DMA_Handle.Init.PeriphInc           = DMA_PINC_ENABLE;
7.    DMA_Handle.Init.MemInc              = DMA_MINC_ENABLE;
8.    DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
9.    DMA_Handle.Init.MemDataAlignment    = DMA_PDATAALIGN_WORD;
10.    DMA_Handle.Init.Mode                = DMA_NORMAL;
11.    DMA_Handle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
12.    DMA_Handle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
13.    DMA_Handle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
14.    DMA_Handle.Init.MemBurst            = DMA_MBURST_INC4;     /*WORD方式,仅支持4次突发 */
15.    DMA_Handle.Init.PeriphBurst         = DMA_PBURST_INC4;     /*WORD方式,仅支持4次突发 */
16.    DMA_Handle.XferCpltCallback         = DMA_TransferCompleteCallback;
17.    
18.    HAL_DMA_Init(&DMA_Handle);

下面将程序设计中几个关键地方做个阐释:

  •   第1行,务必优先初始化DMA时钟,测试发现没有使能时钟的情况下就配置DMA很容易失败。
  •   第14-15行,突发传输的配置非常考究,这里要特别注意数据位宽,FIFO以及突发的配置。

 

程序中数据位宽是配置为32bit,FIFO配置为满,那么突发仅可以配置为4beat,即DMA_MBURST_INC4。

  •   第16行,设置传输完成回调函数。

62.5.2 第2步,DMA1中断配置

DMA1的中断设置比较简单,代码如下:

HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);

void DMA1_Stream1_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&DMA_Handle);
}
static void DMA_TransferCompleteCallback(DMA_HandleTypeDef *hdma)
{
    TransferCompleteDetected = 1;
}

在传输完成回调里面设置了一个变量标志TransferCompleteDetected,方便指示传输完成。

62.5.3 第3步,AXI SRAM内部互传64KB数据

通过下面的程序实现将地址0x2400 0000开始的64KB数据复制到地址0x2400 0000 + 64*1024里面:

/* AXI SRAM的64KB数据传输测试 ***********************************************/
TransferCompleteDetected = 0;
HAL_DMA_Start_IT(&DMA_Handle, (uint32_t)0x24000000, (uint32_t)(0x24000000 + 64*1024), 64*256);

start = DWT_CYCCNT;
while(TransferCompleteDetected == 0) {}
end = DWT_CYCCNT;
cnt = end - start;

//64*1024/(cnt/400/1000/1000)/1024/1024 = 64*1000*1000*400/1024/cnt = 25000000/cnt
printf("DMA1---AXI SRAM内部互传64KB数据耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);

通过时钟周期计数器测量执行时间,单位2.5ns。

62.5.4 第4步,D2域SRAM1内部互传64KB数据

通过下面的程序实现将地址0x3000 0000开始的64KB数据复制到地址0x3000 0000 + 64*1024里面:

/* D2域SRAM1的64KB数据传输测试 ***********************************************/
TransferCompleteDetected = 0;
HAL_DMA_Start_IT(&DMA_Handle, (uint32_t)0x30000000, (uint32_t)(0x30000000 + 64*1024), 64*256);

start = DWT_CYCCNT;
while(TransferCompleteDetected == 0) {}
end = DWT_CYCCNT;
cnt = end - start;

printf("DMA1---D2域SRAM1内部互传64KB数据耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);

通过时钟周期计数器测量执行时间,单位2.5ns。

62.5.5 第5步,AXI SRAM传输64KB数据到SDRAM

通过下面的程序实现将地址0x2400 0000开始的64KB数据复制到地址0xC000 0000里面:

/* AXI SRAM向SDRAM的64KB数据传输测试 ***********************************************/
TransferCompleteDetected = 0;
HAL_DMA_Start_IT(&DMA_Handle, (uint32_t)0x24000000, (uint32_t)0xC0000000, 64*256);

start = DWT_CYCCNT;
while(TransferCompleteDetected == 0) {}
end = DWT_CYCCNT;
cnt = end - start;

printf("DMA1---AXI SRAM传输64KB数据到SDRAM耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);

通过时钟周期计数器测量执行时间,单位2.5n。

62.5.6 第6步,SDRAM内部互传64KB数据

通过下面的程序实现将地址0xC000 0000开始的64KB数据复制到地址0xC000 0000 + 64*1024里面:

/* SDRAM的64KB数据传输测试 ***********************************************/
TransferCompleteDetected = 0;
HAL_DMA_Start_IT(&DMA_Handle, (uint32_t)0xC0000000, (uint32_t)(0xC0000000 + 64*1024), 64*256);    

start = DWT_CYCCNT;
while(TransferCompleteDetected == 0) {}
end = DWT_CYCCNT;
cnt = end - start;

printf("DMA1---SDRAM内部互传64KB数据耗时 =  %dus %dMB/S
", cnt/400, 25000000/cnt);

通过时钟周期计数器测量执行时间,单位2.5n。

62.6 MDMA,DMA2D和通用DMA性能比较

最终测试的性能如下:

 

可以看到DMA1的性能跟其它两个不是一个级别的,适合搞搞低速的外设。

DMA2D和MDMA互有高低。

62.7 MDMA驱动移植和使用

MDMA驱动的移植比较方便:

  •   第1步:添加MDMA的HAL库文件,简单省事些可以添加所有HAL库.C源文件进来。
  •   第2步,应用方法看本章节配套例子即可,另外就是根据自己的需要做配置修改。

62.8 实验例程设计框架

通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:

 

  第1阶段,上电启动阶段:

  • 这部分在第14章进行了详细说明。

  第2阶段,进入main函数:

  •  第1步,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器,LED和SDRAM。
  •  第2步,测评MDMA,DMA2D和通用DMA性能。

62.9 实验例程说明(MDK)

配套例子: 

V7-038_MDMA,DMA2D和通用DMA性能比较

实验目的:

  1. 比较MDMA,DMA2D和DMA1的性能

实验内容:

 MDMA,DMA2D和DMA1都测试了如下四种情况:

  1. 64位带宽的AXI SRAM内部做64KB数据传输。
  2. 32位带宽的D2域SRAM1内部64KB数据传输。
  3. AXI SRAM向SDRAM传输64KB的数据传输。
  4. 32位带宽的SDRAM内部做64KB数据传输。

上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1

 

程序设计:

  系统栈大小分配:

 

  RAM空间用的DTCM:

 

  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
       - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
       - 设置NVIV优先级分组为4。
     */
    HAL_Init();

    /* 
       配置系统时钟到400MHz
       - 切换使用HSE。
       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder并开启 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */       
    bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    bsp_InitTimer();      /* 初始化滴答定时器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    
    bsp_InitLed();        /* 初始化LED */    
     bsp_InitExtSDRAM(); /* 初始化SDRAM */
    
    bsp_InitI2C();     /* 初始化I2C总线 */
}

  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区和D3域的SRAM4。DAC的数据缓存开在了SRAM4。

/*
*********************************************************************************************************
*    函 数 名: MPU_Config
*    功能说明: 配置MPU
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    /* 配置SDRAM的MPU属性为Write through, read allocate,no write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0xC0000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_32MB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 数 名: CPU_CACHE_Enable
*    功能说明: 使能L1 Cache
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 主功能:

主程序实现如下操作:

  •  测试了MDMA,DMA2D和DMA1。
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: c程序入口
*    形    参: 无
*    返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
    bsp_Init();    /* 硬件初始化 */
    PrintfLogo();    /* 打印例程名称和版本等信息 */
    
    MDMA_SpeedTest();
    printf("----------------------------------

");
    DMA2D_SpeedTest();
    printf("----------------------------------

");    
    DMA1_SpeedTest();
    
    bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器,软件定时器0 */
      
      /* 进入主程序循环体 */
    while (1)
    {
        bsp_Idle();
        
        /* 判断软件定时器0是否超时 */
        if(bsp_CheckTimer(0))
        {
            /* 每隔200ms 进来一次 */  
            bsp_LedToggle(2);
        }
    }
}

62.10          实验例程说明(IAR)

配套例子: 

V7-038_MDMA,DMA2D和通用DMA性能比较

实验目的:

  1. 比较MDMA,DMA2D和DMA1的性能

实验内容:

 MDMA,DMA2D和DMA1都测试了如下四种情况:

  1. 64位带宽的AXI SRAM内部做64KB数据传输。
  2. 32位带宽的D2域SRAM1内部64KB数据传输。
  3. AXI SRAM向SDRAM传输64KB的数据传输。
  4. 32位带宽的SDRAM内部做64KB数据传输。

上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1

 

程序设计:

  系统栈大小分配:

 

  RAM空间用的DTCM:

 

  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
       - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
       - 设置NVIV优先级分组为4。
     */
    HAL_Init();

    /* 
       配置系统时钟到400MHz
       - 切换使用HSE。
       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder并开启 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */       
    bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    bsp_InitTimer();      /* 初始化滴答定时器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    
    bsp_InitLed();        /* 初始化LED */    
     bsp_InitExtSDRAM(); /* 初始化SDRAM */
    
    bsp_InitI2C();     /* 初始化I2C总线 */
}

  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区和D3域的SRAM4。DAC的数据缓存开在了SRAM4。

/*
*********************************************************************************************************
*    函 数 名: MPU_Config
*    功能说明: 配置MPU
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    /* 配置SDRAM的MPU属性为Write through, read allocate,no write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0xC0000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_32MB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 数 名: CPU_CACHE_Enable
*    功能说明: 使能L1 Cache
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

  主功能:

主程序实现如下操作:

  •   测试了MDMA,DMA2D和DMA1。
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: c程序入口
*    形    参: 无
*    返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
    bsp_Init();    /* 硬件初始化 */
    PrintfLogo();    /* 打印例程名称和版本等信息 */
    
    MDMA_SpeedTest();
    printf("----------------------------------

");
    DMA2D_SpeedTest();
    printf("----------------------------------

");    
    DMA1_SpeedTest();
    
    bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器,软件定时器0 */
      
      /* 进入主程序循环体 */
    while (1)
    {
        bsp_Idle();
        
        /* 判断软件定时器0是否超时 */
        if(bsp_CheckTimer(0))
        {
            /* 每隔200ms 进来一次 */  
            bsp_LedToggle(2);
        }
    }
}

62.11   总结

本章节涉及到的知识点比较重要,以后用到DMA的地方比较多,可以根据性能选择合适的DMA。

原文地址:https://www.cnblogs.com/armfly/p/12371258.html