2012-07-13 07:08 AM
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.
2013-03-21 11:37 AM
2013-03-21 01:57 PM
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.
2013-03-22 01:13 AM
2013-03-22 06:57 AM
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
2013-03-22 08:52 AM
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 Umar2013-03-22 09:08 AM
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;
..
2013-04-09 09:11 PM