cancel
Showing results for 
Search instead for 
Did you mean: 

ADC behavior abnormal after wake up from STOP mode

dz huang
Associate III
Posted on April 23, 2018 at 18:00

Hello,

   I'm using STM32L071RBT6 MCU, encounter a strange issue. when MCU power on, using ADC channel 0 to monitor external battery voltage, the result is OK. but once system waked up from STOP mode, ADC converter behavior is abnormal, the sampling value random varied from 0 ~ 2000. following is my STOP method, can give me some hint?

0690X0000060AjyQAE.png0690X0000060AjtQAE.png

/* ADC init function */

static void MX_ADC_Init(void)

{

ADC_ChannelConfTypeDef sConfig;

HAL_ADCEx_EnableVREFINT();

/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)

*/

hadc.Instance = ADC1;

hadc.Init.OversamplingMode = DISABLE;

hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV16;

hadc.Init.Resolution = ADC_RESOLUTION_12B;

hadc.Init.SamplingTime = ADC_SAMPLETIME_79CYCLES_5;

hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;

hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;

hadc.Init.ContinuousConvMode = ENABLE;

hadc.Init.DiscontinuousConvMode = DISABLE;

hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;

hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;

hadc.Init.DMAContinuousRequests = ENABLE;

hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;

hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;

hadc.Init.LowPowerAutoWait = DISABLE;

hadc.Init.LowPowerFrequencyMode = DISABLE;

hadc.Init.LowPowerAutoPowerOff = DISABLE;

if (HAL_ADC_Init(&hadc) != HAL_OK)

{

_Error_Handler(__FILE__, __LINE__);

}

/**Configure for the selected ADC regular channel to be converted.

*/

sConfig.Channel = ADC_CHANNEL_0;

sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;

if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)

{

_Error_Handler(__FILE__, __LINE__);

}

/**Configure for the selected ADC regular channel to be converted.

*/

sConfig.Channel = ADC_CHANNEL_1;

if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)

{

_Error_Handler(__FILE__, __LINE__);

}

}

/***********************************************************************

* trigger MCU enter stop mode

*/

void powerSaving(void){

        //config GPIO to analog mode (zero consumption for every analog GPIO)

        lightLed(LED_GREEN, false);

       

       

        //HAL_ADC_DeInit(&hadc);

        //clear wakeup flag

        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

        //call power function to enter ''STOP'' mode

        HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

        //after stop, program continue run from here

        //config RCC

        SystemClock_Config();

        //sysclkConfig_after_stop();

        //adc init

        HAL_ADC_DeInit(&hadc);

        MX_ADC_Init();

        HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED);

        //config GPIO

}

// following is ADC sampling methods:

static volatile uint16_t adcValue[5][2];

/***************************************************************************************

* Get battery voltage

*/

static void handleBattVoltageFetch(void){

if (ADC_IS_CONVERSION_ONGOING_REGULAR(&hadc) == SET) return;

//enable MOSFET

HAL_GPIO_WritePin(BATT_ADC_EN_GPIO_Port, BATT_ADC_EN_Pin, GPIO_PIN_SET);

osDelay(100);

//start  timer

osTimerStart(myMotoTimerHandle, 500);

//set batt vol ADC enable flag

setAdcControlFlag(BATT_VOL_ADC_EN);

//start ADC

HAL_ADC_Start_DMA(&hadc, (uint32_t*)adcValue, sizeof(adcValue)/sizeof(uint16_t));

}

/***************************************************************************************

* ADC completed, calculate voltage value

*/

