ADC measurements varying between boards of same type nRF5340

Hi,

Currently, we are facing a huge problem with our custom boards. Between different boards the ADC values varying very much even with a given voltage input.

For instance, we measure the battery voltage of 8V (shown on the AC/DC source and 8.011V measured with a multimeter). One board measures 7.6V and another of the same type measures 8.2V.

First, we thought that it is a problem of the tolerances of the resistors but they are in 1% tolerance range.

Here are the images of the schematic we are using (JP2 is closed): 

MEAS_VBAT_+7V4 is connected to P0.04/AIN0.

Here is the code I'm using. Before this is called the following line is called in the main. The first results can be neglected but the problem exists regardless how often I get the ADC values.

NRF_SAADC_S->TASKS_CALIBRATEOFFSET = 1;

#define ADC_BATTERY_DEVICE_NAME "ADC_0"
#define ADC_BATTERY_RESOLUTION 12
#define ADC_BATTERY_GAIN ADC_GAIN_1_6
#define ADC_BATTERY_REFERENCE ADC_REF_INTERNAL
#define ADC_BATTERY_ACQUISITION_TIME ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10)

#define ADC_BATTERY_HIGH_CHANNEL_ID 0
#define ADC_BATTERY_HIGH_CHANNEL_INPUT NRF_SAADC_INPUT_AIN0
#define ADC_BATTERY_LOW_CHANNEL_ID 2
#define ADC_BATTERY_LOW_CHANNEL_INPUT NRF_SAADC_INPUT_AIN2


static const struct device *adc_dev;

static const struct adc_channel_cfg m_battery_high_channel_cfg = {
	.gain = ADC_BATTERY_GAIN,
	.reference = ADC_BATTERY_REFERENCE,
	.acquisition_time = ADC_BATTERY_ACQUISITION_TIME,
	.channel_id = ADC_BATTERY_HIGH_CHANNEL_ID,
#if defined(CONFIG_ADC_CONFIGURABLE_INPUTS)
	.input_positive = ADC_BATTERY_HIGH_CHANNEL_INPUT,
#endif
};

static const struct adc_sequence_options sequence_opts = {
	.interval_us = 0,
	.callback = NULL,
	.user_data = NULL,
	.extra_samplings = 7,
};

#define BUFFER_SIZE_BATTERY 8
static int16_t m_sample_buffer[BUFFER_SIZE_BATTERY];


float battery_voltage_get_high() {
    const struct adc_sequence sequence = {
        .options = &sequence_opts,
        .channels = BIT(ADC_BATTERY_HIGH_CHANNEL_ID),
        .buffer = &m_sample_buffer,
        .buffer_size = sizeof(m_sample_buffer),
        .resolution = ADC_BATTERY_RESOLUTION,
    };

	int mV = 0;
	adc_helper_sample(adc_dev, &sequence, &mV, ADC_BATTERY_GAIN);

	return adc_helper_compute_voltage_divider_get_voltage(((float)mV)/1000.0f, 10.0f, 4.12f);
}


int battery_voltage_initialize() {
    int err;

    adc_dev = device_get_binding(ADC_BATTERY_DEVICE_NAME);
    if (!adc_dev) {
        LOG_ERR("device_get_binding %s failed", ADC_BATTERY_DEVICE_NAME);
        return -1;
    }

	err = adc_channel_setup(adc_dev, &m_battery_high_channel_cfg);
	if (err) {
		LOG_ERR("Error in adc setup for channel %d: %d", m_battery_high_channel_cfg.channel_id, err);
        return err;
	}

    LOG_INF("Battery voltage channels initialized");

    return 0;
}

Here are the helper methods.

float adc_helper_compute_voltage_divider_get_voltage(float Vpin, float R1, float R2) {
    float V = (R1 + R2)*Vpin / R2;
    return V;
}

float adc_helper_compute_voltage_divider_get_resistance2(float Vpin, float Vin, float R1) {
    float R2 = Vpin*R1 / (Vin - Vpin);
    return R2;
}


int adc_helper_sample(const struct device *adc_dev, const struct adc_sequence *sequence, int32_t *mV, enum adc_gain gain) {
	int ret;

	if (!adc_dev || sequence == NULL) {
		return -1;
	}
    
	ret = adc_read(adc_dev, sequence);
    if (ret != 0) {
	    LOG_ERR("ADC read err: %d", ret);
        return -1;
    }

    int16_t buffer_val = average_int16(sequence->buffer, sequence->buffer_size / sizeof(int16_t));
    int16_t *buffer_int16 = (int16_t *)sequence->buffer;
    buffer_int16[0] = buffer_val;

    LOG_HEXDUMP_DBG(sequence->buffer, sequence->buffer_size, "Sequence buffer");

    int32_t adc_vref = adc_ref_internal(adc_dev);
    if (adc_vref > 0) {
        int32_t mv_value = buffer_val;
        adc_raw_to_millivolts(adc_vref, gain, sequence->resolution, &mv_value);
        LOG_INF("ADC value: %d, mV: %d (Ref: %d, resolution: %d)", buffer_val, mv_value, adc_vref, sequence->resolution);

        if (mV != NULL) {
            *mV = mv_value;
        }
    }

	return ret;
}

Hopefully, one of you can find a bug in my code that explains the problems. I don't think it is related to the resistors but maybe there may be another hardware problem I don't see.

Thank you very much!

Related