Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SAADC reading changes after system finish booting

Hi everyone,

I am having problems reading the battery level using the SAADC. The main problem is that the reading is not consistent from when the device is booting to once the device has boot completely an is stable/IDLE. 

During the initialization the reading is around 4.2 V for battery totally charged while later it drops to around 3.6 V.

Hardware Configuration:

To read the battery, I have a voltage divider connected to the AIN0 pin for reading the battery voltage. The resistor pulled to ground is 51K ohms while the one pulled to VBAT is 100k ohms. I had checked the voltage across both resistor with a multimeter and it stays consistent the whole time. So, it seems like a software problem.

Software Configuration:

For reading the battery, I am using a simple blocking approach using the SAADC in single mode in the pin AIN0. The SAADC is configured as:

   // SAADC configuration
    nrf_drv_saadc_config_t saadc_config;           
    saadc_config.low_power_mode = true;
    saadc_config.resolution = SAADC_RESOLUTION;
    saadc_config.oversample = SAADC_OVERSAMPLE;
    
    // SAADC CHANNEL configuration
    channel_config.gain = NRF_SAADC_GAIN1_6;
    channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
    channel_config.acq_time = NRF_SAADC_ACQTIME_20US;
    channel_config.burst = SAADC_BURST_MODE;
    err_code = nrf_drv_saadc_channel_init(0, &channel_config);              // Init SAADC channel

and then use the simple function `nrf_drv_saadc_sample_convert()` to read the battery, like:

    nrf_saadc_value_t value;
    ret_code_t err_code;
    
    saadc_init(false);
    
    err_code = nrf_drv_saadc_sample_convert(0, &value);
    APP_ERROR_CHECK(err_code);

    saadc_uninit();
    
    

The oversampling and burst are currently disable, but I had tried those to with same result. I had also tried calibrate before the reading, use a buffer to get an average value or increase the acquisition time but none of those helped at all. I still got the same results.

One thing worthy to mention is that, the battery read changes after the LCD is turned on and the system uses SPI for communicate with the LCD hardware. This makes me wonder if there is a possibility that the SPI could cause problems with the SAADC somehow. 

Regards,

spw.

