So this question is kind of a "sequel" of this one: Stm32f4: DMA + ADC Transfer pausing.
Again, I am trying to implement such an algorithm:
- Initialize DMA with ADC in tripple interleaved mode on one channel
- Wait for an external interrupt
- Suspend DMA transfer and the ADC
- Send the buffered data from memory through USART in the interrupt
- Resume the DMA and ADCs
- Exit the interrupt, goto 2.
The DMA and ADC suspends and resumes, but sometimes (in about 16% of interrupt calls) the resuming fails - DMA just writes first measurement from the ADCs and stops until next interrupt, in which DMA and ADC are restarted (since they are suspended and resumed again) and - well, everything returns back to normal until next such bug.
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 actual difference is in the written NDTR value while resuming DMA working. In my case it is buffer_size, in the RefMan case - it is the value read while pausing the DMA. In the RefMan case, DMA never starts again after pausing. In my case, as I said above, it starts, but not always.
How can I prevent this from happening?
The interrupt code looks like this currently:
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);
}