problem: not accurate measurement of battery using ADC

I'm trying to accurately measure our 3.0 volt coin battery (CR2450).

Our product consume low power (<50 uA) and we measure the battery voltage every minute.

We must have an accurate measurement in order to corectly estimate the time left for our product.

the measurements voltage changes over time in 0.01/0.02 volts:

sometimes goes down, somtimes goes up 

What I need to do in order to have an accorate measurements

I attached a typical log, circuit drawing and relevant code:

            // sample every one minute
            vdd = peripherals_read_vdd();


            vdd_float      = ((float)vdd) / 1000;
            vdd_on_no_load = vdd;

            // save measurement value in flash
            event_create_measurement(vdd_float, (float)(temperature), false);
            last_saved_measurement = xTaskGetTickCount();
            first                  = false;

            terminal_buffer_lock();
            sprintf(alert_str, "\r\nStatus: T=%dC, V=%.2fV\r\n", temperature, vdd_float);
            terminal_display_message(alert_str, 0, false);
            terminal_buffer_release();




static void init_saadc(void)
{
    nrfx_err_t err_code;

    err_code = nrfx_saadc_init(NRFX_SAADC_CONFIG_IRQ_PRIORITY);
    APP_ERROR_CHECK(err_code);

    calibrate_saadc();
    init_saadc_channels();
}

static void calibrate_saadc(void)
{
    nrfx_err_t err_code;

    err_code = nrfx_saadc_offset_calibrate(NULL);
    APP_ERROR_CHECK(err_code);
}

static void init_saadc_channels(void)
{
    nrfx_err_t err_code;

    // trying external input or internal
#if VBAT_MEASURE_EXTERNAL_PIN_EN == 1
    nrfx_saadc_channel_t channels[] = {NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN0, HAL_SAADC_VBAT_CHANNEL)};
#else
    nrfx_saadc_channel_t channels[] = {NRFX_SAADC_DEFAULT_CHANNEL_SE(HAL_ADC_VBATT, HAL_SAADC_VBAT_CHANNEL)};
#endif

    channels[0].channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
    channels[0].channel_config.gain      = NRF_SAADC_GAIN1_6;
    // channels[0].channel_config.acq_time  = NRF_SAADC_ACQTIME_40US;
    channels[0].channel_config.acq_time  = NRF_SAADC_ACQTIME_3US;

    err_code = nrfx_saadc_channels_config(channels, sizeof(channels) / sizeof(nrfx_saadc_channel_t));
    if (NRFX_SUCCESS != err_code)
        NRFX_LOG_ERROR("%s nrfx_saadc_channels_config failed: %s", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
    APP_ERROR_CHECK(err_code);
}

inline uint16_t peripherals_read_vdd(void)
{
    uint32_t vdd_adc = hal_read_vdd_raw();

    // INFO https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/measuring-lithium-battery-voltage-with-nrf52
    vdd_adc *= 100 * 6 * 6; // Convert to mV, Gain 1/6, Ref 0.6V

#if VBAT_MEASURE_EXTERNAL_PIN_EN == 1
    // return ROUNDED_DIV(vdd_adc, 4095); // Div by 4095 (12 bits)  and X2
    return (vdd_adc >> 11);
#else

    // return ROUNDED_DIV(vdd_adc, 4095); // Div by 4095 (12 bits)
    return (vdd_adc >> 12);
#endif
}





int16_t hal_read_vdd_raw(void)
{
    nrfx_err_t        err_code;
    nrf_saadc_value_t battery_voltage[1];
    uint32_t          channels = (1 << HAL_SAADC_VBAT_CHANNEL);

    err_code = nrfx_saadc_simple_mode_set(channels, NRFX_SAADC_CONFIG_RESOLUTION, NRFX_SAADC_CONFIG_OVERSAMPLE, NULL);
    APP_ERROR_CHECK(err_code);

    err_code = nrfx_saadc_buffer_set(battery_voltage, 1);
    APP_ERROR_CHECK(err_code);

    err_code = nrfx_saadc_mode_trigger();
    APP_ERROR_CHECK(err_code);

    if (err_code != NRFX_SUCCESS) {
        NRFX_LOG_ERROR("%s %s", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
        return UINT8_MAX;
    }

    return battery_voltage[0];
}

---------------------------------------------------- - measurement log ----------------------------------------------------------------\


Status: T=0C, V=2.88V

09-01-22 12:01:39 Event Created: sector=271, id=51656, type=14

Status: T=0C, V=2.91V

09-01-22 12:02:38 Event Created: sector=271, id=51657, type=14

Status: T=0C, V=2.91V

09-01-22 12:03:36 Event Created: sector=271, id=51658, type=14

Status: T=0C, V=2.91V

09-01-22 12:04:35 Event Created: sector=271, id=51659, type=14

Status: T=0C, V=2.89V

09-01-22 12:05:33 Event Created: sector=271, id=51660, type=14

Status: T=0C, V=2.90V

09-01-22 12:06:32 Event Created: sector=271, id=51661, type=14

Status: T=0C, V=2.90V

09-01-22 12:07:31 Event Created: sector=271, id=51662, type=14

Status: T=0C, V=2.90V

09-01-22 12:08:29 Event Created: sector=271, id=51663, type=14

Status: T=0C, V=2.90V

09-01-22 12:09:28 Event Created: sector=271, id=51664, type=14

Status: T=0C, V=2.90V

09-01-22 12:10:27 Event Created: sector=271, id=51665, type=14

Status: T=0C, V=2.91V

09-01-22 12:11:25 Event Created: sector=271, id=51666, type=14

Status: T=0C, V=2.90V

09-01-22 12:12:24 Event Created: sector=271, id=51667, type=14

Status: T=0C, V=2.90V

09-01-22 12:13:22 Event Created: sector=271, id=51668, type=14

Status: T=0C, V=2.91V

09-01-22 12:14:21 Event Created: sector=271, id=51669, type=14

Status: T=0C, V=2.90V

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

our product

Parents
  • It looks like to me that you're already having accurate measurement with tens of millivolts as noise. The battery voltage will swing a lot more based on temperature and load. 

    The best thing I have done is to synchronize voltage measurement so that voltage is sampled after heavy activity, such as radio TX. The voltage will drop steeply at start, stay relatively flat for most of the device life time and will start to drop again when battery is nearing the end. I have not found a good formula to estimate remaining run time, but it's possible to give a "battery low" warning based on loaded voltage. You'll need to find your threshold for battery low at your temperature, load and battery model. 

Reply
  • It looks like to me that you're already having accurate measurement with tens of millivolts as noise. The battery voltage will swing a lot more based on temperature and load. 

    The best thing I have done is to synchronize voltage measurement so that voltage is sampled after heavy activity, such as radio TX. The voltage will drop steeply at start, stay relatively flat for most of the device life time and will start to drop again when battery is nearing the end. I have not found a good formula to estimate remaining run time, but it's possible to give a "battery low" warning based on loaded voltage. You'll need to find your threshold for battery low at your temperature, load and battery model. 

Children
Related