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

Sometimes nrf52840 aadc calibration is wrong

Using the nrf52840 for analog sensor measurements we wanted to increase the accuracy by doing a regular calibration. Doing so we sometimes noticed spurious changes in measurement offset error when the calibration was done. After investigating this behaviour on a devboard it looks like the calibration is just sometimes off!

The following can be used to reproduce the problem on a devboard using AIN0 with an open-input.

/**
This file shows that the adc calibration make the measurements fluctuate
*/

#include "aadc_errors.h"


#include <nrf_delay.h>
#include <nrfx_saadc.h>

#include <stdio.h>
#include <stdlib.h>

#define ADC_RESOLUTION 4096u /// 12 bit adc reolution (14 bit is only accomplished with oversampling)

#define CHANNEL_CONFIG(PIN, RESISTOR_P, GAIN) (nrf_saadc_channel_config_t)\
{\
    .resistor_p = RESISTOR_P,      \
    .resistor_n = NRF_SAADC_RESISTOR_DISABLED,      \
    .gain       = GAIN,                \
    .reference  = NRF_SAADC_REFERENCE_INTERNAL,     \
    .acq_time   = NRF_SAADC_ACQTIME_10US,           \
    .mode       = NRF_SAADC_MODE_SINGLE_ENDED,      \
    .burst      = NRF_SAADC_BURST_DISABLED,         \
    .pin_p      = PIN,       \
    .pin_n      = NRF_SAADC_INPUT_DISABLED         \
}

static nrfx_saadc_config_t saadc_config = NRFX_SAADC_DEFAULT_CONFIG;

static void saadc_callback(nrfx_saadc_evt_t const * p_event)
{
    UNUSED_PARAMETER(p_event);
}

// sample 1 channel, already configured
static void channel_convert(uint8_t channel, unsigned oversample, nrf_saadc_value_t *adc)
{
    int adc_sum = 0;
    for( unsigned i = 0; i<oversample+1; i++ )
    {
        ret_code_t rr = nrfx_saadc_sample_convert(channel, adc);
        ASSERT( rr == NRF_SUCCESS );
        //printf("    part_adc=%hd\n", *adc);
        adc_sum += *adc;
    }
    
    int adc_i = adc_sum / (int)(oversample+1);

    *adc = adc_i;
}

static void mcu_adc_calibrate(void)
{
    ret_code_t r;
    
    printf("Calibrate\n");

    r = nrfx_saadc_calibrate_offset();
    ASSERT(r == NRF_SUCCESS);
    
    while (nrfx_saadc_is_busy())
    {
    }

    // Nordic errata 86 workaround...
    nrfx_saadc_uninit();
    r = nrfx_saadc_init(&saadc_config, saadc_callback);
    ASSERT(r == NRF_SUCCESS);
        
    // Note: According documentation, the errata is also handled in    nrfx_saadc_abort()
}

static void mcu_adc_read_gnd(nrf_saadc_gain_t gain, nrf_saadc_value_t *adc)
{
    nrf_saadc_channel_config_t chcfg = CHANNEL_CONFIG(NRF_SAADC_INPUT_AIN0, NRF_SAADC_RESISTOR_PULLDOWN, gain);
    
    nrfx_saadc_channel_init(0, &chcfg);
    // get rid of noise by oversampling a lot
    channel_convert(0, 1000, adc);
    nrfx_saadc_channel_uninit(0);
    nrfx_saadc_abort();
    
    // FIXME this is a patch for the fact the that ADC does not stop (reason unknonw).
    nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
}

void show_calibration_error(void)
{
    ret_code_t err_code = nrfx_saadc_init(&saadc_config, saadc_callback);
    ASSERT(err_code == NRF_SUCCESS);
    
    for(unsigned j=0; j<10; j++)
    {
        mcu_adc_calibrate();
        for(unsigned i=0; i<2; i++)
        {
            nrf_saadc_value_t adc = 0;
            const char *sep = "   ";
            
            for( nrf_saadc_gain_t gain = NRF_SAADC_GAIN1_6; gain <=NRF_SAADC_GAIN4; gain++ )
            {
                mcu_adc_read_gnd( gain, &adc );
                printf("%sadc:{gain:%d, v:%hd}", sep, gain, adc);
                sep = ", ";
            }

            printf("\n");
        }
    }
    
    nrfx_saadc_uninit();
}




This results in something like:


Calibrate
   adc:{gain:0, v:0}, adc:{gain:1, v:0}, adc:{gain:2, v:0}, adc:{gain:3, v:0}, adc:{gain:4, v:3}, adc:{gain:5, v:9}, adc:{gain:6, v:24}, adc:{gain:7, v:51}
   adc:{gain:0, v:0}, adc:{gain:1, v:0}, adc:{gain:2, v:1}, adc:{gain:3, v:0}, adc:{gain:4, v:3}, adc:{gain:5, v:9}, adc:{gain:6, v:24}, adc:{gain:7, v:53}
Calibrate
   adc:{gain:0, v:-12}, adc:{gain:1, v:-11}, adc:{gain:2, v:-10}, adc:{gain:3, v:-11}, adc:{gain:4, v:-8}, adc:{gain:5, v:-1}, adc:{gain:6, v:13}, adc:{gain:7, v:42}
   adc:{gain:0, v:-12}, adc:{gain:1, v:-11}, adc:{gain:2, v:-11}, adc:{gain:3, v:-11}, adc:{gain:4, v:-8}, adc:{gain:5, v:-2}, adc:{gain:6, v:14}, adc:{gain:7, v:41}
