This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Accuracy of the ADC measurement with and without calibration

Dear All,

I am using  the ADC to measure 3 voltages applied on different ADC inputs and 3 different channels.

My ADC configuration looks like this:

static void saadc_init(void)
{
    ret_code_t err_code;

    err_code = nrf_drv_saadc_init(NULL, NULL);
    APP_ERROR_CHECK(err_code);

    nrf_saadc_channel_config_t temperature_channel_config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(TEMPERATURE_ADC_DEVICE);

    err_code = nrf_drv_saadc_channel_init(TEMPERATURE_ADC_CHANNEL, &temperature_channel_config);
    APP_ERROR_CHECK(err_code);

    nrf_saadc_channel_config_t battery_channel_config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(BATTERY_ADC_DEVICE);

    battery_channel_config.gain = NRF_SAADC_GAIN2;

    err_code = nrf_drv_saadc_channel_init(BATTERY_ADC_CHANNEL, &battery_channel_config);
    APP_ERROR_CHECK(err_code);

    nrf_saadc_channel_config_t capacitor_channel_config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(CAPACITOR_ADC_DEVICE);

    capacitor_channel_config.gain = NRF_SAADC_GAIN2;

    err_code = nrf_drv_saadc_channel_init(CAPACITOR_ADC_CHANNEL, &capacitor_channel_config);
    APP_ERROR_CHECK(err_code);
}


And this is how I am performing the individual measurements:
/** @brief Function converting input voltage to temperature
 * @return the temperature in centiCelcius
 */
static uint16_t measure_temperature(void)
{
    float temperature = 0;
    float resistance = 0.0;
    nrf_saadc_value_t temperature_adc_value;
    ret_code_t err_code = nrf_drv_saadc_sample_convert(TEMPERATURE_ADC_CHANNEL, &temperature_adc_value);
    APP_ERROR_CHECK(err_code);
    uint32_t sample = TEMPERATURE_ADC_RESULT_IN_MILLI_VOLTS(temperature_adc_value);
    resistance = ((PIN_VOLTAGE / (float)sample) - 1.0) * R2;
    temperature = BETA * T2 / (BETA + (T2 * (log(resistance / R_REF))));
    temperature -= 273.15;
    temperature *= 100;
    return (uint16_t)temperature;
}

/** @brief Function measuring battery voltage in milliVolts
 * @return the battery voltage in milliVolts
 */
static int16_t measure_battery_voltage(void)
{
    int32_t sum = 0;
    for (uint8_t i = 0; i < 100; i++) {
        nrf_saadc_value_t battery_adc_value;
        ret_code_t err_code = nrf_drv_saadc_sample_convert(BATTERY_ADC_CHANNEL, &battery_adc_value);
        APP_ERROR_CHECK(err_code);
        int32_t sample = BATTERY_ADC_RESULT_IN_MILLI_VOLTS(battery_adc_value);
        sum += sample * (BATTERY_R1 + BATTERY_R2) / BATTERY_R2;
    }
    return (int16_t)(sum / 100);
}

/** @brief Function measuring capacitor voltage in milliVolts
 * @return the capacitor voltage in milliVolts
 */
static int16_t measure_capacitor_voltage(void)
{
    int32_t sum = 0;
    for (uint8_t i = 0; i < 100; i++) {
        nrf_saadc_value_t capacitor_adc_value;
        ret_code_t err_code = nrf_drv_saadc_sample_convert(CAPACITOR_ADC_CHANNEL, &capacitor_adc_value);
        APP_ERROR_CHECK(err_code);
        int32_t sample = BATTERY_ADC_RESULT_IN_MILLI_VOLTS(capacitor_adc_value);
        sum = sample * (BATTERY_R1 + BATTERY_R2) / BATTERY_R2;
    }
    return (int16_t)(sum / 100);
}


This approach yields overall quite good results, with low standard deviation for the measurements and quite low error when comparing the input voltage to the average over 2000 measurements.

Trying to investigate what happens at different temperatures, I did some measurements in room temperature (~25C) and then using an oven I measured under 40C. The results here were somewhat weird, because, on 2 different devices, I saw the error between the average measurement and the applied voltage decrease when the temperature was higher.

So then I decided to perform ADC calibration, upon initializing the device and also every 5 seconds after that, in the hope that this would further improve the accuracy, but instead, the accuracy went lower when I measured in room temperature.

What I do in this case is:

static void adc_calibration_handler(void* p_context)
{
    ret_code_t err_code;
    nrf_drv_saadc_abort();
    err_code = nrf_drv_saadc_calibrate_offset();
    APP_ERROR_CHECK(err_code);
    adc_calibration_pending = true;
}



This makes me wonder if I am missing anything in my configuration.

This is what I have in the sdk_config.h:
// <e> SAADC_ENABLED - nrf_drv_saadc - SAADC peripheral driver - legacy layer
//==========================================================
#ifndef SAADC_ENABLED
#define SAADC_ENABLED 1
#endif
// <o> SAADC_CONFIG_RESOLUTION  - Resolution
 
// <0=> 8 bit 
// <1=> 10 bit 
// <2=> 12 bit 
// <3=> 14 bit 

#ifndef SAADC_CONFIG_RESOLUTION
#define SAADC_CONFIG_RESOLUTION 1
#endif

// <o> SAADC_CONFIG_OVERSAMPLE  - Sample period
 
// <0=> Disabled 
// <1=> 2x 
// <2=> 4x 
// <3=> 8x 
// <4=> 16x 
// <5=> 32x 
// <6=> 64x 
// <7=> 128x 
// <8=> 256x 

#ifndef SAADC_CONFIG_OVERSAMPLE
#define SAADC_CONFIG_OVERSAMPLE 0
#endif

// <q> SAADC_CONFIG_LP_MODE  - Enabling low power mode
 

#ifndef SAADC_CONFIG_LP_MODE
#define SAADC_CONFIG_LP_MODE 1
#endif

// <o> SAADC_CONFIG_IRQ_PRIORITY  - Interrupt priority
 

// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice
// <0=> 0 (highest) 
// <1=> 1 
// <2=> 2 
// <3=> 3 
// <4=> 4 
// <5=> 5 
// <6=> 6 
// <7=> 7 

#ifndef SAADC_CONFIG_IRQ_PRIORITY
#define SAADC_CONFIG_IRQ_PRIORITY 6
#endif

// </e>


I am using the v16 of the nRF5 SDK.

Thank you very much for your assistance.

Parents Reply Children
No Data
Related