完整教程下载地址: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数据。

.    __HAL_RCC_MDMA_CLK_ENABLE();
.
. MDMA_Handle.Instance = MDMA_Channel0;
.
. MDMA_Handle.Init.Request = MDMA_REQUEST_SW; /* 软件触发 */
. MDMA_Handle.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER; /* 块传输 */
. MDMA_Handle.Init.Priority = MDMA_PRIORITY_HIGH; /* 优先级高*/
. MDMA_Handle.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; /* 小端 */
. MDMA_Handle.Init.SourceInc = MDMA_SRC_INC_DOUBLEWORD; /* 源地址自增,双字,即8字节 */
. MDMA_Handle.Init.DestinationInc = MDMA_DEST_INC_DOUBLEWORD; /* 目的地址自增,双字,即8字节 */
. MDMA_Handle.Init.SourceDataSize = MDMA_SRC_DATASIZE_DOUBLEWORD; /* 源地址数据宽度双字,即8字节 */
. MDMA_Handle.Init.DestDataSize = MDMA_DEST_DATASIZE_DOUBLEWORD;/* 目的地址数据宽度双字,即8字节 */
. MDMA_Handle.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; /* 小端,右对齐 */
. MDMA_Handle.Init.SourceBurst = MDMA_SOURCE_BURST_16BEATS; /* 源数据突发传输 */
. MDMA_Handle.Init.DestBurst = MDMA_DEST_BURST_16BEATS; /* 目的数据突发传输 */
.
. MDMA_Handle.Init.BufferTransferLength = ; /* 每次传输128个字节 */
.
. MDMA_Handle.Init.SourceBlockAddressOffset = ; /* 用于block传输,地址偏移0 */
. MDMA_Handle.Init.DestBlockAddressOffset = ; /* 用于block传输,地址偏移0 */
.
. /* 初始化MDMA */
. if(HAL_MDMA_Init(&MDMA_Handle) != HAL_OK)
. {
. Error_Handler(__FILE__, __LINE__);
. }

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

  • 第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, , );
HAL_NVIC_EnableIRQ(MDMA_IRQn); void MDMA_IRQHandler(void)
{
HAL_MDMA_IRQHandler(&MDMA_Handle);
}
static void MDMA_TransferCompleteCallback(MDMA_HandleTypeDef *hmdma)
{
TransferCompleteDetected = ;
}

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

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

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

TransferCompleteDetected = ;
HAL_MDMA_Start_IT(&MDMA_Handle,
(uint32_t)0x24000000,
(uint32_t)(0x24000000 + *),
*,
); start = DWT_CYCCNT;
while(TransferCompleteDetected == ) {}
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\r\n", cnt/, /cnt);

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

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

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

TransferCompleteDetected = ;
HAL_MDMA_Start_IT(&MDMA_Handle,
(uint32_t)0x30000000,
(uint32_t)(0x30000000 + *),
*,
); start = DWT_CYCCNT;
while(TransferCompleteDetected == ) {}
end = DWT_CYCCNT;
cnt = end - start; printf("MDMA---D2域SRAM1内部互传64KB数据耗时 = %dus %dMB/S\r\n", cnt/, /cnt);

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

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

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

TransferCompleteDetected = ;
HAL_MDMA_Start_IT(&MDMA_Handle,
(uint32_t)0x24000000,
(uint32_t)0xC0000000,
*,
); start = DWT_CYCCNT;
while(TransferCompleteDetected == ) {}
end = DWT_CYCCNT;
cnt = end - start; printf("MDMA---AXI SRAM传输64KB数据到SDRAM耗时 = %dus %dMB/S\r\n", cnt/, /cnt);

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

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

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

TransferCompleteDetected = ;
HAL_MDMA_Start_IT(&MDMA_Handle,
(uint32_t)0xC0000000,
(uint32_t)(0xC0000000 + *),
*,
); start = DWT_CYCCNT;
while(TransferCompleteDetected == ) {}
end = DWT_CYCCNT;
cnt = end - start; printf("MDMA---SDRAM内部互传64KB数据耗时 = %dus %dMB/S\r\n", cnt/, /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 = ;
DMA2D->OOR = ; /* 前景层和输出区域都采用的ARGB8888颜色格式 */
DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_ARGB8888;
DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_ARGB8888; DMA2D->NLR = (uint32_t)( << ) | (uint16_t);

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 + *);
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\r\n", cnt/, /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 + *);
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\r\n", cnt/, /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\r\n", cnt/, /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 + *);
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\r\n", cnt/, /cnt);

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

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

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