static void handleBattVolAdcAvail(void){

HAL_ADC_Stop_DMA(&hadc);

//disable MOSFET

HAL_GPIO_WritePin(BATT_ADC_EN_GPIO_Port, BATT_ADC_EN_Pin, GPIO_PIN_RESET);

if (xTimerIsTimerActive(myMotoTimerHandle)){

osTimerStop(myMotoTimerHandle);

}

uint32_t vol = 0;

for (int i=0;i<5;i++){

vol += adcValue[i][1] & 0xfff;

#ifdef DEBUG_LOG_LEVEL_1

printf(''val[%d][0] = %d, val[%d][1] = %d\n'', i, adcValue[i][0], i, adcValue[i][1]);

osDelay(10);

#endif

}

vol = (vol * 67 * VDDA_VOLTAGE) / (20 * 5 * 4096);

sensorData.voltageData.batteryVoltage = vol;

if (vol > BATTERY_VOLTAGE_WARNING)

sensorData.voltageData.flag = (uint8_t)BATT_INFO;

else if (vol >= BATTERY_VOLTAGE_LOW)

sensorData.voltageData.flag = (uint8_t)BATT_WARNING;

else

sensorData.voltageData.flag = (uint8_t)BATT_LOW;

sensorData.flag |= DATA_UPDATE_VOLTAGE;

//inform mainApp that vol data is ready

osSignalSet(mainAppTaskHandle, SIG_MAINAPP_VOL_DATA_OK);

}

/****************************************************************************************

* ADC DMA convert completed callback

*/

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle){

uint32_t val;

if (isVolAdcEn()){ //check Voltage

HAL_ADC_Stop_DMA(&hadc);

osSignalSet(sensorTaskHandle, SIG_SENSOR_VOL_AVAIL);

}

}
18 REPLIES 18
Max
ST Employee
Posted on April 23, 2018 at 19:19

I think you should restart the reference after wakeup:

HAL_ADCEx_EnableVREFINT();

and wait until it stabilizes before making acquisitions.

Posted on April 24, 2018 at 04:56

Max, thanks for your reply, according to your suggestion, I add HAL_ADCEx_EnableVREFINT() after system wakeup, unfortunately, problem still exist.

0690X0000060Ak3QAE.png0690X0000060AkDQAU.png
AvaTar
Lead
Posted on April 24, 2018 at 07:27

Don't know the HAL code.

I would compare the ADC register settings before stop mode, and afterwards.

And perhaps other registers that influence the ADC operation.

T J
Lead
Posted on April 24, 2018 at 17:14

do you have a calibrate function ?

on the 'F091 the ADC readings change dramatically after a calibrate.

Posted on April 25, 2018 at 03:23

TJ, yes, every wakeup from stop, I will do a ADC calibration, is this wrong?

Posted on April 25, 2018 at 04:48

it sounds like you are scanning more than one channel after stop.

oooo yes, dont use the highest clock and highest speed conversion, the result is noisy.ADCs

Are you using the DMA ? , it seems very stable and reliable.

All my projects use the ADC DMA circular buffer function to scan all the ADCs.

I write a table of 14 ADCs, 16 rows deep.

after every line conversion the Interrupt copies the DMA buffer(32bytes) to the next row in my table.

then I add all 16 rows for each channel.  It takes about 5uSec in that interrupt.

That gives me an oversampled 16bit total for each channel.

That offers you some extra stability in the reading,

You can shift it down 2/3/4.. bits if you need a more stable value.

I don't use stop, sorry for the lack of knowledge....

Bill Gates never used stop. and he said 256k Ram is all you will ever need.

Posted on April 25, 2018 at 15:56

thanks TJ for share your experience,

yes, I using 2 ADC channels, and I have a 2X10 bytes array, using DMA circle reading ADC data, every DMA interrupt, I calculate the average value of 5 samplings of every channel.

since my device is using for low-power consumption scenario, device will wake-up periodic, so have to use STOP mode.

Anyhow, I will try to downgrade the ADC clk frequency see whether can kill this problem. 

Posted on April 25, 2018 at 17:32

I downgrade ADC CLK from HSI-16M to HSI- 4M, (prescale by 16, sampling time 79.5), issue still exist.

Posted on April 26, 2018 at 01:52

go much slower, check out the results,

or else its your problematic programatic issue..

you are doing a cal on wake ?

run your DMA cycle a few times, see if it stabilizes.

My ADC-DMA runs continuously without issue.