1
votes

My purpose is sampling signal by ADC channel with DMA data moving in STM32Fx board. Generate a square wave to ADC channel. If using DMA mode, some data is out of order or called mess. Same result happened on STM32F207 and STM32F373 board.

(1) When I collect converted data by using ADC EOC interrupt, the data array looks like a square wave. This is OK.

(2) I would like to try DMA circle instead of EOC IRQ, but the data array seems to mess up, some data missing or incorrect. It could worse if sampling rate was increasing. Below are my test results.

The picture shows my test result: EOC IRQ vs DMA circle mode

EOC IRQ vs DMA with sampling 62.5KHz The waveform in DMA became shorter.

The picture shows very worse DMA with sampling 200KHz The data on DMA mode mess up, but it's consistent by using ADC EOC IRQ.

enter code here

<<<< ADC config >>>>

/* ADC Common Init */
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = 
ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);

/* ADC1 DeInit */
ADC_StructInit(&ADC_InitStructure);

/* Configure the ADC1 in continuous mode */
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);

/* ADC1 regular channels 6 configuration */ 
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, 
ADC_SampleTime_480Cycles);
ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);

#ifdef __DMA_ENABLE__

/* Enable DMA request after last transfer (Single-ADC mode) */
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);

/* Enable ADC1 DMA since ADC1 is the Master*/
ADC_DMACmd(ADC1, ENABLE);

#else

ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
ADC_ITConfig(ADC1, ADC_IT_OVR, ENABLE);

NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

#endif

/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);


<<<< DMA config >>>>

/* DMA1 clock enable */
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_DMA2, ENABLE );
/* DMA1 Channel1 Config */
DMA_DeInit(DMA2_Stream0);

DMA_DoubleBufferModeConfig(DMA2_Stream0, (uint32_t)ADCValB, DMA_Memory_1);
DMA_DoubleBufferModeCmd(DMA2_Stream0, ENABLE);

DMA_InitStructure.DMA_Channel = DMA_Channel_0; 
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ADCValA;
DMA_InitStructure.DMA_PeripheralBaseAddr = ((uint32_t) ADC1) + 0x4C;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = NUM_OF_ADC; // 512 buffer size
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &DMA_InitStructure);

DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

/* DMA1 Channel1 enable */
DMA_Cmd(DMA2_Stream0, ENABLE);

Finally, I expect the result should be same as that using ADC EOC IRQ.

2
Why do you think the data is out of order? It looks good to me: an alternating sequence of about four values close to 30 and four values close to 1130. What else do you expect? And how do you generate and feed the square wave?Codo
(1) It will worse if I increase sampling rate. The data array in DMA will not be consistent and periodical (2) square wave is generated by STM32 DAC itself.Peter Lu

2 Answers

0
votes

I have encountered the same problem, as a part of my complicated application, the ADC reads continuously an input PWM data and configured with DMA request in circular mode.

  • First, I tried to store the converted data in a memory buffer at each End Of Conversion Interrupt. This works well, so I am confident that the ADC is correctly converting the data.

  • Second, I started the ADC with DMA request in circular mode, (for each End Of Conversion Interrupt, the DMA handler get the ADC converted data and store in a memory buffer). At this stage, when I check the memory buffer via the Debugger - It seams that the data are messed and it's like the DMA was skipping values).

  • Third, I just wanted to verify, if it's a DMA problem or it's a Debugger one. I started the ADC with DMA request in "normal" mode. And all of a sudden, when opening the Debugger, the memory buffer data are correctly stored.

To summarize, the main difference between the second and the third method when opening the Debugger, is that the DMA handler is still running (in circular mode) and as a result the Debugger can't be able to show correctly the memory buffer data, due to the speed of the ADC conversion time/ DMA Handler requests (~1 µs). And in the other hand (in normal mode), the DMA handlers stops once it has completed filling the buffer.

To conclude, the DMA handler works fine and you can output the square wave with the DAC using your buffer. And if you want to view correctly the data using the Debugger you need to stop the DMA (after a period of time; for example).

HAL_ADC_Stop_DMA(&hadc);
0
votes

Based on the tabular view of the data you provided, it appears that the DMA is configured properly as the data looks nearly identical to the data obtained from ADC EOC IRQ.

The only variable between relying on DMA and an IRQ is that there may be unexpected bus "collisions" between the DMA Controller and the CPU as, unlike when using an IRQ, they are both running concurrently, potentially resulting in wait states.

From the STM32 Reference Manual Section 13.4:

The DMA controller performs direct memory transfer by sharing the system bus with the Cortex-M4 ® F core. The DMA request may stop the CPU access to the system bus for some bus cycles, when the CPU and DMA are targeting the same destination (memory or peripheral).

And your observed sampling rate-dependent degradation in performance certainly corroborates this hypothesis, as the busmatrix must arbitrate more frequent accesses between the DMA Controller and the CPU.

Without seeing the rest of your code that sets up and reads from the buffer, it is hard to say what aspect of your application code may be causing this issue.