cancel
Showing results for 
Search instead for 
Did you mean: 

How to use the stm32f4xx ADC in dual regular simultaneous mode

zdravko
Associate II
Posted on May 31, 2016 at 16:38

The task is to measure three phase power. ADC1 measures voltage, ADC2 measures current. Here is the adc configuration routine:

static void ADC_Config(void)

{

  ADC_InitTypeDef       ADC_InitStructure;

  ADC_CommonInitTypeDef ADC_CommonInitStructure;

  DMA_InitTypeDef       DMA_InitStructure;

  GPIO_InitTypeDef      GPIO_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

EXTI_InitTypeDef EXTI_InitStructure;

 

  /* Enable ADCx, DMA and GPIO clocks ****************************************/ 

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

  RCC_AHB1PeriphClockCmd(ADCx1_CHANNEL_GPIO_CLK, ENABLE); 

RCC_AHB1PeriphClockCmd(ADCx2_CHANNEL_GPIO_CLK, ENABLE);  

  RCC_APB2PeriphClockCmd(ADCx1_CLK, ENABLE);

RCC_APB2PeriphClockCmd(ADCx2_CLK, ENABLE);

  

/* Enable the DMA2 Stream0 Global Interrupt (to handle the Transfer Complete Interrupt TCIF) */

NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;

  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStructure);

EXTI_InitStructure.EXTI_Line = EXTI_Line0;

  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;

  EXTI_InitStructure.EXTI_LineCmd = ENABLE;

  EXTI_Init(&EXTI_InitStructure); 

/* Modify the WAKEUP_BUTTON_EXTI_IRQn Interrupt Preemption Priority */

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = SW_VRMS_IRQ_PRIOR;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

  /* DMA2 Stream0 channel15 configuration **************************************/

  DMA_InitStructure.DMA_Channel = DMA_CHANNELx1;  

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_DR_ADDRESS;

  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&uhADCxConvertedValue;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

  DMA_InitStructure.DMA_BufferSize = DMA_BUFFER_SIZE;

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

  DMA_InitStructure.DMA_Priority = DMA_Priority_High;

  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(DMA_STREAMx1, &DMA_InitStructure);

  DMA_Cmd(DMA_STREAMx1, ENABLE);

  /* Configure ADC1 Channel15 pin as analog input ******************************/

  GPIO_InitStructure.GPIO_Pin = ADCx1_GPIO_PIN_1_2;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

  GPIO_Init(ADCx1_GPIO_PORT_1_2, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = ADCx1_GPIO_PIN_3;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

  GPIO_Init(ADCx1_GPIO_PORT_3, &GPIO_InitStructure);

/* Configure ADC2 Channel15 pin as analog input ******************************/

 

GPIO_InitStructure.GPIO_Pin = ADCx2_GPIO_PIN;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

  GPIO_Init(ADCx2_GPIO_PORT, &GPIO_InitStructure);

  /* ADC Common Init **********************************************************/

  ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;

// ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;

  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;

  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_2;

  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_12Cycles;

  ADC_CommonInit(&ADC_CommonInitStructure);

  /* ADC1 Init ****************************************************************/

  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;

  ADC_InitStructure.ADC_ScanConvMode = ENABLE;

  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;

  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;

  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

  ADC_InitStructure.ADC_NbrOfConversion = N_CONV;

  ADC_Init(ADCx1, &ADC_InitStructure);

// ADC_TempSensorVrefintCmd(ENABLE);

/* ADC1 regular channel15 configuration **************************************/

  ADC_RegularChannelConfig(ADCx1, ADC1_CHANNEL_1, 1, ADC_SampleTime_480Cycles);

ADC_RegularChannelConfig(ADCx1, ADC1_CHANNEL_2, 2, ADC_SampleTime_480Cycles);

  ADC_RegularChannelConfig(ADCx1, ADC1_CHANNEL_3, 3, ADC_SampleTime_480Cycles);

/* ADC2 Init ****************************************************************/

ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;

  ADC_InitStructure.ADC_ScanConvMode = ENABLE;

  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;

  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;

  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

  ADC_InitStructure.ADC_NbrOfConversion = N_CONV;

  ADC_Init(ADCx2, &ADC_InitStructure);

/* ADC1 regular channel15 configuration **************************************/

  ADC_RegularChannelConfig(ADCx2, ADC2_CHANNEL_1, 1, ADC_SampleTime_480Cycles);

ADC_RegularChannelConfig(ADCx2, ADC2_CHANNEL_2, 2, ADC_SampleTime_480Cycles);

ADC_RegularChannelConfig(ADCx2, ADC2_CHANNEL_3, 3, ADC_SampleTime_480Cycles);

// ADC_TempSensorVrefintCmd(ENABLE);

//  /* Enable DMA request after last transfer (Single-ADC mode) */

//   ADC_DMARequestAfterLastTransferCmd(ADCx1, ENABLE);

/* Enable DMA request after last transfer (Multi-ADC mode) */

  ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);

//   /* Enable ADC1 DMA */

//   ADC_DMACmd(ADCx1, ENABLE);

  /* Enable ADC1 */

  ADC_Cmd(ADCx1, ENABLE);

/* Enable ADC2 */

  ADC_Cmd(ADCx2, ENABLE);

