完整教程下载地址: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. 一张图快速上手Xmind思维导图

    使用软件:Xmind8 pro版(可在网上找破解版),应用广泛,功能强大

  2. [洛谷P3621] [APIO2007] 风铃

    Description 你准备给弟弟 Ike 买一件礼物,但是,Ike 挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西. 你准备给 Ike 买一个风铃.风铃是一种多层的装饰品,一般挂在天花 ...

  3. 网鼎杯题目“phone”--十六进制mysql注入

    注册后,即可点击查看谁的电话和我类似. 注册时有三个必填项,分别是用户名.密码和电话.电话要求必须数字. 注册个1111的电话后,点击查看,返回有1个人电话和我类似,在注册一个为1111的,返回有2人 ...

  4. Python环境搭建(win)——Python官方解释器

    Python官方解释器搭建方法: 本文以当前最新的3.8.1为例 1.在电脑上打开Python的官网https://www.python.org/ 2.找到Download下的All releases ...

  5. linux容器技术和Docker

    linux容器技术和Docker 概述 Docker在一定程度上是LXC的增强版,早期的Docker使用LXC作为容器引擎,所以也可以说Docker是LXC的二次封装发行版,目前docker使用的容器 ...

  6. 高阶函数及 map、reduce、filter 的实现

    博客地址:https://ainyi.com/85 2020 开年国家经历了不少困难,最为凶猛的局势就是新型冠状病毒的蔓延,国务院最终决定春节假期延长至==2 月 2 号==:公司决定 3 - 7 号 ...

  7. Maven的scope属性作用域范围

    在POM 4中,<dependency>中还引入了<scope>,它主要管理依赖的部署.目前<scope>可以使用5个值: 1. compile,缺省值,适用于所有 ...

  8. Django报错Watching for file changes with StatReloader

    Django项目运行时出现:Watching for file changes with StatReloader错误 原因:环境里的django或者python的版本有问题 解决方案:升级或者降级D ...

  9. html块级元素的水平垂、直居中的方式

    说明 对于初学者来说,块级元素的剧中,也是一大难题,我学习的时候,也是一脸懵逼,每次遇到都要百度,但是写的多了也自然记住一些常用的剧中方式,但是还是很模糊,今天就来好好总结一些. 布局 布局即为简单, ...

  10. math type白嫖教程

    math type作为一款很方便的公式编辑器,缺陷就是要收费,只有30天的免费试用.这里有一个白嫖的方法,当你30天的期限过了之后,只需要删除HKEY_CURRENT_USER\Software\In ...