Electronic – Memory To Memory DMA on STM32

armcmsisdmamicrocontrollerstm32

According to STM32F407 reference manual page 313, memory to memory mode in DMA is a mode that doesn't need any triggering request from a peripheral and it will happen just after the stream enable bit is set. (also we know from the reference that only the DMA2 could handle memory to memory data transfer) :

enter image description here

So the question is for a transfer between two integer array defined in the memory area, which stream of DMA2 must be enabled and which channel of this stream must be used to have a DMA transfer without any peripheral triggering and only when enabling the DMA stream?

enter image description here

In fact what i want is sending some data from a buffer array in the memory area to a GPIO output data register. As is said what i expect to happens is when stream is enabled the transaction just happens but the result was not what i expected and no data is sent to that GPIO output data register. I want to use memory to memory DMA mode because it doesn't need any extra triggering

Best Answer

After one day really hard challenging with the problem! i finally figured out some important points that is i think will be worthy for other people out there so i decided to represent them:

  1. The STM32's DMA is a very quaint tool that i today realized that it can handle almost every kind of data transmission from a memory area to another memory area(such as moving one array members to another one) or from a memory area to a control register of a peripheral(for example i tried to config and initialize a GPIO by DMA and it works absolutely accurate!). The code is placed in the following and that is for STM32F407 Disco Board(PD12 to PD15 are connected to on board LEDs).

  2. We can handle this goal(GPIO initializing using DMA) in either "Memory to Peripheral" mode with a Peripheral request triggering or in "Memory to Memory" mode without any triggering and just after enabling the DMA stream(no matter which stream and all do the job perfectly)

  3. sizeof() operator returns size of an array in bytes, to determine the number of elements in an integer array you must divide the result by 4

  4. The most important point that really annoyed me is that in "Memory to Memory" mode , the DMA peripheral port is the Source of transition and Memory port is the Destination of transition. See the Arrows direction below:

enter image description here

And finally i share my code that does the GPIO Initialization job:

//****** This program is a creative way to Initialize and Config a GPIO on STM32F407 Disco Board (GPIOD Pin_12, Pin_13, Pin_14, Pin_15) using DMA (Memory to Memory mode)  ******//
#include "stm32f4xx.h"
#include "stm32f4xx_dma.h"
#include "stm32f4xx_rcc.h"
//---------- Forward Declaration -------//
void DMA2_GPIOD_Initializer(void);


//----------- Variables Definition ------//
#define  MODER     0x55<<24 //sets direction of PORTD  Pin_12, Pin_13, Pin_14, Pin_15 to output  
#define  OTYPER    0<<12    //sets GPIOD Pin_12, Pin_13, Pin_14, Pin_15 as push-pull
#define  OSPEEDER  0        //sets GPIO Pins OSpeed in Low Speed
#define  PUPDR     0        //sets no Pull resistor to pins
#define  IDR       0
#define  ODR       0xF<<12  // sets Pin_12, Pin_13, Pin_14, Pin_15 to High

int Config_Buffer[] = {MODER,OTYPER,OSPEEDER,PUPDR,IDR,ODR};

int main(void)
{

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);
DMA2_GPIOD_Initializer();

while(1)
{
    
}
}


//------------------- DMA2_GPIOD_Initializer ----------------------//
void DMA2_GPIOD_Initializer(void)
{
DMA_InitTypeDef  DMA_InitStruct;

//Clock for DMA2
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);

//Stream_7 initializing(Each stream can do the job and you can choose one arbitrary)
DMA_InitStruct.DMA_Channel            = DMA_Channel_0;//In Memory to Memory mode channel number isn't important and has no effect
DMA_InitStruct.DMA_Priority           = DMA_Priority_High;
DMA_InitStruct.DMA_DIR                = DMA_DIR_MemoryToMemory;

/* Be very careful that in Memory to Memory mode Peripheral is the SOURCE 
memory*/
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)Config_Buffer;    
DMA_InitStruct.DMA_Memory0BaseAddr    = (uint32_t)(GPIOD_BASE);

DMA_InitStruct.DMA_BufferSize         = sizeof(Config_Buffer)/4;//"sizeof()"function determines the size of array in (bytes) But we need To know the number of int(4bytes) in array
                                                                // so we must to devide sizeof(Config_Buffer) result by 4                                                             
DMA_InitStruct.DMA_MemoryDataSize     = DMA_MemoryDataSize_Word;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStruct.DMA_MemoryInc          = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralInc      = DMA_PeripheralInc_Enable;
DMA_InitStruct.DMA_MemoryBurst        = DMA_MemoryBurst_Single;
DMA_InitStruct.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single;
DMA_InitStruct.DMA_FIFOMode           = DMA_FIFOMode_Enable;
DMA_InitStruct.DMA_FIFOThreshold      = DMA_FIFOThreshold_1QuarterFull;
DMA_InitStruct.DMA_Mode               = DMA_Mode_Normal;

DMA_Cmd(DMA2_Stream7,DISABLE);
while(DMA_GetCmdStatus(DMA2_Stream7));/**Before calling DMA_Init() function,it is recommended to check that the Stream  is actually disabled using the function DMA_GetCmdStatus().*/                                                                                    

/**All the stream dedicated bits set in the status register (DMA_LISR and 
DMA_HISR) from the previous data block DMA transfer should be cleared before the 
stream could be re-enabled. *For More informations refer to Reference manual Page 324*/
DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7|DMA_FLAG_HTIF7|DMA_FLAG_TEIF7|DMA_FLAG_DMEIF7|DMA_FLAG_FEIF7);

DMA_Init(DMA2_Stream7,&DMA_InitStruct);
DMA_Cmd(DMA2_Stream7,ENABLE);
}

At the end im sorry for my not so good English and i appreciate whom corrects my mistakes. My main aim was just to share my knowledge. Thanks for reading :)