cancel
Showing results for 
Search instead for 
Did you mean: 

Problem DMA interrupt with ADC

ronandouguet
Associate II
Posted on July 13, 2012 at 16:08

Hello everybody,

I 'm using the STM32F4 discovery board. I use the ADC in triple mode with DMA. I configure the ADC in scan mode and regular channel. Here is my setup :

/**************************************************************************
* @descr Initialise ADC : Triple Mode avec DMA
* @param None
* @retval None
*************************************************************************/
void
ADCInit(
void
)
{
GPIO_InitTypeDef GPIO_AdcInit;
ADC_InitTypeDef ADC1_InitStructure;
ADC_InitTypeDef ADC2_InitStructure;
ADC_InitTypeDef ADC3_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
/* Deinitializes the ADC peripheral registers */
ADC_DeInit();
/* Enable the GPIOC Clock & ADC1 Periph Clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2 | RCC_APB2Periph_ADC3, ENABLE);
/* Configure the PA0 pin (ADC123_IN0)
* PA3 pin (ADC123_IN3)
* PA4 pin (ADC12_IN4)
* PA5 pin (ADC12_IN5)
* PA6 pin (ADC12_IN6) */
GPIO_AdcInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6;
GPIO_AdcInit.GPIO_Mode = GPIO_Mode_AN;
GPIO_AdcInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_AdcInit);
/* Configure the PB0 pin (ADC12_IN8)
* PB1 pin (ADC12_IN9) */
GPIO_AdcInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_AdcInit.GPIO_Mode = GPIO_Mode_AN;
GPIO_AdcInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_AdcInit);
/* Configure the PC0 pin (ADC123_IN10)
* PC2 pin (ADC123_IN12) */
GPIO_AdcInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2;
GPIO_AdcInit.GPIO_Mode = GPIO_Mode_AN;
GPIO_AdcInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_AdcInit);
/* ADC Common configuration -- ADC_CCR Register */
ADC_CommonInitStructure.ADC_Mode = ADC_TripleMode_RegSimult ;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1 ;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInit(&ADC_CommonInitStructure);
/* ADC1 regular channel 6 configuration -- ADC_CR1, ADC_CR2, ADC_SQR1 Register */
ADC1_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC1_InitStructure.ADC_ScanConvMode = ENABLE;
ADC1_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC1_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC1_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC1_InitStructure.ADC_NbrOfConversion = 3;
ADC_Init(ADC1, &ADC1_InitStructure);
/* ADC1 regular channel6 configuration -- ADCx->SMPR1,SMPR2 et ADCx->SQR1,SQR2,SQR3
* channel8 */
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 2, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 3, ADC_SampleTime_15Cycles );
/* ADC2 regular channel 12 configuration -- ADC_CR1, ADC_CR2, ADC_SQR1 Register */
ADC2_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC2_InitStructure.ADC_ScanConvMode = ENABLE;
ADC2_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC2_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC2_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC2_InitStructure.ADC_NbrOfConversion = 3;
ADC_Init(ADC2, &ADC2_InitStructure);
/* ADC2 regular channel12 configuration -- ADCx->SMPR1,SMPR2 et ADCx->SQR1,SQR2,SQR3 */
ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 1, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 2, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC2, ADC_Channel_5, 3, ADC_SampleTime_15Cycles );
/* ADC3 regular channel 0 configuration -- ADC_CR1, ADC_CR2, ADC_SQR1 Register */
ADC3_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC3_InitStructure.ADC_ScanConvMode = ENABLE;
ADC3_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC3_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC3_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC3_InitStructure.ADC_NbrOfConversion = 3;
ADC_Init(ADC3, &ADC3_InitStructure);
/* ADC3 regular channel12 configuration -- ADCx->SMPR1,SMPR2 et ADCx->SQR1,SQR2,SQR3 */
ADC_RegularChannelConfig(ADC3, ADC_Channel_0, 1, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 2, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC3, ADC_Channel_10, 3, ADC_SampleTime_15Cycles );
/* DMA2 Stream0 channel0 configuration */
DMA_ADC_Config();
/* Enable DMA request after last transfer (Multi-ADC mode) */
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
/* Enable ADC1 -- ADC_CR2_ADON */
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC2 */
ADC_Cmd(ADC2, ENABLE);
/* Enable ADC3 */
ADC_Cmd(ADC3, ENABLE);
/* Enable end of conversion interrupt */
ADC_ITConfig(ADC1,ADC_IT_EOC, ENABLE);
/* Enable DMA2 Stream0 Transfer complete interrupt */
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
/* Enable ADC1 DMA since ADC1 is the Master*/
ADC_DMACmd(ADC1, ENABLE);
}
/**************************************************************************
* @descr Configuration du DMA pour l'ADC
* @param None
* @retval None
*************************************************************************/
void
DMA_ADC_Config(
void
)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_0; 
// DMA channel 0
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCValue; 
// Adresse de depart de stockage
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CDR_ADDRESS; 
// Adresse du registre du periph
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; 
// Direction : Periph vers memoire
DMA_InitStructure.DMA_BufferSize = 9; 
// Taille du buffer : 9 (x 4 octets)
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 
// Pas d'incrementation de l'adresse du periph
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 
// Incremente l'adresse de la memoire
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; 
// Taille de la donnee du periph (32 bits)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; 
// Taille de la donnee memoire (32 bits)
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 
// Mode circulaire (remise à 0 de l'adresse mémoire après 9 increment)
DMA_InitStructure.DMA_Priority = DMA_Priority_High; 
// Pririte haute
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; 
// Mode fifo valide
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull; 
// Taille de la fifo : HalfFull (2 words, 8 octets)
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; 
// Transfert en mémoire en un seul burst
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; 
// Transfert du peripherique en 1 seul burst
/* Configure le DMA2_stream0 */
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
/* DMA2_Stream0 enable */
DMA_Cmd(DMA2_Stream0, ENABLE);
}

