6
\$\begingroup\$

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

\$\endgroup\$
4
  • \$\begingroup\$ Realistically search the example code for an example - perhaps as a memcpy() \$\endgroup\$ – Chris Stratton Jan 19 at 18:49
  • 1
    \$\begingroup\$ Are you trying to achieve some kind of DMA driven parallel output using GPIO? Memory-to-memory transfers are not to way to do it. Besides, you need some kind of timing. M2M transfer don't have timing. See ST's AN4666. \$\endgroup\$ – Tagli Jan 19 at 19:40
  • \$\begingroup\$ @Tagli Somehow this is what i want but the way that the source you have mentioned is use is triggering the DMA by Timers and i dont want to have any triggering. So you tell me that its not possible to transfer a data from memory area to GPIO output data register using memory to memory DMA mode? \$\endgroup\$ – Amin Rostami Jan 19 at 19:46
  • 1
    \$\begingroup\$ I'm not sure if there is a HW limitation that prevents to set a peripheral register address as the destination of M2M transfer. But even if it works, I can't see how you are planning to use such a GPIO output with un-controlled timing. \$\endgroup\$ – Tagli Jan 19 at 19:58
9
\$\begingroup\$

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 :)

\$\endgroup\$
3
  • \$\begingroup\$ If you could share your code in a more portable way, like copying it here and use the code blocks, it would be easier to use. \$\endgroup\$ – Arsenal Jan 20 at 9:34
  • \$\begingroup\$ so if I'm right, for the DMA, the source is always considered a peripheral, and destination is any memory location. \$\endgroup\$ – Tirdad Sadri Nejad Jan 20 at 10:33
  • \$\begingroup\$ Instead of literally dividing by "4", you should rather use sizeof Config_Buffer / sizeof Config_Buffer[0], or sizeof Config_Buffer / sizeof (int). BTW, sizeof is an operator, not a function. The parentheses are part of the syntax, if the argument is a type, not an object. \$\endgroup\$ – the busybee Mar 11 at 12:06
6
\$\begingroup\$

You can select any stream you want (of course, DMA2 only). Channel selection is also irrelevant, because channels are related with requests. Notice the title of the Table 43, which is "DMA2 request mapping". Memory-to-memory transfers aren't triggered by DMA requests.

Here is an example of Memory-to-memory transfer:

#define MEMSIZE 32

uint16_t source[MEMSIZE];
uint16_t dest[MEMSIZE];


int main(void) {

    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // Enable DMA2 clock

    // Initialize the test data
    for (int i = 0; i < MEMSIZE; ++i) {
        source[i] = i;
    }

    DMA2_Stream0->CR |= (0b10 << DMA_SxCR_DIR_Pos) // Memory-to-memory Mode
            | (0b01 << DMA_SxCR_PSIZE_Pos) // Source: 16-bit
            | (0b01 << DMA_SxCR_MSIZE_Pos) // Destination: 16-bit
            | DMA_SxCR_PINC // Increment source pointer
            | DMA_SxCR_MINC; // Increment destination pointer
    DMA2_Stream0->PAR = (uint32_t) source;
    DMA2_Stream0->M0AR = (uint32_t) dest;
    DMA2_Stream0->NDTR = MEMSIZE;

    DMA2_Stream0->CR |= DMA_SxCR_EN; // Start DMA transfer

    while (1) {
        __NOP();
    }

}
\$\endgroup\$
2
  • \$\begingroup\$ I tried DMA2 stream0 and it didn't work correctly :(( \$\endgroup\$ – Amin Rostami Jan 19 at 19:00
  • \$\begingroup\$ I updated my answer to include a tested/working example. \$\endgroup\$ – Tagli Jan 20 at 11:31
2
\$\begingroup\$

Any stream can be used.

No DMA request channel is used because the memory-to-memory transfer mode is selected and triggered in software.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ As explained by the reference manual, and DMA application notes, the enable bit (EN) triggers the transfer. \$\endgroup\$ – Justme Jan 19 at 19:31
  • \$\begingroup\$ @Amin Wiggle the correct registers. It will involve writing the source address and destination address, how much data to transfer and probably how wide the datawidth is, and a few control bits to initiate. Read the user reference manual \$\endgroup\$ – DKNguyen Jan 19 at 19:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.