Calibrate
   adc:{gain:0, v:-1}, adc:{gain:1, v:0}, adc:{gain:2, v:0}, adc:{gain:3, v:0}, adc:{gain:4, v:3}, adc:{gain:5, v:9}, adc:{gain:6, v:24}, adc:{gain:7, v:51}
   adc:{gain:0, v:0}, adc:{gain:1, v:0}, adc:{gain:2, v:1}, adc:{gain:3, v:0}, adc:{gain:4, v:3}, adc:{gain:5, v:10}, adc:{gain:6, v:25}, adc:{gain:7, v:52}
Calibrate
   adc:{gain:0, v:-12}, adc:{gain:1, v:-11}, adc:{gain:2, v:-10}, adc:{gain:3, v:-10}, adc:{gain:4, v:-8}, adc:{gain:5, v:-2}, adc:{gain:6, v:14}, adc:{gain:7, v:42}
   adc:{gain:0, v:-12}, adc:{gain:1, v:-11}, adc:{gain:2, v:-10}, adc:{gain:3, v:-11}, adc:{gain:4, v:-8}, adc:{gain:5, v:-1}, adc:{gain:6, v:13}, adc:{gain:7, v:42}
Calibrate
   adc:{gain:0, v:-11}, adc:{gain:1, v:-11}, adc:{gain:2, v:-10}, adc:{gain:3, v:-11}, adc:{gain:4, v:-7}, adc:{gain:5, v:-1}, adc:{gain:6, v:14}, adc:{gain:7, v:42}
   adc:{gain:0, v:-11}, adc:{gain:1, v:-11}, adc:{gain:2, v:-10}, adc:{gain:3, v:-10}, adc:{gain:4, v:-8}, adc:{gain:5, v:-1}, adc:{gain:6, v:13}, adc:{gain:7, v:42}
Calibrate
   adc:{gain:0, v:-1}, adc:{gain:1, v:0}, adc:{gain:2, v:0}, adc:{gain:3, v:0}, adc:{gain:4, v:4}, adc:{gain:5, v:9}, adc:{gain:6, v:24}, adc:{gain:7, v:53}
   adc:{gain:0, v:0}, adc:{gain:1, v:0}, adc:{gain:2, v:1}, adc:{gain:3, v:1}, adc:{gain:4, v:3}, adc:{gain:5, v:10}, adc:{gain:6, v:24}, adc:{gain:7, v:52}
Calibrate
   adc:{gain:0, v:0}, adc:{gain:1, v:0}, adc:{gain:2, v:0}, adc:{gain:3, v:1}, adc:{gain:4, v:4}, adc:{gain:5, v:9}, adc:{gain:6, v:24}, adc:{gain:7, v:53}
   adc:{gain:0, v:0}, adc:{gain:1, v:0}, adc:{gain:2, v:0}, adc:{gain:3, v:0}, adc:{gain:4, v:3}, adc:{gain:5, v:9}, adc:{gain:6, v:24}, adc:{gain:7, v:52}
Calibrate
   adc:{gain:0, v:-11}, adc:{gain:1, v:-10}, adc:{gain:2, v:-11}, adc:{gain:3, v:-11}, adc:{gain:4, v:-8}, adc:{gain:5, v:-2}, adc:{gain:6, v:13}, adc:{gain:7, v:42}
   adc:{gain:0, v:-12}, adc:{gain:1, v:-11}, adc:{gain:2, v:-11}, adc:{gain:3, v:-10}, adc:{gain:4, v:-7}, adc:{gain:5, v:-1}, adc:{gain:6, v:13}, adc:{gain:7, v:41}
Calibrate
   adc:{gain:0, v:-12}, adc:{gain:1, v:-11}, adc:{gain:2, v:-10}, adc:{gain:3, v:-10}, adc:{gain:4, v:-7}, adc:{gain:5, v:-2}, adc:{gain:6, v:13}, adc:{gain:7, v:42}
   adc:{gain:0, v:-12}, adc:{gain:1, v:-11}, adc:{gain:2, v:-11}, adc:{gain:3, v:-11}, adc:{gain:4, v:-8}, adc:{gain:5, v:-1}, adc:{gain:6, v:13}, adc:{gain:7, v:42}
Calibrate
   adc:{gain:0, v:-12}, adc:{gain:1, v:-12}, adc:{gain:2, v:-10}, adc:{gain:3, v:-10}, adc:{gain:4, v:-8}, adc:{gain:5, v:-2}, adc:{gain:6, v:13}, adc:{gain:7, v:41}
   adc:{gain:0, v:-12}, adc:{gain:1, v:-11}, adc:{gain:2, v:-11}, adc:{gain:3, v:-11}, adc:{gain:4, v:-8}, adc:{gain:5, v:-2}, adc:{gain:6, v:13}, adc:{gain:7, v:41}



This makes it clear the the calibration jumps between 2 values (some runs it is even 4 different values values)

- Why does this happen?
- what can we do to fix this?

edit: formatting to improve readability

Parents Reply Children
No Data
Related