62.5.1 第1步,DMA1初始化

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

.    __HAL_RCC_DMA1_CLK_ENABLE();
.
. DMA_Handle.Instance = DMA1_Stream1;
. DMA_Handle.Init.Request = DMA_REQUEST_MEM2MEM;
. DMA_Handle.Init.Direction = DMA_MEMORY_TO_MEMORY;
. DMA_Handle.Init.PeriphInc = DMA_PINC_ENABLE;
. DMA_Handle.Init.MemInc = DMA_MINC_ENABLE;
. DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
. DMA_Handle.Init.MemDataAlignment = DMA_PDATAALIGN_WORD;
. DMA_Handle.Init.Mode = DMA_NORMAL;
. DMA_Handle.Init.Priority = DMA_PRIORITY_VERY_HIGH;
. DMA_Handle.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
. DMA_Handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
. DMA_Handle.Init.MemBurst = DMA_MBURST_INC4; /*WORD方式,仅支持4次突发 */
. DMA_Handle.Init.PeriphBurst = DMA_PBURST_INC4; /*WORD方式,仅支持4次突发 */
. DMA_Handle.XferCpltCallback = DMA_TransferCompleteCallback;
.
. 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, , );
HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn); void DMA1_Stream1_IRQHandler(void)
{
HAL_DMA_IRQHandler(&DMA_Handle);
}
static void DMA_TransferCompleteCallback(DMA_HandleTypeDef *hdma)
{
TransferCompleteDetected = ;
}

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

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

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

/* AXI SRAM的64KB数据传输测试 ***********************************************/
TransferCompleteDetected = ;
HAL_DMA_Start_IT(&DMA_Handle, (uint32_t)0x24000000, (uint32_t)(0x24000000 + *), *); start = DWT_CYCCNT;
while(TransferCompleteDetected == ) {}
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\r\n", cnt/, /cnt);

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

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

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

/* D2域SRAM1的64KB数据传输测试 ***********************************************/
TransferCompleteDetected = ;
HAL_DMA_Start_IT(&DMA_Handle, (uint32_t)0x30000000, (uint32_t)(0x30000000 + *), *); start = DWT_CYCCNT;
while(TransferCompleteDetected == ) {}
end = DWT_CYCCNT;
cnt = end - start; printf("DMA1---D2域SRAM1内部互传64KB数据耗时 = %dus %dMB/S\r\n", cnt/, /cnt);

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

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

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

/* AXI SRAM向SDRAM的64KB数据传输测试 ***********************************************/
TransferCompleteDetected = ;
HAL_DMA_Start_IT(&DMA_Handle, (uint32_t)0x24000000, (uint32_t)0xC0000000, *); start = DWT_CYCCNT;
while(TransferCompleteDetected == ) {}
end = DWT_CYCCNT;
cnt = end - start; printf("DMA1---AXI SRAM传输64KB数据到SDRAM耗时 = %dus %dMB/S\r\n", cnt/, /cnt);

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

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

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

/* SDRAM的64KB数据传输测试 ***********************************************/
TransferCompleteDetected = ;
HAL_DMA_Start_IT(&DMA_Handle, (uint32_t)0xC0000000, (uint32_t)(0xC0000000 + *), *); start = DWT_CYCCNT;
while(TransferCompleteDetected == ) {}
end = DWT_CYCCNT;
cnt = end - start; printf("DMA1---SDRAM内部互传64KB数据耗时 = %dus %dMB/S\r\n", cnt/, /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("----------------------------------\n\r");
DMA2D_SpeedTest();
printf("----------------------------------\n\r");
DMA1_SpeedTest(); bsp_StartAutoTimer(, ); /* 启动1个200ms的自动重装的定时器,软件定时器0 */ /* 进入主程序循环体 */
while ()
{
bsp_Idle(); /* 判断软件定时器0是否超时 */
if(bsp_CheckTimer())
{
/* 每隔200ms 进来一次 */
bsp_LedToggle();
}
}
}

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("----------------------------------\n\r");
DMA2D_SpeedTest();
printf("----------------------------------\n\r");
DMA1_SpeedTest(); bsp_StartAutoTimer(, ); /* 启动1个200ms的自动重装的定时器,软件定时器0 */ /* 进入主程序循环体 */
while ()
{
bsp_Idle(); /* 判断软件定时器0是否超时 */
if(bsp_CheckTimer())
{
/* 每隔200ms 进来一次 */
bsp_LedToggle();
}
}
}

