Skip to main content
Visitor II
April 23, 2018
Question

ADC behavior abnormal after wake up from STOP mode

  • April 23, 2018
  • 8 replies
  • 5695 views
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);

}

}
    This topic has been closed for replies.

    8 replies

    ST Employee
    April 23, 2018
    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.

    dz huangAuthor
    Visitor II
    April 24, 2018
    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
    Visitor II
    April 24, 2018
    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.

    Visitor II
    April 24, 2018
    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.

    dz huangAuthor
    Visitor II
    April 25, 2018
    Posted on April 25, 2018 at 03:23

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

    Visitor II
    April 25, 2018
    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.

    Visitor II
    April 26, 2018
    Posted on April 26, 2018 at 11:11

    I've seen this issue raised by several uses of Mbed and the mDot/xDot devices which contain an STM32. I can only find these two references now:

    https://os.mbed.com/questions/52467/Cant-read-ADC-after-WakeUp-from-deepslee/

     

    https://os.mbed.com/questions/78300/xDot-ADC-problem-when-coming-out-of-slee/

     

    and both suggest the HSI has to be explicitly re-enabled, but it looks like you're already calling

    SystemClock_Config

    (). I thought there was another solution along the lines having to manually reconfigure something but can't find it now.
    dz huangAuthor
    Visitor II
    April 28, 2018
    Posted on April 28, 2018 at 12:36

    yes, after wake-up, I have called HSI init, this patch seems do not work on my board. I am trying to do ADC register value comparation before and after STOP.  

    dz huangAuthor
    Visitor II
    April 29, 2018
    Posted on April 29, 2018 at 08:44

    I have print all ADC and DMA register before and after wakeup, no any finding. following is the output of ADC & DMA register values comparation before and after waekup from stop. 

    I am suspecting  whether this is a STM32L071RB chipset bug now.

    Start to fetch battery voltage

    val[0][0] = 1671, val[0][1] = 1536

    val[1][0] = 1673, val[1][1] = 1535

    val[2][0] = 1675, val[2][1] = 1536

    val[3][0] = 1674, val[3][1] = 1535

    val[4][0] = 1671, val[4][1] = 1535

    Voltage result->s 4144mv

    Before STOP

    before adc-deinit

    ADC->ISR : 0x00000808H

    ADC->IER : 0x00000000H

    ADC->CR : 0x10000000H

    ADC->CFGR1 : 0x00002002H

    ADC->CFGR2 : 0x00000000H

    ADC->TR : 0x0FFF0000H

    ADC->CHSELR : 0x00000003H

    ADC->DR : 0x000005FFH

    ADC->CALFACT: 0x0000003DH

    DMA->CCR : 0x000015AEH

    DMA->CNDTR: 0x0000000AH

    DMA->CPAR : 0x40012440H

    DMA->CMAR : 0x200025FCH

    after adc-dinit

    ADC->ISR : 0x00000000H

    ADC->IER : 0x00000000H

    ADC->CR : 0x00000000H

    ADC->CFGR1 : 0x00000000H

    ADC->CFGR2 : 0x00000000H

    ADC->TR : 0x00000000H

    ADC->CHSELR : 0x00000000H

    ADC->DR : 0x00000000H

    ADC->CALFACT: 0x00000000H

    DMA->CCR : 0x00000000H

    DMA->CNDTR: 0x00000000H

    DMA->CPAR : 0x00000000H

    DMA->CMAR : 0x00000000H

    Enter STOP

    wakeup from STOP

    after stop, before adc-init

    ADC->ISR : 0x00000000H

    ADC->IER : 0x00000000H

    ADC->CR : 0x00000000H

    ADC->CFGR1 : 0x00000000H

    ADC->CFGR2 : 0x00000000H

    ADC->TR : 0x00000000H

    ADC->CHSELR : 0x00000000H

    ADC->DR : 0x00000000H

    ADC->CALFACT: 0x00000000H

    DMA->CCR : 0x00000000H

    DMA->CNDTR: 0x00000000H

    DMA->CPAR : 0x00000000H

    DMA->CMAR : 0x00000000H

    after adc-reinit

    ADC->ISR : 0x00000800H

    ADC->IER : 0x00000000H

    ADC->CR : 0x10000000H

    ADC->CFGR1 : 0x00002002H

    ADC->CFGR2 : 0x00000000H

    ADC->TR : 0x0FFF0000H

    ADC->CHSELR : 0x00000003H

    ADC->DR : 0x0000003EH

    ADC->CALFACT: 0x0000003EH

    DMA->CCR : 0x000015A0H

    DMA->CNDTR: 0x00000000H

    DMA->CPAR : 0x00000000H

    DMA->CMAR : 0x00000000H

    Echo:gvol

    Start to fetch battery voltage

    val[0][0] = 1669, val[0][1] = 4095

    val[1][0] = 2047, val[1][1] = 2047

    val[2][0] = 2047, val[2][1] = 2047

    val[3][0] = 2047, val[3][1] = 2047

    val[4][0] = 2047, val[4][1] = 2047

    Voltage result->s 6630mv
    Graduate
    April 26, 2018
    Posted on April 26, 2018 at 14:06

    Probably not directly relevant but maybe something interesting in this thread :

    https://community.st.com/0D50X00009XkYPSSA3

    dz huangAuthor
    Visitor II
    May 2, 2018
    Posted on May 02, 2018 at 10:13

    thanks all of person's help, finially, I found the fix. if I enable ultra low power mode, this issue disappeared, although I don't understand the real reason.

    HAL_PWREx_EnableUltraLowPower();

    0690X0000060AsRQAU.png
    Visitor II
    May 2, 2018
    Posted on May 02, 2018 at 12:11

    Thanks very much for the follow-up, and thanks to Thomas for digging up a thread I was thinking of. There's definitely some peculiarities related to the ADC and sleep modes in the STM32 family. Hopefully threads like this will help others find a solution.

    Visitor II
    October 17, 2023

    I just wanted to bump this for the mods.

    Has anyone found a solution? In any configuration if we enable ULP, after STOP wakeup the vref seems corrupt.

    Visitor II
    October 30, 2023

    @dz huang 

    Try disabling the ULP before ADC calibration and enable when you are done with the ADC.

    This worked for us:

     

    void ADC_Start() {
     LL_PWR_DisableUltraLowPower();
     HAL_ADCEx_EnableVREFINT(); //Wait for vref to start
    
     HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED);
     HAL_ADC_Start_DMA(&hadc, (uint32_t*) &ADC_raw, adc_Channels);
    }
    
    void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
     HAL_ADC_Stop_DMA(hadc);
     LL_PWR_EnableUltraLowPower();
    }

     

     

     

     

    Visitor II
    September 2, 2024

    @Head 

    After several hours I have found what was wrong.

    When I switch to Ultra Low Power Mode I had to switch IO ports to alternative function mode too ( to save some current ).

    And that was the reason.

    On PB0 or PB1 ( don't remember ) I have Vref out with few uA ability to drive with pull down on same pin.

    So when I swich port to alternate function I have pull down on Vref and it is not able to restart properly due to load.

    It is enough to not switch I/o port to AF before entering ULP. Instead AF , input is enough.

    Adam