2014-02-13 03:05 PM
Hello all
I am trying to configure stm32f407 MCU to work in ADC regular-simultaneous dual mode, but with no luck so far. I hope that some of you could point where the bug is. Generally, the idea is to gather two streams of samples and store them in uint32_t array of size ADC1_CONV_CNT. Samples of two streams should be moved to array by DMA reading from ADC1's DR register. When DMA fills whole array it should rise an interrupt which has to count average for both streams and store them in avg1, avg2 variables. Then array filling process should start from the beginning and so on. Here is the code I use:/* system configuration */void adc_init_dual_regsimultaneous(void)
{
/* init RCC */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
/* init GPIO */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* init NVIC */
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
/* init DMA */
DMA_InitTypeDef DMA_InitStruct;
DMA_DeInit(DMA2_Stream0);
DMA_InitStruct.DMA_Channel = DMA_Channel_0;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)ADC1_vals;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory; //PeripheralSRC;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_InitStruct.DMA_BufferSize = ADC1_CONV_CNT;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
//DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA2_Stream0, &DMA_InitStruct);
DMA2_Stream0->CR |= DMA_SxCR_TCIE;
/* init ADC common */
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_2;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
/* init ADC1 */
ADC_InitTypeDef ADC_InitStructure;
ADC_StructInit(&ADC_InitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 1, ADC_SampleTime_480Cycles);
//commented ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
/* init ADC2 */
ADC_StructInit(&ADC_InitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC2, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC2, ADC_Channel_13, 1, ADC_SampleTime_480Cycles);
/* enabling */
DMA_Cmd(DMA2_Stream0, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_SoftwareStartConv(ADC1);
}
/* interrupt code */
void DMA2_Stream0_IRQHandler(void)
{
avg1 = test_avg1(1);
avg2 = test_avg1(0);
DMA2->LIFCR = DMA_LISR_TCIF0;
}
/* function which counts average */
uint16_t test_avg1(int inc)
{
uint32_t sum = 0;
uint16_t* array = (uint16_t*)ADC1_vals;
int i;
for (i = 0; i < ADC1_CONV_CNT; i++)
sum += array[2*i + inc];
return sum / ADC1_CONV_CNT;
}
What I can see when running above configuration is that interrupt is risen only once and avg1 is updated with correct average, but avg2 is updated by value 0. It's looks like that all half-words are equal to 0 for this stream. No more interrupts is risen. When I comment out this line in configuration:ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE); and uncomment following line:ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); then interrupt in risen in periodic manner and it updates avg1 properly every time it is executed, but avg2 is still 0. I will be grateful for any help and suggestion.
2014-02-13 03:35 PM
It wouldn't be the ADC1->DR that needs reading, but the common data register for ADC1/ADC2
Clearing the DMA interrupt before leaving is not advisable, you should do this on entry after qualifying the source.Pretty sure I've previously posted Dual examples.2014-02-15 07:48 AM
Thank you for help!
Regarding DMA suggestion - you mean that I should clear the flag at the beginning of IRQ handler, not at the end, right?2014-02-15 08:18 AM
Yes, and qualify the source, which will make it significantly more robust.