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

SAADC calibration on nRF52840

Hi,

I am using the SAADC (AIN2) to get temperature values from a Pt1000, and I am experiencing sometimes strange behaviour after the offset calibration is done.I have quite  simple requirements (only one analog value, repeat sampling every 2 ms until I have 50 samples, than stop). Wait for some time, than repeat. This is my initialization:

nrfx_saadc_config_t nrfConfig;
nrf_saadc_channel_config_t nrfChannelConfig;

nrfConfig.resolution = NRF_SAADC_RESOLUTION_12BIT;
nrfConfig.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;
nrfConfig.interrupt_priority = APP_IRQ_PRIORITY_LOW;
nrfConfig.low_power_mode = false;
err = nrfx_saadc_init(&nrfConfig, &pt1000EventHandler);

if (err != NRFX_SUCCESS) { ... }

 nrfChannelConfig.resistor_p = NRF_SAADC_RESISTOR_DISABLED;
 nrfChannelConfig.resistor_n = NRF_SAADC_RESISTOR_DISABLED;
 nrfChannelConfig.gain = NRF_SAADC_GAIN1_5;
 nrfChannelConfig.reference = NRF_SAADC_REFERENCE_INTERNAL;
 nrfChannelConfig.acq_time = NRF_SAADC_ACQTIME_40US;
 nrfChannelConfig.mode = NRF_SAADC_MODE_SINGLE_ENDED;
 nrfChannelConfig.pin_p = NRF_SAADC_INPUT_AIN3;
 nrfChannelConfig.pin_n = NRF_SAADC_INPUT_DISABLED;
 nrfChannelConfig.burst = NRF_SAADC_BURST_DISABLED;
 err = nrfx_saadc_channel_init(0, &nrfChannelConfig);

if (err != NRFX_SUCCESS) { ... }

I start by doing one calibration and getting one measurement if the internal temperature sensor. While normal operation is going on, I repeat the temperature measurement every minute, and if the temperature has changed more then 10°C, I repeat the calibration and store the temperature value for later comparison.

The 10°C threshold is taken from the  recommendation in the PS 1.1, 6.23.8.

To test temperature stability, I connect a 1kOhm resistor to my circuit and start heating up my device. I expected to see some drift, countered by the calibrations occurring when temperature has been changed by more then 10K. What I found was little drift, but changes of up to11 digits after calibration (see excel sheet) .The values from column C are calculated by aggregating 50 values as mentioned above and than converted to °C according to the circuity. Than they are sampled to my PC every 30 seconds. The first calibration is not visible in the graph, because it is done directly after startup, the second calibration pushes the measured values up by 0.6K (about 11 ADC digits), the third calibration pulls the measured values down by 0.4K (about 7 ADC digits) and the third calibration has no visible effect. I should add, that this effect does not occur on every test run. I do not get any error code on calling nrfx_saadc_calibrate_offset(), and I always get the NRFX_SAADC_EVT_CALIBRATEDONE event delivered to the event handler.

To me it looks a bit like the second calibration miscalculated a too large offset, and the third calibration then corrected the error.

Is there any explanation of this behaviour? Do I overlook something simple? I am also missing some more detailed explanation, what this calibration really does. What reference is used internally to determine an offset? I could not find a register where I can observe the calculated offset. Would it help to do the calibration more often (either by time interval or by reducing the temperature threshold from 10 K to maybe 1-2 K)?

Regards

  Dirk

temperatur-2020-04-02-13-58-42.xlsx

