cancel
Showing results for 
Search instead for 
Did you mean: 

DMA fails to trigger on STM32F446

luke2
Associate II
Posted on April 30, 2016 at 16:20

I'm using the STM32F446 nucleo board and am trying to simply trigger a DMA transfer onto gpio when an OC triggers. Although I've done this many times on other boards (particularly F0 boards) I cannot figure out why it is not working this time. It seems if I use the same code but switch to TIM1 triggering DMA2 eventd this code works fine, but for TIM3 and TIM4 I get no output on GPIO. The OC is outputting a signal on its AF pin and I can manually set the GPIO output so it seems to be a DMA/triggering issue. The event mappings seem to be correct according to the manual. Is there something different on DMA1 or TIM3/4 when implementing this code as opposed to a DMA2+TIM1 config which works fine???

Any ideas?

uint8_t txbuf[4] = {0xff,0x00,0xff,0xff};

void test() {

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz;

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//OD;//

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//NOPULL;//DOWN;

GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz;

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//OD;//

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//DOWN;

GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3);

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_TimeBaseStructure.TIM_Period = 100-1;

TIM_TimeBaseStructure.TIM_Prescaler = 0;

TIM_TimeBaseStructure.TIM_ClockDivision = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

TIM_OCInitTypeDef  TIM_OCInitStructure;

TIM_OCStructInit(&TIM_OCInitStructure);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//Active;//Toggle;//Timing;

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

TIM_OCInitStructure.TIM_Pulse = 1;

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//Low;//High;

TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;

TIM_OC1Init(TIM3, &TIM_OCInitStructure);

DMA_InitTypeDef DMA_InitStructure;

DMA_Cmd(DMA1_Stream4, DISABLE);

DMA_DeInit(DMA1_Stream4); // Reset DMA channel

DMA_InitStructure.DMA_Channel = DMA_Channel_5;

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(GPIOB->ODR);

DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&txbuf[0];

DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;

DMA_InitStructure.DMA_BufferSize = 4;

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //

DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;

DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;

DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;

DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

DMA_Init(DMA1_Stream4, &DMA_InitStructure);

TIM_DMACmd(TIM3, TIM_DMA_CC1, ENABLE);

TIM_Cmd(TIM3, ENABLE);

DMA_Cmd(DMA1_Stream4, ENABLE);

}

Thanks for your time.
9 REPLIES 9
knielsen
Associate II
Posted on April 30, 2016 at 23:09

Your code sets GPIOB->ODR as the DMA destination. I think you meant to use

GPIOA->ODR ...

Otherwise it looks fine to me, but it's the small things that break,

sometimes...

Hope this helps,

 - Kristian.

mark239955_stm1
Associate II
Posted on May 01, 2016 at 02:59

I haven't worked with a 446, but I encountered the same problem on a 405 and 407.

From what I could work out, with much reading of the reference manual, DMA1 can access a limited subset of the memory space, but DMA2 can access pretty much anywhere.

The 446 might be different though.

luke2
Associate II
Posted on May 01, 2016 at 04:08

Good spot, however unfortunately this is not the problem. I believe I forgot to revert this to GPIOA when I was messing around with ports trying to get it working.

luke2
Associate II
Posted on May 01, 2016 at 04:12

Looking at the manual, the only thing that seems relevant is the statement 'Only the DMA2 controller is able to perform memory-to-memory transfers'. I had presumed outputting to GPIO would be considered a memory to peripheral transfer, perhaps it is actually consider m2m and therefore only possible with DMA2?

Does anybody have more insight on this??

Thanks 

mark239955_stm1
Associate II
Posted on May 01, 2016 at 14:18

Have a look at the DMA ''system implementation'' diagrams in the 407's reference manual.  They're a bit more enlightening than the 446's DMA diagrams.  They make it pretty clear why DMA2's peripheral port can access the entire memory space, but DMA1's can not.

In the case of GPIO being accessible from DMA1, my guess is that it's because the GPIO's are AHB1 peripherals.  You probably can't access any APB2 or AHB2/3 peripherals from it's peripheral port.

luke2
Associate II
Posted on May 01, 2016 at 18:40

This would seem to imply that on the F446 only two timers TIM1+TIM8 can be used to trigger DMA transfers via GPIO. This is surprising to me and quite disappointing. I wonder if other versions of the F4 are a little more flexible?

Posted on May 02, 2016 at 09:20

Chip design is always a tradeoff.

The peripheral port of DMA1 in 'F4 is connected to the APB1 bus (through and arbitrating AHB-to-APB bridge) and can't access anything beyond APB1 peripherals. This is a complication but saves AHB cycles to be spent by the processor.

DMA2's peripheral port is forked into two buses, based on the accessed address -  one goes directly to APB2 in the same fashion as in DMA1, and the other to the bus matrix, where it can access ''everything''.

Look at Figure 4. STM32F446xx and Multi-AHB matrix in the datasheet.

Disappointed? Look at it this way: timer-triggered-GPIO-transfers were probably not the prime reason for DMA. Be glad that there *are* options... 😉

Also, TIM1/TIM8 have quite a couple of DMA streams available, each...

JW

mark239955_stm1
Associate II
Posted on May 02, 2016 at 13:39

I agree with Jan, there's not a lot to be disappointed about here.  Not only is the F4's DMA extremely powerful, you have a lot of options off TIM1/8 to trigger DMA transactions into ANY part of the memory space.

If TIM1/8 aren't an option directly, just get sneaky - you can usually find a path to chain several peripherals together in hardware to get the result that you want.  For example, you can use the other timers that can be configured to be a master timer to TIM1/8 to manipulate TIM1/8 to trigger DMA transactions, so a timer that is nominally an APB1 peripheral can still indirectly reach into the entire memory space via DMA.

luke2
Associate II
Posted on May 03, 2016 at 09:55

You are right of course.. I am generally very happy with the F4 line, it is very powerful and capable. It just happens to be disappointing for my current project in which I am porting from an F3 and use 3 timers to trigger GPIO transfers via DMA. There are tricky ways to achieve the same thing of course, it's just a little annoying coming from the F3 and expecting to have less limitations rather than working around this.

Thanks for all the help, much appreciated!