cancel
Showing results for 
Search instead for 
Did you mean: 

Problems with ADC Initialization

victor239955
Associate II
Posted on March 27, 2009 at 02:19

Problems with ADC Initialization

6 REPLIES 6
victor239955
Associate II
Posted on May 17, 2011 at 13:07

Hi Everyone,

I am using the STM32F103 in the 64-pin package. I have

3 external inputs to the ADC:

1) 24v through a voltage divider --> normal should read around 2.18v

2) 3v through a voltage divider --> normal should read around 2.06v

3) 4v through a voltage divider --> normal should read around 2v

They are all adjusted via voltage divider to fit into the ADC range.

Since it is a 64-pin part, Vref+ is Vdda which is 3.25v (measured).

I am also sampling the internal temperature sensor and internal

reference voltage.

The ADC results are transferred continuously via DMA.

The primary clock is the 64Mhz internal clock.

What I am seeing is that if I reset the STM32 via software, it sometimes

comes up and the ADC is in a state where one or more of the voltages/temperature

are read incorrectly. I have verified that the ADC input at the STM32

pin is correct.

When in this state, the only thing I can see is that the ADC results

are just wrong.

I have attempted to re-initialize the ADC whenever this occurs but it

never gets out of that state until reset (sometimes).

An example of the values I am seeing are:

Voltage to sample: 23.8v (measured)

After voltage divider: 2.18 (measured at MCU input pin)

ADC result: 1517 counts

Vdda: 3.25v (measured)

I believe that each LSB is Vref/4096 == Vdda/4096 = .00081v

So, 1517 counts * .00018 v/count = 1.22877v

Scaled back up through the voltage divider that is 13.52v. Yikes.

Here is my ADC Initialization routine which is called during hardware init:

===========================================================================

void adc_controller_init(void)

{

ADC_InitTypeDef ADC_InitStructure;

uint32_t exit_counter;

ADC_DeInit(ADC1);

ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;

ADC_InitStructure.ADC_ScanConvMode = ENABLE;

ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

ADC_InitStructure.ADC_NbrOfChannel = 5;

ADC_Init(ADC1, &ADC_InitStructure);

// ADC1 configuration (temp)

ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_239Cycles5);

// ADC1 configuration (vref)

ADC_RegularChannelConfig(ADC1, ADC_Channel_17, 2, ADC_SampleTime_239Cycles5);

// ADC1 configuration (24v)

ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 3, ADC_SampleTime_239Cycles5);

// ADC1 configuration (2.5v)

ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 4, ADC_SampleTime_239Cycles5);

// ADC1 configuration (3.5v)

ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 5, ADC_SampleTime_239Cycles5);

// Enable ADC1 DMA

ADC_DMACmd(ADC1, ENABLE);

// Enable ADC1

ADC_Cmd(ADC1, ENABLE); // wake up

delay(1); // wait 10msec

ADC_Cmd(ADC1, ENABLE); // 2nd enable required

// Enable temperature sensor

ADC_TempSensorVrefintCmd(ENABLE);

// Enable ADC1 reset calibaration register

ADC_ResetCalibration(ADC1);

// Check the end of ADC1 reset calibration register

exit_counter = DEFAULT_HW_WAIT_COUNT;

while(ADC_GetResetCalibrationStatus(ADC1)) {

if (--exit_counter == 0) {

SET_HW_WAIT_FAIL(WAIT_FAIL_RESET_CAL_STATUS);

DEBUG_TRACE(''TMO waiting for ADC reset cal status\n'');

return;

}

}

// Start ADC1 calibaration

ADC_StartCalibration(ADC1);

// Check the end of ADC1 calibration

exit_counter = DEFAULT_HW_WAIT_COUNT;

while(ADC_GetCalibrationStatus(ADC1)) {

if (--exit_counter == 0) {

SET_HW_WAIT_FAIL(WAIT_FAIL_RESET_CAL_STATUS);

DEBUG_TRACE(''TMO waiting for ADC get cal status\n'');

return;

}

}

// Start ADC1 Software Conversion

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

return;

}

=====================================================================

My DMA initialization routine looks like this:

======================================================================

void adc_dma_init(void)

{

DMA_InitTypeDef DMA_InitStructure;

DMA_DeInit(DMA_Channel1);

DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(ADC1->DR);

DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adc_data;

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

DMA_InitStructure.DMA_BufferSize = 5;

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_M2M = DMA_M2M_Disable;

DMA_Init(DMA_Channel1, &DMA_InitStructure);

/* Enable DMA channel1 */

DMA_Cmd(DMA_Channel1, ENABLE);

return;

}