Parents
  • Hello,

    So the SAADC readings is different depening on what state you are in? Does it only happen if you read the battery? Or does it happen if you try to read another source as well?

    Best regards,

    Edvin

  • Hi Edvin,

    Mora than in the state, it depends on the LCD being ON, or not. One of my latest test has been:

    1- Boot the device normally. (this includes, turn on the LCD).

    2- Read the battery using the SAADC.

    3- Cut off the LCD power using the voltage regulator.

    4- Read the battery using the SAADC.

    In this test, the result was a correct battery reading after the LCD power was cut off (step 4), but not before cut the power (step 2).

    So, somehow, it seems like the LCD is interfering with the SAADC reading, but I have no idea how. Just as an extra piece of information, the LCD is the ili9341, and it's controlled through as SPI communication as I explained previously.

  • Ideally the voltage where you have connected the ADC pin that you use. Is that not accessible?

    In addition. What sort of operation do you do approximately at the same time that you measure the ADC? Do you do it in a BLE event? Do you do it when you receive an event related to the PCB? Or do you call it from a timer interrupt or something?

    And also, is it possible to send your PCB layout, so that I can see where you are measureing?

  • Hi, sorry for the late response.

    The pin where I connected the SAADC pin is not much accessible. I can connect the multimeter, but it will be hard to connect the oscilloscope and impossible to measure the current through it without desolder on the PCB. (I can't do this, I am not skilled enough)

    I have tried to disable most of the components, and as I said in my previous post only cut off the power of the LCD seems to allow a correct reading of the battery. I have tried reading the battery in timers and in normal loop operation, using blocking and non-blocking mode for SAADC, and all of them reported the same kind of problem.

    I had attached 3 images:

    1. It's an image of the MIC2091 layout which output is connected to resistor divider that is connected to the AIN0 pin of the nordic to measure the battery level. (I marked the point were I make my measurements.)

    2. It's an image of the battery charger layout which is connected to the battery and also to the MIC2091 input using the VBAT line.

    3. This is a partial image of the Nordic pins related to the battery monitor and charging. 

    Regards,

    spw

  • Hello,

    I just read your initial post again:

     

    During the initialization the reading is around 4.2 V for battery totally charged while later it drops to around 3.6 V.

     Is this when the battery is totally charged vs when it is almost depleted, or is it within a few seconds?

    And again, is it possible to shut everything else off? the LCD and everything potentially drawing a lot of current? Remember that the more current you draw, the more current will go through R24, and the voltage on your measuring point will be lower. 

    spw said:
    as I said in my previous post only cut off the power of the LCD seems to allow a correct reading of the battery.

     This suggests that this is what is actually happening. The LCD draws more current, increasing the voltage drop over R24, decreasing the voltage on your measuring point. 

    If you want a layout review, I recommend that you create a new ticket explaining that, and it will be assigned to one of our HW engineers. 

     

    Best regards,

    Edvin

  • Is this when the battery is totally charged vs when it is almost depleted, or is it within a few seconds?

    This drop happens within the first seconds, after the LCD is power up and configured, all readings from the SAADC drops but the readings on R26 and R24 with the multimeter stills the same.

    I have shut down everything, the only thing that seems affecting is the LCD. The LCD uses SPI communication, not sure if that is relevant.

     This suggests that this is what is actually happening. The LCD draws more current, increasing the voltage drop over R24, decreasing the voltage on your measuring point. 

    Well, I am not so sure about it, as I said above, the multimeter reads the same voltage drop on R26 and R24 during the boot, but the reading from the SAADC is for some reason different. 

    If you want a layout review, I recommend that you create a new ticket explaining that, and it will be assigned to one of our HW engineers. 

    Honestly, it's not that I want a layout review or similar, I attached the layout because you requested it. I just want to figure it out this issue which to me seems a software issue.

    Why do you think this might be a hardware issue? 

    To me, the fact that the multimeter reads a voltage drop different than the SAADC reading mean that there is some kind of software issue.

    Regards,

    spw

  • spw said:
    Honestly, it's not that I want a layout review or similar, I attached the layout because you requested it. I just want to figure it out this issue which to me seems a software issue.

     Sorry. I forgot. It is a month since I last saw this ticket. 

    I am not sure whether this is a software or HW issue. Is it possible for me to recreate this on a DK without the LCD display?

    Can you try to measure with an oscilloscope instead of a multimeter? Maybe something is drawing power very shortly around the time when you are using the ADC? A multimeter will not be able to pick up these quick spikes/dips. 

    BR,

    Edvin

Reply
  • spw said:
    Honestly, it's not that I want a layout review or similar, I attached the layout because you requested it. I just want to figure it out this issue which to me seems a software issue.

     Sorry. I forgot. It is a month since I last saw this ticket. 

    I am not sure whether this is a software or HW issue. Is it possible for me to recreate this on a DK without the LCD display?

    Can you try to measure with an oscilloscope instead of a multimeter? Maybe something is drawing power very shortly around the time when you are using the ADC? A multimeter will not be able to pick up these quick spikes/dips. 

    BR,

    Edvin

Children
  • Is it possible for me to recreate this on a DK without the LCD display?

    No, I don't think it is really possible to recreate it with a development kit. This is probably specific to the PCB & firmware developed.

    Can you try to measure with an oscilloscope instead of a multimeter?

    I don't think it is possible, I will try it, but I don't think there is a good way to connect the oscilloscope to the PCB in that specific point.

    Maybe something is drawing power very shortly around the time when you are using the ADC? A multimeter will not be able to pick up these quick spikes/dips. 

    I have tried to read continuously the battery using the SAADC in blocking mode, and doing nothing else on the firmware than that (No interruptions either). And all values seems the same result, so I don't think there are spikes/dips happening at the same time of the SAADC, unless these spikes/dips are very common. 

    Regards,

    spw

  • Ok. Let us try a new approach? Have you done any changes to the SAADC settings? Do you have the same settings in NRFX_SAADC_CONFIG_... and SAADC_CONFIG_...?

  • I haven't done any changes, the configuration it's the same in sdk_config.h

  • Two suggestions, first use completely different software since a software issue is suspected; second take a measurement on Vbatt then take a measurement on Vdd to ensure something peculiar is not happening to the latter. Sample ADC twice in both cases to avoid SAADC issues described elsewhere; don't bother with calibration.

    To  run this sample ensure that the SAADC driver code you were using is not instantiated.

    // Analog positive and negative input channels have strange numbering:
    //  PSELP_NC           0UL  Not connected
    //  PSELP_AnalogInput0 1UL  AIN0
    //  PSELP_AnalogInput1 2UL  AIN1
    //  PSELP_AnalogInput2 3UL  AIN2
    //  PSELP_AnalogInput3 4UL  AIN3
    //  PSELP_AnalogInput4 5UL  AIN4
    //  PSELP_AnalogInput5 6UL  AIN5
    //  PSELP_AnalogInput6 7UL  AIN6
    //  PSELP_AnalogInput7 8UL  AIN7
    //  PSELP_VDD          9UL  VDD
    #define PSELP_AnalogInput0 1
    #define PSELP_VDD          9
    int16_t MeasureBattery(void)
    {
        volatile int16_t AdcCounts = 0;
    #if 0
      // Start HFCLK from crystal oscillator, this will give the SAADC higher accuracy
      NRF_CLOCK->TASKS_HFCLKSTART = 1;
      while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
      NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    #endif
        // Configure SAADC singled-ended channel, Internal reference (0.6V) and 1/6 gain
        NRF_SAADC->CH[1].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_6    << SAADC_CH_CONFIG_GAIN_Pos) |
                                  (SAADC_CH_CONFIG_MODE_SE         << SAADC_CH_CONFIG_MODE_Pos) |
                                  (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                                  (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos) |
                                  (SAADC_CH_CONFIG_RESP_Bypass     << SAADC_CH_CONFIG_RESP_Pos) |
                                  (SAADC_CH_CONFIG_TACQ_40us       << SAADC_CH_CONFIG_TACQ_Pos);
    
        // Configure the SAADC channel with battery as positive input, no negative input(single ended)
        NRF_SAADC->CH[1].PSELP = PSELP_AnalogInput0;  // Also do PSELP_VDD for Vdd supply
        NRF_SAADC->CH[1].PSELN = SAADC_CH_PSELN_PSELN_NC << SAADC_CH_PSELN_PSELN_Pos;
    
        // Configure the SAADC resolution; 14-bit can be used but manual suggests not reliable for single sample
        // NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_14bit << SAADC_RESOLUTION_VAL_Pos;
        NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_12bit << SAADC_RESOLUTION_VAL_Pos;
    
        // Configure result to be put in RAM at the location of "AdcCounts" variable
        NRF_SAADC->RESULT.MAXCNT = 1;
        NRF_SAADC->RESULT.PTR = (uint32_t)&AdcCounts;
    
        // No automatic sampling, will trigger with TASKS_SAMPLE
        NRF_SAADC->SAMPLERATE = SAADC_SAMPLERATE_MODE_Task << SAADC_SAMPLERATE_MODE_Pos;
    
        // Enable SAADC
        NRF_SAADC->ENABLE = 1;
    
        // Start the SAADC and wait for the started event
        NRF_SAADC->TASKS_START = 1;
        while (NRF_SAADC->EVENTS_STARTED == 0)
            ;
        NRF_SAADC->EVENTS_STARTED = 0;
    
        // Do a SAADC sample, will put the result in the configured RAM buffer
        NRF_SAADC->TASKS_SAMPLE = 1;
        while (NRF_SAADC->EVENTS_END == 0)
            ;
        NRF_SAADC->EVENTS_END = 0;
    
        // Stop the SAADC, since it's not used anymore
        NRF_SAADC->TASKS_STOP = 1;
        while (NRF_SAADC->EVENTS_STOPPED == 0)
            ;
        NRF_SAADC->EVENTS_STOPPED = 0;
    
        // second sample
        // Start the SAADC and wait for the started event
        NRF_SAADC->TASKS_START = 1;
        while (NRF_SAADC->EVENTS_STARTED == 0)
            ;
        NRF_SAADC->EVENTS_STARTED = 0;
        // Do a SAADC sample, will put the result in the configured RAM buffer.
        NRF_SAADC->TASKS_SAMPLE = 1;
        while (NRF_SAADC->EVENTS_END == 0)
            ;
        NRF_SAADC->EVENTS_END = 0;
        // Stop the SAADC, since it's not used anymore.
        NRF_SAADC->TASKS_STOP = 1;
        while (NRF_SAADC->EVENTS_STOPPED == 0)
            ;
        NRF_SAADC->EVENTS_STOPPED = 0;
    
       // Disable SAADC
       NRF_SAADC->ENABLE = 0;
       return AdcCounts;
    }

Related