cancel
Showing results for 
Search instead for 
Did you mean: 

Stm32f4 DMA doesn't always restart after suspending

Alexey Kirsanov
Associate II
Posted on December 13, 2016 at 19:27

I am trying to implement such an algorithm on Stm32f4-discovery:

  1. Initialize the DMA and Tripple-ADC in the interleaved mode on the same channel
  2. Wait for an external interrupt
  3. Suspend the DMA and ADCs
  4. Send the buffered data through USART
  5. Resume DMA and ADCs
  6. Clear interrupt flag, goto 2

The DMA is working in circular mode, not burst, direct mode, peripheral (ADC) to memory, single memory address, second DMA mode (waiting for two measurements, sending a uint32_t to memory).

The problem is that DMA (or ADCs?) does not always restart after suspending. In about 16% of cases DMA NDTR register keeps being stuck at buffer_size - 2 (so it has written just one measurement and stopped) after the interrupt until the next external interrupt, where it is suspended and restarted again.

I've tried suspending DMA just like the Reference manual says:

In order to restart from the point where the transfer was stopped, the software has to read the DMA_SxNDTR register after disabling the stream by writing the EN bit in DMA_SxCR register (and then checking that it is at �0�) to know the number of data items already collected. Then:

� The peripheral and/or memory addresses have to be updated in order to adjust the address pointers

� The SxNDTR register has to be updated with the remaining number of data items to be transferred (the value read when the stream was disabled)

� The stream may then be re-enabled to restart the transfer from the point it was stopped

The only difference between my code and the reference manual recommendations is that I write buffer_size in the NDTR register before restaring the DMA instead of reading the NDTR register before disabling the DMA and then rewriting the read value. If I do the suspending and restarting like the RefMan says, DMA stucks immediately and always, after every interrupt. In my case, the DMA stucks after one transfer and only in 16% cases.

The question is - how can I avoid it being stuck? All of the flags are clear, there are no DMA errors.

My interrupt code looks something like this at the moment:

void EXTI4_IRQHandler(void) {

     uint16_t temp = DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TEIF0);

     if(EXTI_GetITStatus(EXTI_Line4) != RESET) {

         uint16_t fPoint1 = 0;

         uint16_t fPoint2 = 0;

          //Some delay using the TIM2

         TIM_SetCounter(TIM2, 0);

         TIM_Cmd(TIM2, ENABLE);

          //Measure the first point NDTR

         fPoint1 = DMA2_Stream0->NDTR;

         while(TIM_GetITStatus(TIM2, TIM_IT_Update) != SET) {};

          //Measure the second point here.

         fPoint2 = DMA2_Stream0->NDTR;

          if(fPoint1 == fPoint2) {

             //The NDTR does not change!

             //If it does not change, it is stuck at buffer_size - 1

         }

          //Disable the timer

         TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

         TIM_Cmd(TIM2, DISABLE);

          DMA_Cmd(DMA2_Stream0, DISABLE);

         //Wait until the DMA will turn off

         while((DMA2_Stream0->CR & (uint32_t)DMA_SxCR_EN) != 0x00) {};

          //Turn off all ADCs

         ADC_Cmd(ADC1, DISABLE);

         ADC_Cmd(ADC2, DISABLE);

         ADC_Cmd(ADC3, DISABLE);

          //Send all the data here

          //Turn everything back on

          //Turn the DMA ON again

         DMA_SetCurrDataCounter(DMA2_Stream0, BUFFERSIZE);

         DMA_Cmd(DMA2_Stream0, ENABLE);

         while((DMA2_Stream0->CR & (uint32_t)DMA_SxCR_EN) == 0x00) {};

          //See note @ RefMan (Rev. 12), p. 410

         ADC->CCR &= ~((uint32_t)(0x000000FF));

         ADC->CCR |= ADC_TripleMode_Interl;

          ADC_Cmd(ADC1, ENABLE);

         ADC_Cmd(ADC2, ENABLE);

         ADC_Cmd(ADC3, ENABLE);

         while((ADC1->CR2 & (uint32_t)ADC_CR2_ADON) == 0) {};

         while((ADC2->CR2 & (uint32_t)ADC_CR2_ADON) == 0) {};

         while((ADC3->CR2 & (uint32_t)ADC_CR2_ADON) == 0) {};

          ADC_SoftwareStartConv(ADC1);

     }

      EXTI_ClearITPendingBit(EXTI_Line4);

}

#adc #dma #interrupt
1 ACCEPTED SOLUTION

Accepted Solutions
Posted on December 14, 2016 at 10:20

DMA in itself is a rather simple machine.- a 'from' and a 'to' address register, and some logic which, when triggered from outside, performs one transfer 'from' to 'to'. Yes it may come stuck - and this is then indicated in setting one of the status bits you can check in the DMAx_HISR/LISR, and also by clearing the respective DMAx_SyCR.EN bit.

Now the source of triggers/requests here is the ADC unit, which appears to be a significantly more complex beast. I don't have any significant experience with the ADC in 'F4, but  I'd seek the root source of problem there, especially since

Just turning off and then on the ADCs does not do the trick, the transfer does not restart.

I'd start with looking at the ADC's status bits, too.

JW

View solution in original post

4 REPLIES 4
Posted on December 13, 2016 at 19:47

If DMA is circular and your test for being 'stuck' is simply reading NDTR in two randomly chosen points in time, then the probability that you falsely detect it as 'stuck' is 1/N where N is initial NDTR value.

Also, if you just want to suspend DMA for some time and then resume, it may be better to gate the DMA request source.

JW

Posted on December 13, 2016 at 20:38

Nope, I am sure that it is stuck. ADCs dont't write anything in the buffer until next interrupt, as I am filling the buffer with the 0xFFFFFFFFs during the interrupt to test if anything is written to it afterwards.

Besides, the two points aren't random - the are split across a fixed amount of time (about 10ms in my case). And, of course, I've checked everything in the debug mode.

Thank for the gating suggestion, but I am not sure how can I implement that. Can you give me a hint, please? Just turning off and then on the ADCs does not do the trick, the transfer does not restart.

Posted on December 14, 2016 at 10:20

DMA in itself is a rather simple machine.- a 'from' and a 'to' address register, and some logic which, when triggered from outside, performs one transfer 'from' to 'to'. Yes it may come stuck - and this is then indicated in setting one of the status bits you can check in the DMAx_HISR/LISR, and also by clearing the respective DMAx_SyCR.EN bit.

Now the source of triggers/requests here is the ADC unit, which appears to be a significantly more complex beast. I don't have any significant experience with the ADC in 'F4, but  I'd seek the root source of problem there, especially since

Just turning off and then on the ADCs does not do the trick, the transfer does not restart.

I'd start with looking at the ADC's status bits, too.

JW

Posted on December 14, 2016 at 16:54

Thank you very much! I've checked the ADC flags and saw that the ADC overrun error was occurring. So, I added an interrupt to handle it and now the problem is solved completely.