cancel
Showing results for 
Search instead for 
Did you mean: 

My ADC DMA table switch values, and countains randomized values

jean_prieur
Associate III
Posted on June 01, 2015 at 19:36

Hello everybody,

I have a strange problem, hard to debug, I want to know if somebody already had this kind of ADC madness.

I use 6 ADC inputs in my code, using DMA and NVIC.

Sometimes, after 2 to 5 minutes of program running, my ADC inputs begin to convert error values. For example,TabADC[0] contains the original value ofTabADC[2],TabADC[1] countains the original value ofTabADC[3],TabADC[2] andTabADC[3] converts random values (that changes at each conversion).

It seems that in the DMA table, some values are switched and some values are randomized.

This is my code, I don't know if it can help:

/* Includes */

#include ''stm32f4xx_rcc.h''

#include ''stm32f4xx_gpio.h''

#include ''stm32f4xx_adc.h''

#include ''stm32f4xx_dma.h''

#define ADC_CCR_ADDRESS ((uint32_t)0x40012308)

static

uint16_t TabADC[6] = {0, 0, 0, 0, 0, 0};

// DMA table

void

Refresh_ADC_Values(

void

)

{

ADC_SoftwareStartConv(ADC1);

ADC_SoftwareStartConv(ADC2);

while

(ADC_GetSoftwareStartConvStatus(ADC1) != RESET){}

while

(ADC_GetSoftwareStartConvStatus(ADC2) != RESET){}

}

uint16_t Get_ADC_digital_value(uint8_t ADC_VOICE)

{

uint16_t ADC_digital_value = 0;

switch

(ADC_VOICE)

{

case

INPUT_1_HOT: ADC_digital_value = TabADC[3];

break

;

case

INPUT_1_COLD: ADC_digital_value = TabADC[1];

break

;

case

INPUT_2_HOT: ADC_digital_value = TabADC[2];

break

;

case

INPUT_2_COLD: ADC_digital_value = TabADC[0];

break

;

case

ASK_Z_AXIS: ADC_digital_value = TabADC[4];

break

;

case

ASK_Y_AXIS: ADC_digital_value = TabADC[5];

break

;

}

return

ADC_digital_value;

}

void

ADC_Config(

void

)

{

// ADC initialisation

ADC_CommonInitTypeDef ADC_CommonInitStructure;

// CLOCK init for DMA2 + GPIOA + GPIOC + ADC1 + ADC2

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);

// DMA2 config (direct access memory) Stream0 channel0

DMA_InitTypeDef DMA_InitStructure;

DMA_InitStructure.DMA_Channel = DMA_Channel_0;

// USE CHANNEL 0

DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&TabADC;

// Save in TabADC(4)

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CCR_ADDRESS;

// Use the adress 0x40012308

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

DMA_InitStructure.DMA_BufferSize = 6;

// BUFFER SIZE (same size than TabADC)

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_High;

DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;

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);

/* Enable DMA Stream Half / Transfer Complete interrupt */

DMA_ITConfig(DMA2_Stream0, DMA_IT_HT, ENABLE);

// DMA2_Stream0 enable

DMA_Cmd(DMA2_Stream0, ENABLE);

// GPIO config pins C4 C4 A2 A3 > ANALOG INPUTS

GPIO_InitTypeDef GPIO_InitStructure;

// GPIOC

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;

GPIO_Init(GPIOC, &GPIO_InitStructure);

// GPIOA

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;

GPIO_Init(GPIOA, &GPIO_InitStructure);

// GPIOB

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;

GPIO_Init(GPIOB, &GPIO_InitStructure);

// Init l'ADC ''Common''

ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;

ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;

ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1;

ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles ;

ADC_CommonInit(&ADC_CommonInitStructure);

// Init l'ADC ''1'' channels 15, 3

ADC_InitTypeDef ADC_InitStructure;

ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;

// Faible résolution pour des valeurs entre 0 et 256

ADC_InitStructure.ADC_ScanConvMode = ENABLE;

ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;

ADC_InitStructure.ADC_ExternalTrigConv = 0;

ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

ADC_InitStructure.ADC_NbrOfConversion = 3;

// 3 conversions car 3 channels à convertir

ADC_Init(ADC1, &ADC_InitStructure);

ADC_Init(ADC2, &ADC_InitStructure);

// ADC1 regular channels 15, 14 configuration

ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 1, ADC_SampleTime_480Cycles);

ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 2, ADC_SampleTime_480Cycles);

ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 3, ADC_SampleTime_480Cycles);

// PB0 Z AXIS

// ADC2 regular channels 1, 2 configuration

ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 1, ADC_SampleTime_480Cycles);

ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 2, ADC_SampleTime_480Cycles);

ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 3, ADC_SampleTime_480Cycles);

// PB1 Y AXIS

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

ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);

// ENABLE ADC1 & ADC2

ADC_Cmd(ADC1, ENABLE);

ADC_Cmd(ADC2, ENABLE);

NVIC_Configuration();

}

void

NVIC_Configuration(

void

)

{

NVIC_InitTypeDef NVIC_InitStructure;

// Enable the DMA Stream IRQ Channel

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

}

// After doing a Refresh_ADC_Values :

// ADC VALUES ARE UPDATED !

// Because it take time to convert inputs

void

DMA2_Stream0_IRQHandler(

void

)

{

// Test on DMA Stream Half Transfer interrupt

if

(DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0))

{

// Clear DMA Stream Half Transfer interrupt pending bit

DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0);

// Here do something with the TabADC !!!!

}

}

Thanks for your help!

Best,

Jean

4 REPLIES 4
Posted on June 01, 2015 at 20:28

Wouldn't you need this to be in continuous mode for this to work at all beyond the first set of samples?

Array should be ''volatile''

Try disabling the FIFO mode.

A system under debug may also show odd behaviour.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jean_prieur
Associate III
Posted on June 02, 2015 at 14:50

Thanks Clive! I changed to Continuous mode Enable and now my values are not corrupted anymore.

I don't understand what is the continuous mode, because is still need to use

Refresh_ADC_Values(

)

before

Get_ADC_digital_value()

to update and get my voltages. About that, does the

while

(ADC_GetSoftwareStartConvStatus(ADC1) != RESET){}

takes a long time, with ADC_SampleTime_480Cycles? I mean does the uC wait 480 cycles, so it's a real-time breaker?
Posted on June 03, 2015 at 17:49

This is not how I'd approach the problem. I've posted examples of dual+dma.

I don't see the point of the HT interrupt in this case, or firing both ADC, they are tied together.

If you need periodicity in the samples, use a timer to trigger the start of conversion, and TC to catch the completion. If you're using HT+TC then make the buffer twice as big to manage the two halves.

With Scan+Continuous, and volatile array, it's going to be continuously updating the array with current measurements.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jean_prieur
Associate III
Posted on June 05, 2015 at 12:42

In my case I wanted to not use CONTINUS mode conversion, but only use ADC_SoftwareStartConv and an interrupt to know when the conversion is finished (interrupt not implemented for the moment in my code above) in order to save uC power (I imagine that the conversion takes time and I want to optimize my real-time code). Am I right?

Anyway you're right, if I use CONTINUS mode it's not necessary to use while(ADC_GetSoftwareStartConvStatus(ADC1) != RESET){} after each ADC_SoftwareStartConv 

 

And I can only launch ADC_SoftwareStartConv(ADC1); without launching ADC_SoftwareStartConv(ADC2); because they are tied together.

Thanks!