2016-12-13 10:27 AM
I am trying to implement such an algorithm on Stm32f4-discovery:
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 stoppedThe 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 #interruptSolved! Go to Solution.
2016-12-14 02:20 AM
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
2016-12-13 10:47 AM
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
2016-12-13 12:38 PM
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.
2016-12-14 02:20 AM
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
2016-12-14 08:54 AM
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.