Parents
  • I have a very similar issue, but this suggestion is confusing, Should I configure both the negative and postive of the same channel to the same input at the same time ? (VDD)

    I am assuming I would need to configure it to be differential? 

    What reference should I use at this point ? The internal one ? 

    I am not sure this makes sense to me, but I tried it and only got zero for the reading (as I would expect, but not what this post suggests)

    This is the actual config code :

    nrf_saadc_channel_config_t channel_config = {
            .resistor_p = NRF_SAADC_RESISTOR_DISABLED,
            .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
            .gain       = NRF_SAADC_GAIN1_4,
            .reference  = NRF_SAADC_REFERENCE_INTERNAL,
            .acq_time   = NRF_SAADC_ACQTIME_40US,
            .mode       = NRF_SAADC_MODE_DIFFERENTIAL,
            .burst      = NRF_SAADC_BURST_ENABLED, 
            .pin_p      = NRF_SAADC_INPUT_VDD,
            .pin_n      = NRF_SAADC_INPUT_VDD
        };
    I have tried super sampling and a filtering but the temperature effects can't really be accounted for , and every time the calibration runs, the values jump to a new baseline, so that ruins long term calculations. 
    I would appreciate some help in this regard.
    Thanks !
  • Calibration always results in a discontinuous jump in the baseline, and although calibration is worth doing initially is not very effective following the initial calibration. Far better to use a true differential measurement, even if one end of the measurement is effectively GND, and simply reverse the inputs for successive readings and take the average. Such a reversal is trivial and is performed in software, not hardware; 2 analogue inputs are required, even if one end is at GND; a single input will not allow residual offset correction, I posted a schematic (circuit diagram) and low-level (bare-metal) code showing how to do this for an RTD resistance measurement, getting near 12-bits ENOB which is pretty amazing for a general-purpose MPU  internal ADC. Temperature tracking is automatic and smooth provided SAADC, reference resistor and sense resistor are all in close proximity..

    Measured 14-bit value of 905.0 Ohm 2-Wire test resistor using 128 samples is 904.9 Ohms with -0.01% error

    // Internal 0.6v Ref 14-bit S0S1 2-Wire RTD actual value 905.0 Ohm (reference 2001.4 Ohm) - Pos-Neg ADC
    // VDD mV Iref uA  RdsOn  V1 mV V2 mV V3 mV    Vrtd    Vref   Rrtd  Error
    // ====== ======= ======  ===== ===== =====  ======  ====== ====== ======
    //   2974    1008     41   2932   911    -3  3108.5  6883.3  903.8 -0.13%
    //   2975    1007     41   2933   912    -2  3108.3  6882.6  903.8 -0.13%
    //   2974    1007     37   2936   913    -2  3107.9  6882.4  903.8 -0.14%
    //   2976    1007     42   2933   914    -2  3108.5  6882.0  904.0 -0.11%
    //   2973    1007     38   2934   911    -2  3108.3  6881.3  904.0 -0.11%
    //   2977    1007     42   2934   911    -1  3110.3  6882.9  904.4 -0.07%
    //   2978    1008     40   2937   913    -1  3110.1  6884.8  904.1 -0.10%
    //   2977    1008     40   2936   913    -1  3109.8  6885.3  903.9 -0.12%
    //
    // Internal 0.6v Ref 14-bit S0S1 2-Wire RTD actual value 905.0 Ohm (reference 2001.4 Ohm) - Neg-Pos ADC
    // VDD mV Iref uA  RdsOn  V1 mV V2 mV V3 mV    Vrtd    Vref   Rrtd  Error
    // ====== ======= ======  ===== ===== =====  ======  ====== ====== ======
    //   2977    1010     41   2935   912    -3  3120.1  6896.4  905.5  0.05%
    //   2976    1009     40   2935   912    -2  3119.5  6893.8  905.7  0.07%
    //   2973    1009     39   2933   912    -2  3119.4  6892.3  905.8  0.09%
    //   2976    1010     40   2935   911     0  3121.9  6896.3  906.0  0.11%
    //   2975    1010     39   2935   913    -2  3120.9  6896.3  905.7  0.08%
    //   2975    1009     40   2934   911    -1  3120.0  6891.4  906.1  0.12%
    //   2976    1009     41   2934   912     0  3118.6  6892.3  905.6  0.07%
    //   2975    1009     38   2936   913    -1  3118.3  6891.4  905.6  0.07%
    //
    // Measured 14-bit value of 905.0 Ohm 2-Wire test resistor using 128 samples is 904.9 Ohms with -0.01% error
    

    seeking-more-information-on-nrf52833-adc-reference

  • Thanks for the quick reply.

    The hardware in this case is already in the field in large numbers and we want to tune things to get extra quality out of the current setup, so I can't add resistors or tie pins externally to GND, etc.

    I do have unused analog inputs so we can dedicate one to a calibration but can we tie it internally to gnd with firmware ? is there another idea you can suggest? 

    The input is a 0-5v sensor over a relatively long cable that is filtered and conditioned down to 3v on board and then fed to the NRF52840.

    Thanks !

Reply
  • Thanks for the quick reply.

    The hardware in this case is already in the field in large numbers and we want to tune things to get extra quality out of the current setup, so I can't add resistors or tie pins externally to GND, etc.

    I do have unused analog inputs so we can dedicate one to a calibration but can we tie it internally to gnd with firmware ? is there another idea you can suggest? 

    The input is a 0-5v sensor over a relatively long cable that is filtered and conditioned down to 3v on board and then fed to the NRF52840.

    Thanks !

Children
  • Pity, but yes you can still improve the readings. A lot depends on the board layout, and whether the unused AIN pins are attached to anything. I would start by taking 2 adjacent spare analogue inputs and enabling both high and low 160k internal bias resistors then take a differential reading with the two inputs in 1-pin_p 2-pin_n format then reverse and take a reading in 1-pin_n and 2-pin_p. The difference gives the 2 x the residual SAADC offset. The gain settings and reference need to be the same as used for the real data sampling. Take lots of readings, or use OVERSAMPLE.

    Perhaps try repeating with internal both VDD/4 and internal both GND to see how they compare, again reversing p and n to get balanced values. I would expect the best results from using the mid rail bias however.

    If there is only a single spare input pin this is not an option. Maybe share the front-end schematic and input voltage ranges in case I see another option.

Related