AnsweredAssumed Answered

Stm32f4 DMA doesn't always restart after suspending

Question asked by Kirsanov.Alexey.001 on Dec 13, 2016
Latest reply on Dec 14, 2016 by Kirsanov.Alexey.001

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);
}

Outcomes