=========================================================================

If I cycle the power on the target and let it boot, the problem

does not seem to be there but I am not 100% sure of that.

Sorry for the huge amount of detail but I figure too much is better

than not enough 🙂

Any ideas?

Victor

jj
Associate II
Posted on May 17, 2011 at 13:07

Hi-

Suggest the following:

a) disable the 24V signal so that it cannot ''beat'' your Vdd upon power up and upon reset.

b) is it possible that your 24V voltage divider relies on the STM32 or other logic to, ''pull to gound?'' Similar to (a) above

c) Should neither (a nor b) be the cause - temporarily cease the DMA

and simply monitor the ADC by a more basic method

Interesting - please keep us posted...

[ This message was edited by: jj.sprague on 24-03-2009 23:55 ]

victor239955
Associate II
Posted on May 17, 2011 at 13:07

Thanks for the ideas. My guess is that the state

of the analog input can be anything when the ADC

is initialized so it should not matter if

the 24v beats Vdda.

The voltage divider is just a simple resistor divider on

the input.

An interesting thing I tried was to short the analog input

to ground to see if the sampled value went to zero. It

didn't BUT......the next channel did. I tried the same thing

on the next input and it shifted down as well.

Could it be that the DMA engine is getting funky with placing

the results into the circular buffer in memory? The DMA

configuration registers look the same between a working and

non-working test run so maybe it's a DMA init problem.

I think that your third test would have led to the same

conclusion.

Victor

Victor

andreas2
Associate II
Posted on May 17, 2011 at 13:07

Why the ''ADC_Cmd(ADC1, ENABLE); // 2nd enable required''? That will start a conversion. And you start the calibration just after, sounds like a possible race-condition. If the conversion finishes before you interfere with the calibration, the DMA might transfer that first sample and gets off-by-one when you later start the real sequence.

In any case, I'm pretty sure you don't need the 2nd enable. I've never seen or used one.

victor239955
Associate II
Posted on May 17, 2011 at 13:07

Hi Andreas,

Thanks for the response. I read in the reference manual (pg 373)

the following comments on the ADON bit:

-------------------------------------------------------------------

This bit is set and cleared by software. If this bit holds a value of zero and a 1 is written to it then it

 

wakes up the ADC from Power Down state.

 

Conversion starts when this bit holds a value of 1 and a 1 is written to it. The application should

 

allow a delay of tSTAB between power up and start of conversion. Refer to Figure 142.

 

0: Disable ADC conversion/calibration and go to power down mode.

 

1: Enable ADC and to start conversion

 

Note: If any other bit in this register apart from ADON is changed at the same time, then conversionThis bit is set and cleared by software. If this bit holds a value of zero and a 1 is written to it then it wakes up the ADC from Power Down state. Conversion starts when this bit holds a value of 1 and a 1 is written to it. The application should allow a delay of tSTAB between power up and start of conversion. Refer to Figure 142. 0: Disable ADC conversion/calibration and go to power down mode. 1: Enable ADC and to start conversion Note: If any other bit in this register apart from ADON is changed at the same time, then conversion

------------------------------------------------------------------------

I was confused as to whether I needed this or not. It did not

make a difference.

Now, I did finally get it working. I narrowed down the issue

to the DMA engine. It turns out that in some cases the DMA

write pointer was incorrect and the data was being written to

my memory buffer at the start address + 16 Bits. Very strange.

I tried using the one-shot method of conversion for all 5

inputs together and that did not help either. I finally

ended up, configuring for no DMA and 1 conversion channel. Periodically

I configure each channel one at a time, covert, and finally read

from the DR register. This works fine but it is an abomination.

I also discovered that if you init the ADC without actually configuring

any inputs, you cannot reset the calibration data. Very strange.

Victor

yah996
Associate II
Posted on May 17, 2011 at 13:07

Hi Victor,

did you try already the simple way to configure your DMA?

RCC->AHBENR |= (1<

DMA1_Channel1->CMAR = (unsigned long) adc.analog;

DMA1_Channel1->CPAR = (unsigned long)&(ADC1->DR);

DMA1_Channel1->CNDTR = 2; //DMA_BufferSize

DMA1_Channel1->CCR = 0x000025A0;

DMA1_Channel1->CCR |= (1 << 0); // enable DMA Channe1

I had similar problems, no it works fine.

Greets

K.