To start a sequence of conversion I use ADC_SoftwareStartConv(ADC1). This function is called in the timer interrupt (every 10 ms).

void
TIM2_IRQHandler(
void
)
{
if
(TIM_GetITStatus(TIM2, TIM_IT_Update ) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update ); 
// Reset Flag
GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
ADC_SoftwareStartConv(ADC1);
}
}

Then I check that there is an DMA interrupt every 10 ms

void
DMA2_IRQHANDLER(
void
)
{
if
(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) != RESET)
{
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); 
// Reset du Flag
ADC1->SR = ADC1->SR & 0xFFED;
/* Change l'etat de la LED4 */
GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
}
}

The problem is that the operation is random. Sometimes, there are many DMA interrupts every 10 ms but mostly there all the time of DMA interrupt (As if the ADC is in continuous conversion mode). I don't know where does the problem. Is that it is a bad configuration of the ADC ? Thank you for your response. Ronan.
16 REPLIES 16
lordgty
Associate II
Posted on March 21, 2013 at 19:37

You mean i should setup up a timer which produces interrupt every (1/6400) of a second (sampling interval). I should then perform three samples in the routine, put them in arrays and then continue with the main program? That sure is one way to do this and the most straight forward one as well.

However, what i am try to do is interrupt free method. ADC conversion will be automatically triggered by timer (at sampling interval). Now adc sampled value should be transfered into array using DMA without CPU intervention, as i have to run complex main routines. What is your opinion about that.

Thanks,

Muhammad Umar

Posted on March 21, 2013 at 21:57

The timer just signals/triggers the ADC to do the conversion, it doesn't need to generate a physical interrupt. You'd typically want to handle the DMA TC, and perhaps HT, interrupts when it has a large block of samples. You could also poll the DMA TC/HT events in your main loop, if that suits your needs better.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
lordgty
Associate II
Posted on March 22, 2013 at 09:13

''The timer just signals/triggers the ADC to do the conversion, it doesn't need to generate a physical interrupt'', thats exactly what i am doing. Now after adc has converted three channels, i want dma to transfer data in arrays. Kindly elaborate what you mean by''handle the DMA TC, and perhaps HT, interrupts when it has a large block of samples''.

Thanks,

Muhammad umar

Posted on March 22, 2013 at 14:57

My point is you should pace the ADC via the timer, you set the DMA to do 3 x 128 samples, and THEN interrupt. Unless there is a pressing reason to process each of the 3 samples as they arrive. If you're just stuffing them into a larger array, just make a large array and point the DMA controller at it, and define the transfer size as 3 x 128 samples (or whatever), not 3

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
lordgty
Associate II
Posted on March 22, 2013 at 16:52

Thanks,

I am using TMR2 output trigger to trigger the ADC sampling. Only thing remaining is how to configure DMA to do 3X128 samples transfer. By looking at DMA examples, one finds out that DMA can transfer values of three channels (buffer size=3)  into variables. For this memory increment must be enabled and three variables must be an array. Now is there any way for DMA to populate 3 128 byte arrays. There is only one stream and one channel of DMA2 that can serve ADC3.

There is no immediate need to process three channels as they sample. I am trying ot dump them into large arrays because i need to process them and then use them again. These channels are actually three phase currents. Kindly see if you can figure out how to configure DMA to do 3 X 128 transfer. Thanks for your time,

Muhammad Umar

Posted on March 22, 2013 at 17:08

You cannot generate multiple parallel streams with a single DMA stream.

uint16_t AdcBuffer[128 * 3]; // or AdcBuffer[128][3];
..
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)AdcBuffer; // &AdcBuffer[0] &AdcBuffer[0][0]
..
DMA_InitStructure.DMA_BufferSize = 128 * 3;
..

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
lordgty
Associate II
Posted on April 10, 2013 at 06:11

Thanks a lot. I solved this problem by using the technique you mentioned above. Thnks for help.