cancel
Showing results for 
Search instead for 
Did you mean: 

Problem with DMA-USART Rx on STM32F407VG ?

eraycanli
Associate II
Posted on October 01, 2013 at 09:58

Hello, I am trying to read data from USART using DMA. Receive buffer (DMA_BufferSize) in my code is 4 bytes, but even when 1 byte of data received, MCU is interrupted with Transfer Complete flag (DMA_IT_TCIF1). Giving the code below, can you please check what is wrong with configuration?

STM32F4 Discovery (STM32F407VG)

/* Includes ------------------------------------------------------------------*/

#include ''main.h''

#include ''string.h''

#include <stdlib.h>

#include ''stdio.h''

#include ''float.h''

/*User variables -------------------------------------------------------------*/

#define RXBUFFERSIZE 4

uint8_t RxBuffer[RXBUFFERSIZE];

/* Private functions ---------------------------------------------------------*/

//initialization of USART3 used with DMA

void init_usart3_dma(void)

{

  GPIO_InitTypeDef GPIO_InitStruct;

  NVIC_InitTypeDef NVIC_InitStructure;

  USART_InitTypeDef USART_InitStruct;

  DMA_InitTypeDef DMA_InitStructure;

  

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //USART3 Clock Enable

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //GPIOB Clock Enable

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); //DMA1 Clock Enable

 

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;

  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;

  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;

  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;

  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;  

  GPIO_Init(GPIOB, &GPIO_InitStruct);

  

  GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3); //USART3 Tx Pin

  GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3); //USART3 Rx Pin

  

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

  

  // Enable the USART3 RX DMA Interrupt

  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream1_IRQn;

  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStructure);

  

  USART_InitStruct.USART_BaudRate = 19200;

  USART_InitStruct.USART_WordLength = USART_WordLength_8b;

  USART_InitStruct.USART_StopBits = USART_StopBits_1;

  USART_InitStruct.USART_Parity = USART_Parity_No;

  USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

  USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

  

  USART_Init(USART3, &USART_InitStruct);

  

  USART_Cmd(USART3, ENABLE); // Enable USART3

  // ----- USART3 DMA Rx Stream Init -----

  DMA_DeInit(DMA1_Stream1);

  

  DMA_InitStructure.DMA_Channel = DMA_Channel_4;

  DMA_InitStructure.DMA_PeripheralBaseAddr = USART3_BASE;

  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RxBuffer;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // Receive From Per to Mem

  DMA_InitStructure.DMA_BufferSize = RXBUFFERSIZE;

  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_Normal;

  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;

  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; // ??????

  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;

  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

  

  DMA_Init(DMA1_Stream1, &DMA_InitStructure);

  

  USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE); // Enable USART Rx DMA Request

  DMA_ITConfig(DMA1_Stream1, DMA_IT_TC, ENABLE); // Enable Transfer Complete Interrupt

  DMA_Cmd(DMA1_Stream1, ENABLE); // Enable DMA Rx Stream

}

int main(void)

{

  init_usart3_dma();

  

  while(1)

  {

  }

}

void DMA1_Stream1_IRQHandler(void) // USART3 RX DMA Transfer Complete Interrupt

{

  if (DMA_GetITStatus(DMA1_Stream1, DMA_IT_TCIF1))

  {

// ----------

    DMA_ClearITPendingBit(DMA1_Stream1, DMA_IT_TCIF1);

  }

5 REPLIES 5
Posted on October 01, 2013 at 10:12

Generally doesn't look bad

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART3->DR; // Might be more appropriate and correct address

I might also add some work after clearing the TC flags so as to avoid the hazard with the pipeline, NVIC, and tail-chaining.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
eraycanli
Associate II
Posted on October 01, 2013 at 10:39

thanks, this seems to solve the problem!

what do you mean by saying 'hazard with the pipeline, NVIC, and tail-chaining' ? can you explain a bit?

Posted on October 01, 2013 at 11:16

I've done so before, you might want to Google it.

Basically the pipeline in the processor decides what interrupt to service next before the clearing of the interrupt propagates back from external peripherals on the APB/AHB buses, and back into the NVIC. The latency will depend on the speed disparity of those buses, futher impacted by the write buffer on the processor. The effect will be that the processor will re-enter an interrupt service routine almost immediately with what looks like a spurious interrupt which will not be signalled in the status register as that read will have fenced the pending write, which may also have already completed. It's a classic race condition, the pipeline gives the appearance of single cycle execution (throughput), but the latency is much higher. So you either need to dwell for a couple of cycles to accommodated the pipeline/write buffer, or fence the write with some other reads/writes to enforce in-order completion. If you do anything remotely useful in the Handler you won't see this.

void DMA1_Stream1_IRQHandler(void) // USART3 RX DMA Transfer Complete Interrupt
{
if (DMA_GetITStatus(DMA1_Stream1, DMA_IT_TCIF1)) // Guarded against spurious re-entry
{
DMA_ClearITPendingBit(DMA1_Stream1, DMA_IT_TCIF1); // Clear Early
// ----------
// Do something useful here, toggle a GPIO, anything
}
}

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
eraycanli
Associate II
Posted on October 02, 2013 at 13:55

okay, that sounds useful.

so, when using DMA in Normal Mode, what should be done in interrupt to reset Buffer and DMA to get ready for next data packet? is Disable/Enable logical?

eraycanli
Associate II
Posted on October 25, 2013 at 17:27

I have another question related to DMA Rx and decide not to create a new thread 🙂

What is wrong with behaviour of Half Transfer interrupt? I created a Normal Buffer of 10 bytes for DMA and HT interrupt is reached at 10 bytes, not 5. What could be the problem?