62.11   总结

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

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

  1. 【STM32H7教程】第61章 STM32H7的MDMA基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第61章       STM32H7的MDMA基础知识和HAL ...

  2. 【STM32H7教程】第58章 STM32H7的硬件JPEG应用之图片解码显示

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第58章       STM32H7的硬件JPEG应用之图片解 ...

  3. 【STM32H7教程】第3章 STM32H7整体把控

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第3章   STM32H7整体把控 初学STM32H7一 ...

  4. 【STM32H7教程】第14章 STM32H7的电源,复位和时钟系统

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第14章       STM32H7的电源,复位和时钟系 ...

  5. 【STM32H7教程】第22章 STM32H7的SysTick实现多组软件定时器

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第22章       STM32H7的SysTick实现 ...

  6. 【STM32H7教程】第33章 STM32H7的定时器应用之TIM1-TIM17的中断实现

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第33章       STM32H7的定时器应用之TIM1-T ...

  7. 【STM32H7教程】第25章 STM32H7的TCM,SRAM等五块内存基础知识

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第25章       STM32H7的TCM,SRAM等五块内 ...

  8. 【STM32H7教程】第47章 STM32H7的FMC总线基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第47章       STM32H7的FMC总线基础知识和HA ...

  9. 【STM32H7教程】第57章 STM32H7硬件JPEG编解码基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第57章       STM32H7硬件JPEG编解码基础知识 ...

随机推荐

  1. 9.Java三大版本以及JDK,JRE,JVM简单介绍

    Write Once,Run Anywhere. JavaSE:标准版(桌面程序,控制台开发...),是Java的基础和核心. JavaME:嵌入式开发(手机,小家电...),现在基本不用已经过时. ...

  2. [bzoj2186] [洛谷P2155] [Sdoi2008] 沙拉公主的困惑

    Description 大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为1到N的阶乘,但是,政府只发行编号与M!互质的钞票.房地产第一大户沙拉公主决定预测一下大富翁国现 ...

  3. mysql使用唯一索引避免插入重复数据

    使用MySQL 索引防止一个表中的一列或者多列产生重复值 一:介绍MYSQL唯一索引 如果要强烈使一列或多列具有唯一性,通常使用PRIMARY KEY约束. 但是,每个表只能有一个主键. 因此,如果使 ...

  4. 《编写高质量代码:改善C#程序的157个建议》是C#程序员进阶修炼的必读之作

  5. Redis(七):set/sadd/sismember/sinter/sdiffstore 命令源码解析

    上两篇我们讲了hash和list数据类型相关的主要实现方法,同时加上前面对框架服务和string相关的功能介绍,已揭开了大部分redis的实用面纱. 现在还剩下两种数据类型: set, zset. 本 ...

  6. robotframework从列表中循环读取数据,传入关键字执行

    场景预设:从列表内读取手机号,自动化执行微信加好友,直至选择完所有数据后,脚本停止执行 1.建一个备选数据表,表内列待添加的手机号数据 2.脚本的主要流程新加好友-输入手机号-添加好友-判断好友是否存 ...

  7. GC原理---垃圾收集器

    垃圾收集器 如果说收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现 Serial收集器 串行收集器是最古老,最稳定以及效率高的收集器,可能会产生较长的停顿,只使用一个线程去回收.新生代.老 ...

  8. selenium8中元素定位方式

    Selenium对网页的控制是基于各种前端元素的,在使用过程中,对于元素的定位是基础,只有准去抓取到对应元素才能进行后续的自动化控制,我在这里将对各种元素定位方式进行总结归纳一下. 这里将统一使用百度 ...

  9. 如何播放 WAV 文件?

    from http://www.vckbase.com/index.php/wv/434 平时,你在多媒体软件的设计中是怎样处理声音文件的呢?使用Windows 提供的API函数 sndPlaySou ...

  10. Dubbo学习系列之十七(微服务Soul网关)

    论起微服务,哪能不谈网关,老将有Zuul,后继有Gateway,但这些都和SpringCloud关系密切,其他网关如Kong,因Lua原因,玩起来略不顺手.这不,就来了个Soul,我顺便拿来整进了我在 ...