完整教程下载地址: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. [bzoj1005] [洛谷P2624] 明明的烦恼

    Description 自从明明学了树的结构,就对奇怪的树产生了兴趣-- 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 ...

  2. C0nw4y's L!f3 G4me 代码实现

    这是我转载的博客,关于这个游戏的介绍.估计没人能get到这个游戏的blingbling的地方吧.还是蛮惊叹的. 因为这里网络实在惨淡,闲来无事实现了下这个游戏,UI尽量美化了,可惜python配置不知 ...

  3. 实验四:划分多个VLAN

    1.配置图 2.配置命令 Switch1.Switch2.Switch3的配置是一样的,如下所示:(可直接复制交换机,可以只配置一次) 通过命令查看配置: Switch0的配置如下: 通过命令查看tr ...

  4. linux 网络有关的5个命令

    1:ifconfig 2:ifdown & ifup 3:route 4:traceroute 5:iptables 6

  5. 趣学CCNA 路由与交换

    第1章 OSI和TCP/IP 11.1 协议与协议分层 31.2 OSI参考模型 61.2.1 物理层 91.2.2 数据链路层 91.2.3 网络层 101.2.4 传输层 101.2.5 会话层 ...

  6. azure 第一弹

  7. Linux网络文件共享服务之smaba

    一.SAMBA服务简介 samba是1991年由Andrew Tridgel开发实现,主要用于Windows和unix文件共享.samba实现了共享文件和打印,实现在线编辑,登录SAMBA用户的身份认 ...

  8. WEB Node-JS 服务器搭建

    一.创建express 1.创建一个单独文件 2.打开命令面板,进入该文件 3.npm config set registry = https://registry.npm.taobao.org(设置 ...

  9. static静态变量在c++类中的应用实例

    这个static 如果写在类中,那么就可以得到一个局部的静态变量,也就是说可以实现在类内保存某个特殊值不随函数释放而消失的作用.应用中由于赋初值的位置不对而报错,错误提示为:“无法解析外部符号 ... ...

  10. Java:线程的六种状态及转化

    目录 Java:线程的六种状态及转化 一.新建状态(NEW) 二.运行状态(RUNNABLE) 就绪状态(READY) 运行状态(RUNNING) 三.阻塞状态(BLOCKED) 四.等待状态(WAI ...