Skip to main content
gbigden
Associate III
November 12, 2015
Question

DMA fails to complete (STM32F303x)

  • November 12, 2015
  • 10 replies
  • 1871 views
Posted on November 12, 2015 at 10:26

This is a rather complex problem. I have an interrupt routine running under the systick handler with a period of 10mS. Within this I do 4096 reads of ADC3 via DMA2_Channel5, which works fine. Single shot mode

The problem comes when I have data coming in from USART2 via DMA1_Channel6. Occasionally DMA2 fails to complete the transfer, that is, it's counter decrements but does not get to zero.

AFAIK I have set up the interrupts with systick having the highest priority, followed by DMA2 and then DMA1

Any ideas as to what might be happening?

#triage-nurse #stm32
    This topic has been closed for replies.

    10 replies

    gbigden
    gbigdenAuthor
    Associate III
    November 12, 2015
    Posted on November 12, 2015 at 16:21

    If I add a 500uS delay at the end of my DMA setup the problem disappears - any ideas why?

    _____________________________

    void DMA2_ADC3_Config(void)

    {

      DMA_InitTypeDef   DMA_InitStructure;

      int numSamples;

     

      numSamples = sizeof( Receive.rawData ) / sizeof( Receive.rawData[0]);

         

        // Enable DMA2 clock

      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);

      DMA_DeInit(DMA2_Channel5);

      

      DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &ADC3->DR;

      DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Receive.rawData;       

      DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

      DMA_InitStructure.DMA_BufferSize = numSamples; 

      DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

      DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

      DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;       //Set for 16 bit

      

      DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;                   

      DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

      DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;

      DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

      

      DMA_Init(DMA2_Channel5, &DMA_InitStructure);

      delayMicrosecondsT3(500);

    }

    _____________

    dhiry2k1
    Associate II
    November 12, 2015
    Posted on November 12, 2015 at 18:56

    Try binding ADC using DMA interrupts ... I had this problem using USART. If you notice , one or 2 bytes will always gets stuck in buffer.

    gbigden
    gbigdenAuthor
    Associate III
    November 13, 2015
    Posted on November 13, 2015 at 12:49

    Hi - Not sure how to do that. I have set up the DMA to deliver 4096 samples as fast as the ADC can deliver them. I have now reduced the delay to 50uS and it still works perfectly. I would like to know why.

    gbigden
    gbigdenAuthor
    Associate III
    November 18, 2015
    Posted on November 18, 2015 at 12:35

    Can I please get some help on this. Having to put in a delay is causing serious problems elsewhere

    gbigden
    gbigdenAuthor
    Associate III
    November 18, 2015
    Posted on November 18, 2015 at 16:23

    Full code - which works, unless I remove the delay

    void DMA2_Channel5_Config(void)

    {

      DMA_InitTypeDef   DMA_InitStructure;

      int numSamples;

     

      numSamples = sizeof( Receive.rawData ) / sizeof( Receive.rawData[0]);

         

        // Enable DMA2 clock

      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);

      DMA_DeInit(DMA2_Channel5);

      

      DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &ADC3->DR;

      DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Receive.rawData;       

      DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

      DMA_InitStructure.DMA_BufferSize = numSamples; 

      DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

      DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

      DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;       //Set for 16 bit

      

      DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;                   

      DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

      DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;

      DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

      

      DMA_Init(DMA2_Channel5, &DMA_InitStructure);

      DMA_Cmd( DMA2_Channel5, ENABLE );

      delayMicrosecondsT3(50);                      //NOTE!!!!!!!!!!!!!!!!!!!!!!!!!!! No idea why this is needed, but without it the ADC/DMA sometimes never completes and the data collection hangs up

    }

      //******************************************************************************

      // Set up ADC3 to do DMA conversions of sound reflections to memory

      // Note - Only some DMA and channels mapped to ADC numbers

      // DMA1_Ch1 -> ADC1

      // DMA2_Ch1 -> ADC2

      // DMA2_Ch2 -> ADC4

      // DMA2_Ch3 -> ADC2

      // DMA2_Ch4 -> ADC4

      // DMA2_Ch5 -> ADC3

      //******************************************************************************

      

    void ADC3_Go( void )

    {

      ADC_InitTypeDef ADC_InitStructure;

      int ADC_Sample_Time;

      RCC_ADCCLKConfig(RCC_ADC34PLLCLK_Div1);      

      RCC_AHBPeriphClockCmd( RCC_AHBPeriph_ADC34, ENABLE );

      ADC_StructInit( &ADC_InitStructure );

      ADC_InitStructure.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Enable;

      ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; 

      ADC_InitStructure.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_0;         

      ADC_InitStructure.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_None;

      ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

      ADC_InitStructure.ADC_OverrunMode = ADC_OverrunMode_Disable;   

      ADC_InitStructure.ADC_AutoInjMode = ADC_AutoInjec_Disable; 

      ADC_InitStructure.ADC_NbrOfRegChannel = 1;

      ADC_Init(ADC3, &ADC_InitStructure); 

      ADC_Sample_Time = MachineState.ADC_Sample_Time;                       //Actual conversion time is 12.5 clock cycles + ADC_Sample_Time (usually 1.5 cycles) = 14 cycles

      ADC_RegularChannelConfig(ADC3, ADC_Channel_1, 1, ADC_Sample_Time);    

       

      DMA2_Channel5_Config(); 

      ADC_Cmd( ADC3, ENABLE );      //Enable ADC3 

      ADC_DMACmd( ADC3, ENABLE );   //Enable ADC3 DMA 

          

      while( !ADC_GetFlagStatus( ADC3, ADC_FLAG_RDY ) );    // wait for ADRDY 

      ADC_StartConversion( ADC3 );  // Start ADC3 Software Conversion 

     

    }

    dthedens
    Visitor II
    November 18, 2015
    Posted on November 18, 2015 at 16:37

    Do you call the function DMA2_ADC3_Config() repetitively?

    I would enable the clock once and only once in main()

    Maybe you are trying to be safe by DMA_DeInit() but I doubt it is that useful.

    Using DMA with UART input is tricky.  If there are errors, the DMA might not finish.

    there is a useful app note about using a timer to monitor activity on the UART pin which can provide a recovery mechanism when errors occur.  Forgot the AN number

    Anyway, AN4031 is a good read.

    gbigden
    gbigdenAuthor
    Associate III
    November 18, 2015
    Posted on November 18, 2015 at 16:42

    I use DMA1 with the USART and DMA2 is reserved for ADC3

    Tesla DeLorean
    Guru
    November 18, 2015
    Posted on November 18, 2015 at 17:20

    Full code...

    Um, no.

    I'm not sure of the value of tearing down everything to restart the DMA, and if that is necessary, but the delay is indicative of a race condition, where you want to ensure this path loses. You'll need to look carefully at the order and sequence of things. Consider moving the clocks outside this level, and just what it takes to Init the DMA a second time around.

    You need to condense your failure condition into a complete/concise example, if I can't see a way to replicate/identify your issue in five minutes or less, I'm not going to even start looking. I think you need to figure out who's going to help you, and how much context and framework that's going to take.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    gbigden
    gbigdenAuthor
    Associate III
    November 19, 2015
    Posted on November 19, 2015 at 16:03

    I have followed your suggestions and moved various initializations outside the loop. Still the same problem, which seems to stem from this code, in that when I do not use it there is no problem. This is set up during initialization and is not touched again.

    void DMA_ConfigurationUSART2(void)

    {

      DMA_InitTypeDef  DMA_InitStructure;

      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); 

      

     

      // USART2_RX DMA1 Channel 6 

     

      DMA_DeInit(DMA1_Channel6); 

      DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                            // Receive

      DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&PacketIn.buffer;

      DMA_InitStructure.DMA_BufferSize = sizeof( PacketIn.buffer );

      DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART2->RDR;

      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_Mode = DMA_Mode_Circular;

      DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

      DMA_InitStructure.DMA_Priority = DMA_Priority_Low;

     

      DMA_Init(DMA1_Channel6, &DMA_InitStructure);

     

      // Enable the USART Rx DMA request 

      USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);

     

      // Enable DMA Stream Transfer Complete interrupt 

      DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);

     

      // Enable the DMA RX Stream 

      DMA_Cmd(DMA1_Channel6, ENABLE);

    }

     

     

    void DMA1_Channel6_IRQHandler(void) //RX

    {

      //Test on DMA Stream Transfer Complete interrupt 

      if (DMA_GetITStatus(DMA1_IT_TC6))

      {

          // Clear DMA Stream Transfer Complete interrupt pending bit 

        DMA_ClearITPendingBit(DMA1_IT_TC6);

        

        if (PacketIn.rxFull == true) 

          return;        //Because we have not yet handled the other requests

        else

          PacketIn.rxFull = true;

      }

    }

    gbigden
    gbigdenAuthor
    Associate III
    November 20, 2015
    Posted on November 20, 2015 at 13:15

    The answer appears to be to change this line

      ADC_InitStructure.ADC_OverrunMode = ADC_OverrunMode_Disable;   

    to this

      ADC_InitStructure.ADC_OverrunMode = ADC_OverrunMode_Enable;