DMA_ITConfig(DMA_STREAMx1,DMA_IT_TC,ENABLE);

}

When I N_CONV=1 and I connect the first phase only, then it's working nice. Then if I connect the second or third phase with N_CONV=1, but ADC2_CHANNEL_1 changed to _2 or _3 on first place and all other commented, then it's working fine. The N_CONV=3 version is however not working i.e. the measured values are all wrong. Are the configuration done correctly?
4 REPLIES 4
Posted on May 31, 2016 at 19:11

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; // NO

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; // NO

It is not triggering, so nothing will have alignment to anything, just ADC1 to ADC2 for the specific sample

The peripheral address needs to be the common data register, no define visible.

The peripheral address does NOT increment

The memory address should increment

I'd use Word wide peripheral and memory access. Ie pulls 2x 16-bit half words from common, stuffs 2x 16-half words into array, size/definition not visible.

I've posted dual mode examples to forum, review.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
zdravko
Associate II
Posted on June 01, 2016 at 09:18

Thank you for the answer.

About the first point ''It is not triggering, so nothing will have alignment to anything, just ADC1 to ADC2 for the specific sample'' I think exactly this is the main problem. Could you clarify or point out some reading articles that are explaining your point.

Thanks. 

zdravko
Associate II
Posted on June 01, 2016 at 10:06

I have done some more tests and when DMA_BUFFER_SIZE=1, then nothing is alligned, when DMA_BUFFER_SIZE=3 it's alligned, but I want to process each dual sample in real time. 

I realized that you probably meant that the external trigger is disabled, but that's ok because I don't have any external trigger. 

So the big question is why the results are not ordered. Here is the IRQ:

void DMA2_Stream0_IRQHandler(void) {

static uint8_t first_zero[Ph_N]={0};

static int32_t unscaled_voltage[Ph_N], unscaled_voltage_old[Ph_N], unscaled_current[Ph_N],unscaled_current_old[Ph_N];

static int32_t unscaled_centered_v[Ph_N], unscaled_centered_i[Ph_N];

static uint8_t current_phase=0;

if(DMA_GetITStatus(DMA_STREAMx1,DMA_IT_TCIF0)!=RESET) {

DMA_ClearITPendingBit(DMA_STREAMx1,DMA_IT_TCIF0|DMA_IT_HTIF0);

unscaled_current_old[current_phase]=unscaled_current[current_phase];

unscaled_voltage_old[current_phase]=unscaled_voltage[current_phase];

unscaled_voltage[current_phase] = (uhADCxConvertedValue[0]&ADC_CDR_DATA1)<<W;

unscaled_current[current_phase] = (uhADCxConvertedValue[0]&ADC_CDR_DATA2);

unscaled_centered_v[current_phase]=(V0[current_phase]>>W)-(unscaled_voltage[current_phase]>>W);

unscaled_centered_i[current_phase]=(I0[current_phase]>>W)-(unscaled_current[current_phase]>>W);

if(first_zero[current_phase]==1) {

S_v[current_phase]=S_v[current_phase]+(unscaled_centered_v[current_phase]*unscaled_centered_v[current_phase]);

S_i[current_phase]=S_i[current_phase]+(unscaled_centered_i[current_phase]*unscaled_centered_i[current_phase]);

S_p[current_phase]=S_p[current_phase]+(unscaled_centered_v[current_phase]*unscaled_centered_i[current_phase]);

Nsamp[current_phase]++;

}

I0[current_phase]=I0[current_phase]-unscaled_centered_i[current_phase];

V0[current_phase]=V0[current_phase]-unscaled_centered_v[current_phase];

if((unscaled_current[0]<=I0[0] && unscaled_current_old[0]>I0[0]) || 

(unscaled_current[0]>=I0[0] && unscaled_current_old[0]<I0[0])) {

if((unscaled_current[0]>=I0[0] && unscaled_voltage[0]<V0[0]) ||

(unscaled_current[0]<=I0[0] && unscaled_voltage[0]>V0[0]))  {

Q_sign=-1;

} else {

Q_sign=1;

}

}

if((unscaled_voltage[0]<=V0[0] && unscaled_voltage_old[0]>V0[0]) || 

(unscaled_voltage[0]>=V0[0] && unscaled_voltage_old[0]<V0[0])) {

if(first_zero[current_phase]) {

//trigger the sw interrupt

EXTI_GenerateSWInterrupt(EXTI_Line0);

} else {

first_zero[current_phase]=1;

S_v[current_phase]=0;

S_i[current_phase]=0;

S_p[current_phase]=0;

Nsamp[current_phase]=0;

steady_Nsamp[current_phase]=Nsamp[current_phase]*P;

}

}

irq_phase=current_phase;

current_phase=(current_phase+1)%Ph_N;

}

}

Walid FTITI_O
Senior II
Posted on June 01, 2016 at 12:25

Hi georgiev.zdravko,

I recommend you to take a look to '' ADC_DualModeInterleaved'' in

http://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32cube-embedded-software/stm32cubef4.html

. I think it is the most suitable for your need.

Example at thi path: STM32Cube_FW_F4_V1.12.0\Projects\STM324xG_EVAL\Examples\ADC\ADC_DualModeInterleaved

-Hannibal-