Seeking more information on NRF52833 ADC, reference, and buffers

Hi,

We are developing a product which is reading RTDs and thermistors from the NRF52833's internal ADC. We're working on compensating for the intrinsic errors of the ADC and gain buffers and have some questions not covered in the product specifications.

Do the ADC gain buffers have offsets associated with them? The datasheet says the ADC has an intrinsic offset of ±2LSB @ 10bit res but doesn't mention whether there are voltage offsets associated with the buffers.

Is there any procedure to internally determine the ADC buffer gains? For example, connecting the buffer inputs directly to the reference voltage?

Can you provide any more detail on what the ADC's internal calibration routine does or what it accounts for?

Are there any details on how the ADC input buffer gain is implemented?

Are there any specs on channel isolation in sequential measurement mode?

Is there a spec for temperature drift of the VDDH/5 divider?

Thank you!

Parents
  • Sorry, another question:

    In Product Spec v1.7, section 6.21.2.3 (SAADC Scan mode) it says "The time it takes to sample all channels is less than the sum of the conversion time of all enabled channels. The conversion time for a channel is defined as the sum of the acquisition time tACQ and the conversion time tCONV."

    Why is the total conversion time less than the sum of the channels? What is skipped? What is the expected conversion time?

    Thanks

  • Do the ADC gain buffers have offsets associated with them? The datasheet says the ADC has an intrinsic offset of ±2LSB @ 10bit res but doesn't mention whether there are voltage offsets associated with the buffers.

    1) The input buffer of the ADC has auto-zero for offset. The offset of the ADC core should be scaled with the gain. 

    Is there any procedure to internally determine the ADC buffer gains? For example, connecting the buffer inputs directly to the reference voltage?

    2) Only for VDD as reference. There is an undocumented TASKS_CALIBRATEGAIN that connects the input to VDD/4 and uses gain 1/4

    Can you provide any more detail on what the ADC's internal calibration routine does or what it accounts for?

    3) The offset shorts the ADC input, and then a digital feedback loop tunes a current DAC in the SAR comparator to compensate for the offset. The offset may introduce a noise component if the TASKS_CALIBRATEOFFSET is triggered often

    Are there any details on how the ADC input buffer gain is implemented?

    4) Sorry, no details, but it's a switched capacitor gain stage with high common mode rejection. 

    Are there any specs on channel isolation in sequential measurement mode?

    5) There is only one core ADC, the muxing is done through T-gates (transmission gate, pull down, transmission gate), so there should be very little cross coupling between channels. 

    Is there a spec for temperature drift of the VDDH/5 divider?

    6) No, but it's a resistive divider, as such, as long as the ADC input settles, should not change with temperature (although the resistors do change with temperature, the relative size does not). The ADC temperature coefficient will dominate.

    jdub said:
    Also - any details on the accuracy of the internal VDD/4 divider which can be used as an ADC reference? We are also finding that when using the VDD/4 reference, our signals are much noisier. Any specs on that noise or advice for reducing it?

    7)  The noise that you're seeing is probably coming from VDD. There is a low pass filter inside the ADC, but that is in the MHz range. 

    jdub said:
    Is the VDDH/5 input only enabled when REG0 is on? I'm working on a device where VDDH and VDD are tied together. The 1% spec on the VDDH/5 divider is better than the 3% spec on the ADC's input gain buffers, so I have been trying to use the VDDH5 input. But it behaves like it's floating (the reading seems arbitrary and the value decreases with sample rate, implying a loading effect)

    8) That does not matter. VDDH/5 divider goes to the input gain buffers. I'm not sure it's enabled without the 5V regulator is enabled.

    jdub said:
    Does the ADC sequence converter fail if you use the CH[X}.CONFIG register's RESP and RESN settings to set inputs at VDD/2 while setting CH[X}.PSEL and NSEL to Not Connected? I can take a valid measurement in this configuration if I sample as a one-off, but if I make this measurement part of a sequence the ADC never finishes.

    9) You need to enable the PSEL/NSEL, but not connect them to anything. Try writing 0xFE to both.

    jdub said:

    In Product Spec v1.7, section 6.21.2.3 (SAADC Scan mode) it says "The time it takes to sample all channels is less than the sum of the conversion time of all enabled channels. The conversion time for a channel is defined as the sum of the acquisition time tACQ and the conversion time tCONV."

    Why is the total conversion time less than the sum of the channels? What is skipped? What is the expected conversion time?

    10) Not sure why it's written like this as it's very confusing. We expect the conversion time to be N x (TACQ + t_conv).

  • > In 12bit mode the INL is 4.7 LSBs, not bits.

    You're right, that was just a typo on my part, I wasn't making my calculations with 2^4.7 = 26 LSB of noise Laughing

    > If you're using resistive input it's important that you select a TACQ that is long enough compared to the source impedance, see the product specification. 

    Yes our input impedance is <1kOhm which is very low, so we can use a short acquisition time. But we have found that extending it out to the maximum makes no difference on this measurement.

    Thank you!

  • The nRF52833 supports both 4-wire and 3-wire RTD modes on the SAADC, and the performance is better than the discussions above indicate. The trick is to use ratiometric measurements, and assuming no space for large S/H caps this can be done with a single reference resistor - typically mid-range say 2k0 - and a single 100nF capacitor (2 capacitors are better). Performance is within a few Ohms. No SAADC calibration is required due to the ratiometric measurement. 14-bit mode gives the best results, though 12-bit is almost as good. Sampling time of 10uSecs works fine, though if the remote lead length to the RTD is capacitive a longer setup time for the excitation may be required. Current is VDD dependent, but again ratiometric mode removes any issues with that.

    These results are using a noisy USB as power source; even better results if operating on a battery. This test is with current flowing from RTD to Reference and then reverse current through RTD and reference and the results would be averaged. Using both current directions removes long-term DC offset which avoids other issues. Current is off except during measurement.

    Edit: I decided to use two 100nF capacitors; if you have room on the board this improves the readings. I updated my schematic and code to clarify.

    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 909 Ohm (reference 2014 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref       Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========= ======
    // RTD 2980mV,  919 uA,  170 Ohm,  137 Ohm, 2823mV, 1982mV, 1982mV,  126mV,  477 counts, 1054 counts,  911 Ohms  0.22%
    // RTD 2977mV,  919 uA,  179 Ohm,  140 Ohm, 2812mV, 1976mV, 1983mV,  129mV,  476 counts, 1054 counts,  909 Ohms  0.00%
    // RTD 2984mV,  921 uA,  175 Ohm,  136 Ohm, 2822mV, 2006mV, 1980mV,  126mV,  477 counts, 1056 counts,  909 Ohms  0.00%
    // RTD 2966mV,  919 uA,  156 Ohm,  137 Ohm, 2822mV, 1988mV, 1981mV,  126mV,  475 counts, 1053 counts,  908 Ohms -0.11%
    // RTD 2982mV,  920 uA,  168 Ohm,  145 Ohm, 2827mV, 1982mV, 1991mV,  134mV,  477 counts, 1055 counts,  910 Ohms  0.11%
    // RTD 2980mV,  920 uA,  169 Ohm,  145 Ohm, 2824mV, 1981mV, 1982mV,  134mV,  477 counts, 1055 counts,  910 Ohms  0.11%
    // RTD 2975mV,  920 uA,  161 Ohm,  145 Ohm, 2826mV, 1982mV, 1986mV,  134mV,  477 counts, 1055 counts,  910 Ohms  0.11%
    // RTD 2980mV,  920 uA,  169 Ohm,  139 Ohm, 2824mV, 2000mV, 1973mV,  128mV,  476 counts, 1055 counts,  908 Ohms -0.11%
    
    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 909 Ohm (reference 2014 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref       Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========= ======
    // RTD 2976mV,  919 uA,  130 Ohm,  173 Ohm,  120mV,  969mV,  973mV, 2817mV,  475 counts, 1053 counts,  908 Ohms -0.11%
    // RTD 2976mV,  919 uA,  139 Ohm,  175 Ohm,  128mV,  962mV,  967mV, 2815mV,  474 counts, 1053 counts,  906 Ohms -0.33%
    // RTD 2982mV,  917 uA,  138 Ohm,  175 Ohm,  127mV,  965mV,  971mV, 2821mV,  475 counts, 1051 counts,  910 Ohms  0.11%
    // RTD 2975mV,  917 uA,  131 Ohm,  174 Ohm,  121mV,  966mV,  968mV, 2815mV,  475 counts, 1051 counts,  910 Ohms  0.11%
    // RTD 2976mV,  917 uA,  133 Ohm,  167 Ohm,  122mV,  970mV,  967mV, 2822mV,  474 counts, 1051 counts,  908 Ohms -0.11%
    // RTD 2982mV,  918 uA,  143 Ohm,  183 Ohm,  132mV,  976mV,  958mV, 2814mV,  475 counts, 1052 counts,  909 Ohms  0.00%
    // RTD 2971mV,  918 uA,  141 Ohm,  167 Ohm,  130mV,  965mV,  963mV, 2817mV,  475 counts, 1052 counts,  909 Ohms  0.00%
    // RTD 2981mV,  919 uA,  140 Ohm,  168 Ohm,  129mV,  962mV,  966mV, 2826mV,  476 counts, 1053 counts,  910 Ohms  0.11%
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 909 Ohm (reference 2014 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref       Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========= ======
    // RTD 2973mV,  920 uA,  168 Ohm,  133 Ohm, 2818mV, 1987mV, 1983mV,  123mV, 1901 counts, 4217 counts,  907 Ohms -0.22%
    // RTD 2974mV,  919 uA,  173 Ohm,  141 Ohm, 2815mV, 1978mV, 1971mV,  130mV, 1901 counts, 4215 counts,  908 Ohms -0.11%
    // RTD 2968mV,  918 uA,  155 Ohm,  137 Ohm, 2825mV, 1985mV, 1981mV,  126mV, 1900 counts, 4210 counts,  908 Ohms -0.11%
    // RTD 2969mV,  917 uA,  162 Ohm,  145 Ohm, 2820mV, 1978mV, 1976mV,  133mV, 1897 counts, 4207 counts,  908 Ohms -0.11%
    // RTD 2973mV,  919 uA,  157 Ohm,  139 Ohm, 2828mV, 1980mV, 1977mV,  128mV, 1899 counts, 4213 counts,  907 Ohms -0.22%
    // RTD 2973mV,  919 uA,  177 Ohm,  134 Ohm, 2810mV, 1982mV, 1977mV,  124mV, 1903 counts, 4215 counts,  909 Ohms  0.00%
    // RTD 2973mV,  919 uA,  166 Ohm,  138 Ohm, 2820mV, 1975mV, 1971mV,  127mV, 1900 counts, 4213 counts,  908 Ohms -0.11%
    // RTD 2973mV,  919 uA,  159 Ohm,  131 Ohm, 2826mV, 1979mV, 1985mV,  121mV, 1900 counts, 4213 counts,  908 Ohms -0.11%
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 909 Ohm (reference 2014 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref       Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========= ======
    // RTD 2981mV,  920 uA,  120 Ohm,  178 Ohm,  111mV,  962mV,  947mV, 2817mV, 1910 counts, 4220 counts,  911 Ohms  0.22%
    // RTD 2978mV,  920 uA,  129 Ohm,  179 Ohm,  119mV,  979mV,  963mV, 2813mV, 1913 counts, 4219 counts,  913 Ohms  0.44%
    // RTD 2973mV,  920 uA,  131 Ohm,  173 Ohm,  121mV,  959mV,  960mV, 2813mV, 1908 counts, 4218 counts,  911 Ohms  0.22%
    // RTD 2966mV,  920 uA,  144 Ohm,  170 Ohm,  133mV,  965mV,  947mV, 2809mV, 1913 counts, 4219 counts,  913 Ohms  0.44%
    // RTD 2973mV,  920 uA,  130 Ohm,  171 Ohm,  120mV,  958mV,  965mV, 2815mV, 1913 counts, 4218 counts,  913 Ohms  0.44%
    // RTD 2978mV,  920 uA,  132 Ohm,  179 Ohm,  122mV,  962mV,  964mV, 2813mV, 1909 counts, 4218 counts,  911 Ohms  0.22%
    // RTD 2977mV,  920 uA,  127 Ohm,  178 Ohm,  117mV,  955mV,  960mV, 2813mV, 1910 counts, 4220 counts,  911 Ohms  0.22%
    // RTD 2978mV,  920 uA,  140 Ohm,  186 Ohm,  129mV,  964mV,  962mV, 2806mV, 1910 counts, 4220 counts,  911 Ohms  0.22%
    
    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 2994 Ohm (reference 2014 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref       Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========= ======
    // RTD 2969mV,  556 uA,  161 Ohm,  115 Ohm, 2879mV, 1202mV, 1206mV,   64mV,  953 counts,  638 counts, 3008 Ohms  0.47%
    // RTD 2979mV,  558 uA,  202 Ohm,  132 Ohm, 2866mV, 1194mV, 1194mV,   74mV,  953 counts,  640 counts, 2998 Ohms  0.13%
    // RTD 2975mV,  557 uA,  172 Ohm,  132 Ohm, 2879mV, 1198mV, 1209mV,   74mV,  953 counts,  639 counts, 3003 Ohms  0.30%
    // RTD 2974mV,  557 uA,  159 Ohm,  150 Ohm, 2885mV, 1200mV, 1200mV,   84mV,  954 counts,  639 counts, 3006 Ohms  0.40%
    // RTD 2971mV,  556 uA,  161 Ohm,  158 Ohm, 2881mV, 1202mV, 1199mV,   88mV,  952 counts,  638 counts, 3005 Ohms  0.37%
    // RTD 2968mV,  556 uA,  156 Ohm,  138 Ohm, 2881mV, 1197mV, 1202mV,   77mV,  953 counts,  638 counts, 3008 Ohms  0.47%
    // RTD 2977mV,  558 uA,  177 Ohm,  118 Ohm, 2878mV, 1196mV, 1196mV,   66mV,  954 counts,  640 counts, 3002 Ohms  0.27%
    // RTD 2978mV,  556 uA,  176 Ohm,  133 Ohm, 2880mV, 1198mV, 1195mV,   74mV,  952 counts,  638 counts, 3005 Ohms  0.37%
    
    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 2994 Ohm (reference 2014 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref       Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========= ======
    // RTD 2974mV,  559 uA,  132 Ohm,  173 Ohm,   74mV, 1757mV, 1751mV, 2877mV,  954 counts,  641 counts, 2997 Ohms  0.10%
    // RTD 2962mV,  559 uA,  132 Ohm,  148 Ohm,   74mV, 1745mV, 1755mV, 2879mV,  956 counts,  641 counts, 3003 Ohms  0.30%
    // RTD 2973mV,  560 uA,  135 Ohm,  182 Ohm,   76mV, 1745mV, 1754mV, 2871mV,  957 counts,  642 counts, 3002 Ohms  0.27%
    // RTD 2978mV,  560 uA,   83 Ohm,  191 Ohm,   47mV, 1747mV, 1755mV, 2871mV,  957 counts,  642 counts, 3002 Ohms  0.27%
    // RTD 2974mV,  561 uA,  131 Ohm,  167 Ohm,   74mV, 1747mV, 1759mV, 2880mV,  956 counts,  643 counts, 2994 Ohms  0.00%
    // RTD 2973mV,  561 uA,  178 Ohm,  176 Ohm,  100mV, 1757mV, 1752mV, 2874mV,  956 counts,  643 counts, 2994 Ohms  0.00%
    // RTD 2967mV,  559 uA,  118 Ohm,  171 Ohm,   66mV, 1764mV, 1749mV, 2871mV,  956 counts,  641 counts, 3003 Ohms  0.30%
    // RTD 2979mV,  560 uA,  133 Ohm,  175 Ohm,   75mV, 1749mV, 1741mV, 2881mV,  956 counts,  642 counts, 2999 Ohms  0.17%
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 2994 Ohm (reference 2014 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref       Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========= ======
    // RTD 2981mV,  558 uA,  175 Ohm,  132 Ohm, 2883mV, 1197mV, 1196mV,   74mV, 3816 counts, 2560 counts, 3002 Ohms  0.27%
    // RTD 2981mV,  557 uA,  179 Ohm,  131 Ohm, 2881mV, 1198mV, 1200mV,   73mV, 3812 counts, 2556 counts, 3003 Ohms  0.30%
    // RTD 2966mV,  557 uA,  143 Ohm,  134 Ohm, 2886mV, 1205mV, 1207mV,   75mV, 3815 counts, 2557 counts, 3004 Ohms  0.33%
    // RTD 2974mV,  558 uA,  145 Ohm,  134 Ohm, 2893mV, 1209mV, 1205mV,   75mV, 3813 counts, 2561 counts, 2998 Ohms  0.13%
    // RTD 2974mV,  558 uA,  159 Ohm,  123 Ohm, 2885mV, 1213mV, 1204mV,   69mV, 3815 counts, 2559 counts, 3002 Ohms  0.27%
    // RTD 2977mV,  558 uA,  161 Ohm,  132 Ohm, 2887mV, 1172mV, 1206mV,   74mV, 3821 counts, 2561 counts, 3004 Ohms  0.33%
    // RTD 2966mV,  557 uA,  113 Ohm,  123 Ohm, 2903mV, 1198mV, 1198mV,   69mV, 3813 counts, 2557 counts, 3003 Ohms  0.30%
    // RTD 2974mV,  558 uA,  161 Ohm,  125 Ohm, 2884mV, 1204mV, 1206mV,   70mV, 3815 counts, 2560 counts, 3001 Ohms  0.23%
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 2994 Ohm (reference 2014 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref       Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========= ======
    // RTD 2973mV,  560 uA,  133 Ohm,  155 Ohm,   75mV, 1751mV, 1750mV, 2886mV, 3824 counts, 2569 counts, 2997 Ohms  0.10%
    // RTD 2975mV,  561 uA,  124 Ohm,  201 Ohm,   70mV, 1755mV, 1750mV, 2862mV, 3825 counts, 2572 counts, 2995 Ohms  0.03%
    // RTD 2973mV,  561 uA,   99 Ohm,  171 Ohm,   56mV, 1753mV, 1739mV, 2877mV, 3830 counts, 2571 counts, 3000 Ohms  0.20%
    // RTD 2979mV,  561 uA,  128 Ohm,  183 Ohm,   72mV, 1749mV, 1758mV, 2876mV, 3828 counts, 2572 counts, 2997 Ohms  0.10%
    // RTD 2977mV,  561 uA,  135 Ohm,  176 Ohm,   76mV, 1754mV, 1758mV, 2878mV, 3826 counts, 2571 counts, 2997 Ohms  0.10%
    // RTD 2979mV,  561 uA,  124 Ohm,  185 Ohm,   70mV, 1751mV, 1765mV, 2875mV, 3831 counts, 2575 counts, 2996 Ohms  0.07%
    // RTD 2975mV,  559 uA,  139 Ohm,  164 Ohm,   78mV, 1753mV, 1752mV, 2883mV, 3821 counts, 2565 counts, 3000 Ohms  0.20%
    // RTD 2973mV,  562 uA,  122 Ohm,  160 Ohm,   69mV, 1746mV, 1752mV, 2883mV, 3830 counts, 2576 counts, 2994 Ohms  0.00%
    

    This is a working design to produce these results:

    // RTD Measurement - 4-wire Reversible current option
    //                                         + ----------------------------------------------------------------------+
    //                                         |  nRF52832/nRF52833/nRF52840                                           |
    //                                         |                                                                       |
    //                                         |    VDD               VDD                 VDD                          |
    //                                         |   --#--             --#--               --#--                         |
    //                                         |     |                 |                   |                           |
    //                                         |    _|__               |   Excitation      |   Option                  |
    //                                         |    / \                +-|   On            +-|   13k                   |
    //                                         |   /-+-\                 |<- H0   H1         |<- Pullup                |
    //                                         |     |                 +-|   160R 35R      +-|   (not used)            |
    //                        Excitation  P0.04|     |                 |                   |                           |
    //                       +-----------------O-----#------#----------#-------------------#------------ In            |
    //                       |                 |     |      |          |   Excitation      |   Option                  |
    //                       |                 |   __|__  -----        +-|   Off           +-|   13k                   |
    //                       |                 |    / \   -----          |<- L0   L1         |<- Pulldown              |
    //                       |                 |   /-+-\    |3pF       +-|   160R 35R      +-|   (not used)            |
    //                       |                 |     |      |          |                   |                           |
    //                       |                 |   =====  =====      =====               =====                         |
    //                       |                 |    ===    ===        ===                 ===                          |
    //                       |                 |     =      =          =                   =                           |
    //                       |                 |                                                                       |
    //                       |                 |    VDD                                         VDD                    |
    //                       |                 |   --#--                                       --#--                   |
    //                       |                 |    _|__                    Auto-adjust:         |   160k              |
    //                       |                 |    / \                     Bias inputs at VDD/2 +-| Bias Hi/Lo/Both   |
    //     Red Excitation A  |                 |   /-+-\                     VDD1_2                |<-                 |
    //   +--------/~/--------+                 |     |   PIN_VOLTAGE_1                           +-|                   |
    //   | White Sense A                 P0.28 |     |   AIN4          |----'T'-Mux-P---|        |            P SAADC  |
    //   #--------/~/------------------#-------O-----#------#-------#--+ +----#-------+ +--#-----#------#------#-->    |
    //   |                             |       |     |      |       |  | |    |       | |  |     |      |      |       |
    //   |                             |       |   __|__  -----     |  ----   +-|    ----- |     +-|   +++     |       |
    //   |                             |       |    / \   -----     |   |       |<--+  |   |       |<- | |   -----     |
    //  +++                      100nF |       |   /-+-\    | 3pF   |   |     +-|   |  |   |     +-|   | |   -----     |
    //  | |                          -----     |     |      |       |   +---- |--------#   |     |     +++     | 2.5pF |
    //  | |                          -----     |   =====  =====     |         |     |  |   |     |      |1M0   |       |
    //  +++RTD                         |       |    ===    ===      |       =====   |  |   |   =====  =====  =====     |
    //   | Remote                      |       |     =      =       |        ===    |  |   |    ===    ===    ===      |
    //   |                             |       |                    |         =    Select  |     =      =      =       |
    //   |                             |       |                    |                      |                           |
    //   |                             |       |                    |                      |    VDD                    |
    //   |                             |       |                    |  |----'T'-Mux-N---|  |   --#--                   |
    //   |                             |       |                    +--+ +----#-------+ +----+   |   160k              |
    //   |                             |       |                                           | |   +-| Bias Hi/Lo/Both   |
    //   |                             |       |         PIN_VOLTAGE_2                     | |     |<-                 |
    //   | White Sense B               | P0.29 |         AIN5          |----'T'-Mux-----|  | |   +-|                   |
    //   #--------/~/------------------#-------O-----#------#-------#--+ +----#-------+ +--# |   |            N SAADC  |
    //   |                                     |                    +--+ +----#-------+ +----#---#------#------#-->    |
    //                                         |                                           | |   |      |      |       |
    //   |                                     |         PIN_VOLTAGE_3                     | |   +-|   +++     |       |
    //   |                               P0.31 |         AIN7          |----'T'-Mux-----|  | |     |<- | |   -----     |
    //   +--------/~/--------#---------#-------O-----#------#-------#--+ +----#-------+ +--# |   +-|   | |   -----     |
    //     Red Excitation B  |         |       |                    +--+ +----#-------+ +----#   |     +++     | 2.5pF |
    //                       |         |       |                                           | |   |      |1M0   |       |
    //                       |         |       |         PIN_VOLTAGE_4                     | | =====  =====  =====     |
    //                       |         | P0.30 |         AIN6          |----'T'-Mux-----|  | |  ===    ===    ===      |
    //                       |         |   +---O-----#------#-------#--+ +----#-------+ +--# |   =      =      =       |
    //                       |         |   |   |                    +--+ +----#-------+ +----#                         |
    //                       |   100nF |   |   |                                           | |                         |
    //                       |         |   |   |    VDD                                    | |   P: Single-ended       |
    //                       |         |   |   |     |                                     | |   P&N: Differential     |
    //                       |         |   |   O-----#   PIN_VOLTAGE_0                     | |                         |
    //                Rref  +++        |   |   |     |   AIN_VDD       |----'T'-Mux-----|  | |                         |
    //                2k0   | |      ----- |   |     +-----#--------#--+ +----#-------+ +--+ |                         |
    //                1%    | |      ----- |   |                    +--+ +----#-------+ +----#                         |
    //               (0.1%) +++        |   |   |                                             |                         |
    //                       |         #---+   |    GND                |----'T'-Mux-----|    |                         |
    //                       |         |       O-----#-----------------+ +----#-------+ +----+                         |
    //                       |         |       |     |                                                                 |
    //                       |         |       |   =====                                                               |
    //                       |         |       |    ===                                                                |
    //                       |         |       |     =                                                                 |
    //                       |         |       |                                                                       |
    //                       |         |       |    VDD               VDD                 VDD                          |
    //                       |         |       |   --#--             --#--               --#--                         |
    //                       |         |       |     |                 |                   |                           |
    //                       |         |       |    _|__               |   Excitation      |   Option                  |
    //                       |         |       |    / \                +-|   On            +-|   13k                   |
    //                       |         |       |   /-+-\                 |<- H0   H1         |<- Pullup                |
    //                       |         |       |     |                 +-|   160R 35R      +-|   (not used)            |
    //                       |         |       |     |                 |                   |                           |
    //                       +---------#-------O-----#------#----------#-------------------#------------ In            |
    //                                   P0.03 |     |      |          |   Excitation      |   Option                  |
    //                                         |   __|__  -----        +-|   Off           +-|   13k                   |
    //                                         |    / \   -----          |<- L0   L1         |<- Pulldown              |
    //                                         |   /-+-\    |3pF       +-|   160R 35R      +-|   (not used)            |
    //                                         |     |      |          |                   |                           |
    //                                         |   =====  =====      =====               =====                         |
    //                                         |    ===    ===        ===                 ===                          |
    //                                         |     =      =          =                   =                           |
    //                                         +-----------------------------------------------------------------------+
    // Vrtd = differential voltage V1 and V2 in SAADC counts
    // Vref = differential voltage V3 and V4 in SAADC counts
    // Rrtd = (Vrtd * Rref)/Vref Ohms

    This code sample is designed to work with the schematic above on the nRF52833DK; I haven't bothered to optimise this code as it is already pretty quick and it is a simple proof-of-concept:

    #define SAADC_RESOLUTION_VAL   SAADC_RESOLUTION_VAL_12bit  // 12- or 14-bit in this code
    #define PIN_EXCITATION_A  4 // Red lead A to remote RTD
    #define PIN_EXCITATION_B  3 // Connected via Reference resistor to Red lead B to remote RTD
    //      PIN_VOLTAGE_0   VDD // VDD as source of excitation
    #define PIN_VOLTAGE_1    28 // AIN4 White lead from remote RTD (PIN_EXCITATION_A end)   2027
    #define PIN_VOLTAGE_2    29 // AIN5 White lead from remote RTD (PIN_EXCITATION_B end)     33
    #define PIN_VOLTAGE_3    31 // AIN7 Red lead B to remote RTD and Reference resistor     2967
    #define PIN_VOLTAGE_4    30 // AIN6 Reference resistor and PIN_EXCITATION_B             2017
    
    // Input range = (0.6 V)/(1/6) = 3.6 V. Differential mode has 1 bit less than single-ended due to sign bit
    #if (SAADC_RESOLUTION_VAL == SAADC_RESOLUTION_VAL_12bit)
     #define ADC12_COUNTS_PER_VOLT_SE   1138  // 12-bit Mode Singe-ended:  4096/3.6V
     #define ADC12_COUNTS_PER_VOLT_DIFF  569  // 12-bit Mode Differential: 2048/3.6V
     const uint8_t SAADC_ResolutionStr[] = "12-bit";
    #elif (SAADC_RESOLUTION_VAL == SAADC_RESOLUTION_VAL_14bit)
     #define SAADC_RESOLUTION_VAL SAADC_RESOLUTION_VAL_14bit
     #define ADC12_COUNTS_PER_VOLT_SE   4551  // 14-bit Mode Singe-ended: 16384/3.6V
     #define ADC12_COUNTS_PER_VOLT_DIFF 2276  // 14-bit Mode Differential: 8192/3.6V
     const uint8_t SAADC_ResolutionStr[] = "14-bit";
    #elif
     #error SAADC_RESOLUTION_VAL   not 12- or 14-bit in this code
    #endif
    
    // Do an initial calibration as the offset shows up in comparing +ve and -ve current measurements
    static bool mAmCalibrated = false;
    static float mMeasurementError = 0.0f;
    
    uint16_t GetRTD_Voltage(uint32_t AnalogueChannelP, uint32_t AnalogueChannelN)
    {
       uint16_t result = 9999;
       uint32_t timeout = 10000;
       volatile int16_t buffer[8];
       uint32_t i=0;  // SAADC Channel
       // Check if single-ended or differential SAADC mode
       if (AnalogueChannelN == SAADC_CH_PSELN_PSELN_NC)
       {
          // Configure SAADC singled-ended channel, Internal reference (0.6V) and 1/6 gain, no pull-up or pull-down
          NRF_SAADC->CH[i].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_6    << SAADC_CH_CONFIG_GAIN_Pos)   |
                                    (SAADC_CH_CONFIG_MODE_SE         << SAADC_CH_CONFIG_MODE_Pos)   |
                                    (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos)   |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESP_Pos)   |
                                    (SAADC_CH_CONFIG_TACQ_10us       << SAADC_CH_CONFIG_TACQ_Pos);
       }
       else
       {
          // Configure SAADC differential channel, Internal reference (0.6V) and 1/6 gain, no pull-up or pull-down
          // Could use higher gain as differential values are low, maybe 2V max
          NRF_SAADC->CH[i].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_6    << SAADC_CH_CONFIG_GAIN_Pos)   |
                                    (SAADC_CH_CONFIG_MODE_Diff       << SAADC_CH_CONFIG_MODE_Pos)   |
                                    (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos)   |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESP_Pos)   |
                                    (SAADC_CH_CONFIG_TACQ_10us       << SAADC_CH_CONFIG_TACQ_Pos);
       }
       NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL << SAADC_RESOLUTION_VAL_Pos;
       // Analog positive and negative input channels have strange numbering:
       //  PSELP_NC           0UL  Not connected
       //  PSELP_AnalogInput0 1UL  AIN0
       //  PSELP_AnalogInput1 2UL  AIN1
       //  PSELP_AnalogInput2 3UL  AIN2
       //  PSELP_AnalogInput3 4UL  AIN3
       //  PSELP_AnalogInput4 5UL  AIN4
       //  PSELP_AnalogInput5 6UL  AIN5
       //  PSELP_AnalogInput6 7UL  AIN6
       //  PSELP_AnalogInput7 8UL  AIN7
       //  PSELP_VDD          9UL  VDD
       NRF_SAADC->CH[i].PSELP = AnalogueChannelP << SAADC_CH_PSELP_PSELP_Pos;
       NRF_SAADC->CH[i].PSELN = AnalogueChannelN << SAADC_CH_PSELN_PSELN_Pos;
       // Note the SAMPLE task must be triggered 2^OVERSAMPLE times for each output value
       //NRF_SAADC->OVERSAMPLE = SAADC_OVERSAMPLE_OVERSAMPLE_Over32x;
       // Enable SAADC
       NRF_SAADC->ENABLE = 1;
       NRF_SAADC->RESULT.PTR = (uint32_t)buffer;
       NRF_SAADC->RESULT.MAXCNT = 1;
       //NRF_SAADC->RESULT.AMOUNT
       // Do an initial calibration as the offset shows up in comparing +ve and -ve current measurements
       // Any gain changes require recalibration
       if(!mAmCalibrated)
       {
          NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
          NRF_SAADC->TASKS_CALIBRATEOFFSET = 1;
          while (!NRF_SAADC->EVENTS_CALIBRATEDONE) ;
          mAmCalibrated = true;
       }
       NRF_SAADC->EVENTS_END = 0;
       NRF_SAADC->TASKS_START = 1;
       NRF_SAADC->TASKS_SAMPLE = 1;
       while ((!NRF_SAADC->EVENTS_END) && timeout > 0)
       {
          timeout--;
       }
       NRF_SAADC->TASKS_STOP = 1;
       NRF_SAADC->EVENTS_STARTED = 0;
       NRF_SAADC->EVENTS_END = 0;
       // Disable command
       NRF_SAADC->ENABLE = 0;
       if (timeout != 0)
       {
          // Calculate mVolt value with rounding, assume 16-bit unsigned for prints
          result = (uint16_t)((((int32_t)buffer[0] * 1000L)+(ADC12_COUNTS_PER_VOLT_SE/2)) / ADC12_COUNTS_PER_VOLT_SE);
       }
       if (AnalogueChannelN == SAADC_CH_PSELN_PSELN_NC)
       {
          // Display in mVolts
          return result;
       }
       else
       {
          // Display and calculate in SAADC counts for best resolution
          return abs(buffer[0]);
       }
    }
    
    #define AVERAGING_NUMBER 16
    #define REPORTS_NUMBER    8 //16
    const float Rref_Ohm  = 2014.5f;  // 2014.5 Ohm
    const float Rtest_Ohm =  909.5f;  //  909.5 Ohm 2993.8 Ohm
    float Rrtd = 0.0f;
    float Rrtd_Sum = 0.0f;
    
    
    void TestRTD_ResistanceMeasurement(void)
    {
       char RtdInfoPacket[240] = "?";
       uint16_t BatteryVolts0 = 0, BatteryVolts1 = 0, BatteryVolts2 = 0, BatteryVolts3 = 0, BatteryVolts4 = 0;
       mClocksInit();
       mRtcInit();
       // Take some samples forward current and then some reverse
       for (uint32_t SampleCount=0; SampleCount<REPORTS_NUMBER; SampleCount++)
       {
          uint32_t Vrtd = 0;
          uint32_t Vref = 0;
          uint32_t FET_RdsOnA = 1;
          uint32_t FET_RdsOnB = 1;
          // Configuration               Direction                Input                          Pullup                 Drive Level        Sense Level
          // ==========================  =======================  =============================  =====================  =================  =======================
          nrf_gpio_cfg(PIN_EXCITATION_A, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,   NRF_GPIO_PIN_S0S1, NRF_GPIO_PIN_NOSENSE);
          nrf_gpio_cfg(PIN_EXCITATION_B, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,   NRF_GPIO_PIN_S0S1, NRF_GPIO_PIN_NOSENSE);
          // Test with A high and B low then reverse current with A low and B high, probably average the result
          if (SampleCount < REPORTS_NUMBER/2)
          {
             // Turn on forward current through RTD and Reference
             nrf_gpio_pin_set(PIN_EXCITATION_A);
             nrf_gpio_pin_clear(PIN_EXCITATION_B);
          }
          else
          {
             // Turn on reverse current through RTD and Reference
             nrf_gpio_pin_clear(PIN_EXCITATION_A);
             nrf_gpio_pin_set(PIN_EXCITATION_B);
          }
          if (SampleCount == 0)
          {
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "\r\n// Internal 0.6v Ref %s S0S1 RTD actual value %0.2f Ohm (reference %0.2f Ohm) - Forward current measurement\r\n", SAADC_ResolutionStr, Rtest_Ohm, Rref_Ohm);
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "//        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "//     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
          }
          else if (SampleCount == REPORTS_NUMBER/2)
          {
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "\r\n// Internal 0.6v Ref %s S0S1 RTD actual value %0.2f Ohm (reference %0.2f Ohm) - Reverse current measurement\r\n", SAADC_ResolutionStr, Rtest_Ohm, Rref_Ohm);
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "//        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "//     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
          }
          // Short arbitrary delay to allow current to stabalise after turning on
          for (uint32_t i=0; i<1000UL; i++) {}
          // Get battery voltage first
          BatteryVolts0 = GetRTD_Voltage(SAADC_CH_PSELP_PSELP_VDD, SAADC_CH_PSELN_PSELN_NC);
          // Differential RTD and reference resistor voltage values
          Vrtd = 0;
          Vref = 0;
          for (uint32_t i=0; i<AVERAGING_NUMBER; i++)
          {
             Vrtd += GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput4, SAADC_CH_PSELP_PSELP_AnalogInput5);
             Vref += GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput7, SAADC_CH_PSELP_PSELP_AnalogInput6);
          }
          Vrtd /= AVERAGING_NUMBER;
          Vref /= AVERAGING_NUMBER;
          // Single-ended measurement voltages for testing
          BatteryVolts1 = GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput4, SAADC_CH_PSELN_PSELN_NC);
          BatteryVolts3 = GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput7, SAADC_CH_PSELN_PSELN_NC);
          // Single-ended FET drive voltages
          BatteryVolts2 = GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput5, SAADC_CH_PSELN_PSELN_NC);
          BatteryVolts4 = GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput6, SAADC_CH_PSELN_PSELN_NC);
          // Rrtd = (Vrtd * Rref)/Vref Ohms
          float Rrtd = ((float)Vrtd * Rref_Ohm)/(float)Vref;
          // Accumulate for sum of both forward and reverse measurements
          Rrtd_Sum += Rrtd;
          // Calculate % error
          mMeasurementError = (Rrtd - Rtest_Ohm) * 100.0f/Rtest_Ohm;
          // Calculate mVolt value of Vref using rounding, differential mode
          uint32_t Vref_mV = (((Vref * 1000)+(ADC12_COUNTS_PER_VOLT_DIFF/2)) / ADC12_COUNTS_PER_VOLT_DIFF);
          // Current Iref = Vref/Rref  uA
          uint32_t Iref_uA = (Vref_mV * 1000)/Rref_Ohm;
          // RdsOn = Vref_mV/Iref_uA Ohms
          if (SampleCount < REPORTS_NUMBER/2)
          {
             FET_RdsOnA = ((BatteryVolts0 - BatteryVolts1) * 1000) / Iref_uA;
             FET_RdsOnB = ((BatteryVolts4) * 1000) / Iref_uA;
          }
          else
          {
             FET_RdsOnA = (BatteryVolts1 * 1000) / Iref_uA;
             FET_RdsOnB = ((BatteryVolts0 - BatteryVolts4) * 1000) / Iref_uA;
          }
          //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref       Rrtd  Error
          //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========= ======
          // RTD 2980mV,  919 uA,  170 Ohm,  137 Ohm, 2823mV, 1982mV, 1982mV,  126mV,  477 counts, 1054 counts,  911 Ohms  0.22%
          snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "// RTD %4humV, %4u uA, %4u Ohm, %4u Ohm, %4humV, %4humV, %4humV, %4humV, %4u counts, %4u counts, %4.1f Ohms % 0.2f%%\r\n", BatteryVolts0, Iref_uA, FET_RdsOnA, FET_RdsOnB, BatteryVolts1, BatteryVolts2, BatteryVolts3, BatteryVolts4, Vrtd, Vref, Rrtd, mMeasurementError);
          uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
       }
       // Accumulate for sum of both forward and reverse measurements
       Rrtd_Sum /= (float)REPORTS_NUMBER;
       // Calculate % error
       mMeasurementError = (Rrtd_Sum - Rtest_Ohm) * 100.0f/Rtest_Ohm;
       // Measured 12-bit value of 905.0 Ohm test resistor is 905.2 Ohms with  0.02% error
       // Measured 12-bit value of 2984.6 Ohm test resistor is 2984.3 Ohms with -0.01% error
       snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "\r\n// Measured %s value of %4.1f Ohm test resistor is %4.1f Ohms with % 0.2f%% error\r\n", SAADC_ResolutionStr, Rtest_Ohm, Rrtd_Sum, mMeasurementError);
       uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
       // Hold
       while(true) {}
    }

  • Interestingly I tested with both forward and reverse current and I see a bigger difference than expected. I'll update the previous post with the updated code. The results are very good for this ADC, better than expected.

    Edit: Performing an initial offset calibration removes the differences. I'll update results and code, this has been interesting. Edit 2: Tested reference and 2 test resistors on a 5-digit DMM, updated results

    Measured 14-bit value of 905.0 Ohm test resistor is 904.9 Ohms with -0.01% error

    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 905.0 Ohm (reference 2001.4 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2982mV,  926 uA,  173 Ohm,  136 Ohm, 2821mV, 1989mV, 1977mV,  126mV,  477 counts, 1055 counts, 904.9 Ohms -0.01%
    // RTD 2979mV,  925 uA,  168 Ohm,  139 Ohm, 2823mV, 1984mV, 1984mV,  129mV,  477 counts, 1054 counts, 905.8 Ohms  0.08%
    // RTD 2978mV,  926 uA,  174 Ohm,  150 Ohm, 2816mV, 1989mV, 1989mV,  139mV,  478 counts, 1055 counts, 906.8 Ohms  0.20%
    // RTD 2981mV,  925 uA,  172 Ohm,  144 Ohm, 2821mV, 1989mV, 1989mV,  134mV,  477 counts, 1054 counts, 905.8 Ohms  0.08%
    
    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 905.0 Ohm (reference 2001.4 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2984mV,  922 uA,  135 Ohm,  183 Ohm,  125mV,  965mV,  970mV, 2815mV,  475 counts, 1051 counts, 904.5 Ohms -0.05%
    // RTD 2980mV,  922 uA,  144 Ohm,  180 Ohm,  133mV,  960mV,  959mV, 2814mV,  475 counts, 1051 counts, 904.5 Ohms -0.05%
    // RTD 2981mV,  923 uA,  144 Ohm,  179 Ohm,  133mV,  961mV,  965mV, 2815mV,  475 counts, 1052 counts, 903.7 Ohms -0.15%
    // RTD 2979mV,  921 uA,  145 Ohm,  170 Ohm,  134mV,  970mV,  965mV, 2822mV,  475 counts, 1050 counts, 905.4 Ohms  0.04%
    
    // Measured 12-bit value of 905.0 Ohm test resistor is 905.2 Ohms with  0.02% error
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 905.0 Ohm (reference 2001.4 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2976mV,  923 uA,  169 Ohm,  138 Ohm, 2820mV, 1979mV, 1972mV,  128mV, 1898 counts, 4205 counts, 903.4 Ohms -0.18%
    // RTD 2973mV,  924 uA,  174 Ohm,  136 Ohm, 2812mV, 1975mV, 1977mV,  126mV, 1899 counts, 4211 counts, 902.6 Ohms -0.27%
    // RTD 2968mV,  923 uA,  154 Ohm,  136 Ohm, 2825mV, 1989mV, 1978mV,  126mV, 1897 counts, 4209 counts, 902.0 Ohms -0.33%
    // RTD 2973mV,  923 uA,  167 Ohm,  130 Ohm, 2818mV, 1983mV, 1978mV,  120mV, 1898 counts, 4209 counts, 902.5 Ohms -0.28%
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 905.0 Ohm (reference 2001.4 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2968mV,  924 uA,  130 Ohm,  168 Ohm,  121mV,  954mV,  958mV, 2812mV, 1912 counts, 4213 counts, 908.3 Ohms  0.36%
    // RTD 2973mV,  923 uA,  131 Ohm,  173 Ohm,  121mV,  964mV,  960mV, 2813mV, 1909 counts, 4209 counts, 907.7 Ohms  0.30%
    // RTD 2979mV,  925 uA,  136 Ohm,  175 Ohm,  126mV,  959mV,  963mV, 2817mV, 1908 counts, 4216 counts, 905.8 Ohms  0.08%
    // RTD 2976mV,  924 uA,  132 Ohm,  170 Ohm,  122mV,  953mV,  966mV, 2818mV, 1909 counts, 4214 counts, 906.7 Ohms  0.18%
    
    // Measured 14-bit value of 905.0 Ohm test resistor is 904.9 Ohms with -0.01% error
    

    Measured 14-bit value of 2984.6 Ohm test resistor is 2984.8 Ohms with  0.01% error

    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 2984.6 Ohm (reference 2001.4 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2975mV,  558 uA,  168 Ohm,  136 Ohm, 2881mV, 1213mV, 1196mV,   76mV,  951 counts,  636 counts, 2992.7 Ohms  0.27%
    // RTD 2979mV,  559 uA,  189 Ohm,  130 Ohm, 2873mV, 1201mV, 1191mV,   73mV,  950 counts,  637 counts, 2984.8 Ohms  0.01%
    // RTD 2966mV,  559 uA,  152 Ohm,  123 Ohm, 2881mV, 1212mV, 1195mV,   69mV,  950 counts,  637 counts, 2984.8 Ohms  0.01%
    // RTD 2967mV,  558 uA,  170 Ohm,  121 Ohm, 2872mV, 1223mV, 1192mV,   68mV,  950 counts,  636 counts, 2989.5 Ohms  0.16%
    
    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 2984.6 Ohm (reference 2001.4 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2981mV,  563 uA,  127 Ohm,  190 Ohm,   72mV, 1748mV, 1759mV, 2874mV,  954 counts,  641 counts, 2978.7 Ohms -0.20%
    // RTD 2973mV,  563 uA,  126 Ohm,  152 Ohm,   71mV, 1741mV, 1749mV, 2887mV,  955 counts,  641 counts, 2981.8 Ohms -0.09%
    // RTD 2981mV,  562 uA,  199 Ohm,  170 Ohm,  112mV, 1731mV, 1754mV, 2885mV,  954 counts,  640 counts, 2983.3 Ohms -0.04%
    // RTD 2974mV,  563 uA,  122 Ohm,  168 Ohm,   69mV, 1743mV, 1749mV, 2879mV,  954 counts,  641 counts, 2978.7 Ohms -0.20%
    
    // Measured 12-bit value of 2984.6 Ohm test resistor is 2984.3 Ohms with -0.01% error
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 2984.6 Ohm (reference 2001.4 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2978mV,  558 uA,  166 Ohm,  134 Ohm, 2885mV, 1211mV, 1202mV,   75mV, 3799 counts, 2544 counts, 2988.7 Ohms  0.14%
    // RTD 2980mV,  559 uA,  178 Ohm,  137 Ohm, 2880mV, 1205mV, 1194mV,   77mV, 3811 counts, 2550 counts, 2991.1 Ohms  0.22%
    // RTD 2980mV,  559 uA,  175 Ohm,  139 Ohm, 2882mV, 1209mV, 1190mV,   78mV, 3806 counts, 2550 counts, 2987.2 Ohms  0.09%
    // RTD 2980mV,  560 uA,  196 Ohm,  123 Ohm, 2870mV, 1201mV, 1198mV,   69mV, 3807 counts, 2554 counts, 2983.3 Ohms -0.04%
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 2984.6 Ohm (reference 2001.4 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2977mV,  562 uA,  128 Ohm,  172 Ohm,   72mV, 1750mV, 1765mV, 2880mV, 3817 counts, 2563 counts, 2980.6 Ohms -0.13%
    // RTD 2970mV,  562 uA,  131 Ohm,  158 Ohm,   74mV, 1742mV, 1751mV, 2881mV, 3816 counts, 2560 counts, 2983.3 Ohms -0.04%
    // RTD 2971mV,  562 uA,  165 Ohm,  151 Ohm,   93mV, 1749mV, 1764mV, 2886mV, 3814 counts, 2562 counts, 2979.4 Ohms -0.17%
    // RTD 2978mV,  562 uA,  133 Ohm,  190 Ohm,   75mV, 1741mV, 1741mV, 2871mV, 3819 counts, 2561 counts, 2984.5 Ohms -0.00%
    
    // Measured 14-bit value of 2984.6 Ohm test resistor is 2984.8 Ohms with  0.01% error
    

  • One last post, switching to floating-point to get accurate % error. Even with small averaging numbers looks very good, provided both forward and reverse current measurements are averaged to cater for residual SAADC offset.

    Measured value of 905.0 Ohm test resistor is 905.2 Ohms with 0.02% error in 12-bit mode <- Edit: got better results

    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 905.0 Ohm (reference 2001.4 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2982mV,  926 uA,  173 Ohm,  136 Ohm, 2821mV, 1989mV, 1977mV,  126mV,  477 counts, 1055 counts, 904.9 Ohms -0.01%
    // RTD 2979mV,  925 uA,  168 Ohm,  139 Ohm, 2823mV, 1984mV, 1984mV,  129mV,  477 counts, 1054 counts, 905.8 Ohms  0.08%
    // RTD 2978mV,  926 uA,  174 Ohm,  150 Ohm, 2816mV, 1989mV, 1989mV,  139mV,  478 counts, 1055 counts, 906.8 Ohms  0.20%
    // RTD 2981mV,  925 uA,  172 Ohm,  144 Ohm, 2821mV, 1989mV, 1989mV,  134mV,  477 counts, 1054 counts, 905.8 Ohms  0.08%
    
    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 905.0 Ohm (reference 2001.4 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2984mV,  922 uA,  135 Ohm,  183 Ohm,  125mV,  965mV,  970mV, 2815mV,  475 counts, 1051 counts, 904.5 Ohms -0.05%
    // RTD 2980mV,  922 uA,  144 Ohm,  180 Ohm,  133mV,  960mV,  959mV, 2814mV,  475 counts, 1051 counts, 904.5 Ohms -0.05%
    // RTD 2981mV,  923 uA,  144 Ohm,  179 Ohm,  133mV,  961mV,  965mV, 2815mV,  475 counts, 1052 counts, 903.7 Ohms -0.15%
    // RTD 2979mV,  921 uA,  145 Ohm,  170 Ohm,  134mV,  970mV,  965mV, 2822mV,  475 counts, 1050 counts, 905.4 Ohms  0.04%
    
    // Measured 12-bit value of 905.0 Ohm test resistor is 905.2 Ohms with  0.02% error
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 905.0 Ohm (reference 2001.4 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2976mV,  923 uA,  169 Ohm,  138 Ohm, 2820mV, 1979mV, 1972mV,  128mV, 1898 counts, 4205 counts, 903.4 Ohms -0.18%
    // RTD 2973mV,  924 uA,  174 Ohm,  136 Ohm, 2812mV, 1975mV, 1977mV,  126mV, 1899 counts, 4211 counts, 902.6 Ohms -0.27%
    // RTD 2968mV,  923 uA,  154 Ohm,  136 Ohm, 2825mV, 1989mV, 1978mV,  126mV, 1897 counts, 4209 counts, 902.0 Ohms -0.33%
    // RTD 2973mV,  923 uA,  167 Ohm,  130 Ohm, 2818mV, 1983mV, 1978mV,  120mV, 1898 counts, 4209 counts, 902.5 Ohms -0.28%
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 905.0 Ohm (reference 2001.4 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2968mV,  924 uA,  130 Ohm,  168 Ohm,  121mV,  954mV,  958mV, 2812mV, 1912 counts, 4213 counts, 908.3 Ohms  0.36%
    // RTD 2973mV,  923 uA,  131 Ohm,  173 Ohm,  121mV,  964mV,  960mV, 2813mV, 1909 counts, 4209 counts, 907.7 Ohms  0.30%
    // RTD 2979mV,  925 uA,  136 Ohm,  175 Ohm,  126mV,  959mV,  963mV, 2817mV, 1908 counts, 4216 counts, 905.8 Ohms  0.08%
    // RTD 2976mV,  924 uA,  132 Ohm,  170 Ohm,  122mV,  953mV,  966mV, 2818mV, 1909 counts, 4214 counts, 906.7 Ohms  0.18%
    
    // Measured 14-bit value of 905.0 Ohm test resistor is 904.9 Ohms with -0.01% error
    
    
    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 2984.6 Ohm (reference 2001.4 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2975mV,  558 uA,  168 Ohm,  136 Ohm, 2881mV, 1213mV, 1196mV,   76mV,  951 counts,  636 counts, 2992.7 Ohms  0.27%
    // RTD 2979mV,  559 uA,  189 Ohm,  130 Ohm, 2873mV, 1201mV, 1191mV,   73mV,  950 counts,  637 counts, 2984.8 Ohms  0.01%
    // RTD 2966mV,  559 uA,  152 Ohm,  123 Ohm, 2881mV, 1212mV, 1195mV,   69mV,  950 counts,  637 counts, 2984.8 Ohms  0.01%
    // RTD 2967mV,  558 uA,  170 Ohm,  121 Ohm, 2872mV, 1223mV, 1192mV,   68mV,  950 counts,  636 counts, 2989.5 Ohms  0.16%
    
    // Internal 0.6v Ref 12-bit S0S1 RTD actual value 2984.6 Ohm (reference 2001.4 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2981mV,  563 uA,  127 Ohm,  190 Ohm,   72mV, 1748mV, 1759mV, 2874mV,  954 counts,  641 counts, 2978.7 Ohms -0.20%
    // RTD 2973mV,  563 uA,  126 Ohm,  152 Ohm,   71mV, 1741mV, 1749mV, 2887mV,  955 counts,  641 counts, 2981.8 Ohms -0.09%
    // RTD 2981mV,  562 uA,  199 Ohm,  170 Ohm,  112mV, 1731mV, 1754mV, 2885mV,  954 counts,  640 counts, 2983.3 Ohms -0.04%
    // RTD 2974mV,  563 uA,  122 Ohm,  168 Ohm,   69mV, 1743mV, 1749mV, 2879mV,  954 counts,  641 counts, 2978.7 Ohms -0.20%
    
    // Measured 12-bit value of 2984.6 Ohm test resistor is 2984.3 Ohms with -0.01% error
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 2984.6 Ohm (reference 2001.4 Ohm) - Forward current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2978mV,  558 uA,  166 Ohm,  134 Ohm, 2885mV, 1211mV, 1202mV,   75mV, 3799 counts, 2544 counts, 2988.7 Ohms  0.14%
    // RTD 2980mV,  559 uA,  178 Ohm,  137 Ohm, 2880mV, 1205mV, 1194mV,   77mV, 3811 counts, 2550 counts, 2991.1 Ohms  0.22%
    // RTD 2980mV,  559 uA,  175 Ohm,  139 Ohm, 2882mV, 1209mV, 1190mV,   78mV, 3806 counts, 2550 counts, 2987.2 Ohms  0.09%
    // RTD 2980mV,  560 uA,  196 Ohm,  123 Ohm, 2870mV, 1201mV, 1198mV,   69mV, 3807 counts, 2554 counts, 2983.3 Ohms -0.04%
    
    // Internal 0.6v Ref 14-bit S0S1 RTD actual value 2984.6 Ohm (reference 2001.4 Ohm) - Reverse current measurement
    //        VDD     Iref    RdsOnA    RdsOnB      V1      V2      V3      V4         Vrtd         Vref        Rrtd  Error
    //     ======   ======  ========  ========  ======  ======  ======  ======  ===========  ===========  ========== ======
    // RTD 2977mV,  562 uA,  128 Ohm,  172 Ohm,   72mV, 1750mV, 1765mV, 2880mV, 3817 counts, 2563 counts, 2980.6 Ohms -0.13%
    // RTD 2970mV,  562 uA,  131 Ohm,  158 Ohm,   74mV, 1742mV, 1751mV, 2881mV, 3816 counts, 2560 counts, 2983.3 Ohms -0.04%
    // RTD 2971mV,  562 uA,  165 Ohm,  151 Ohm,   93mV, 1749mV, 1764mV, 2886mV, 3814 counts, 2562 counts, 2979.4 Ohms -0.17%
    // RTD 2978mV,  562 uA,  133 Ohm,  190 Ohm,   75mV, 1741mV, 1741mV, 2871mV, 3819 counts, 2561 counts, 2984.5 Ohms -0.00%
    
    // Measured 14-bit value of 2984.6 Ohm test resistor is 2984.8 Ohms with  0.01% error
    

  • Hi hmolesworth, sorry for the slow reply, I hope you had a good holiday.

    Thank you so much for running these experiments. I'm studying what you did and it aligns almost exactly with what we're doing. We use ratiometric measurements and are presently seeing about a ±2C spread in our final temperature values. This is roughly ±0.8ohm at the RTD. The resistance offset is consistent over time, and it varies from unit to unit. So one unit might be off by +0.4ohms consistently, another by -0.2ohms, another is dead on like your demo, etc.

    Main differences:

    - You are measuring both the reference resistor and RTD in differential mode. Due to the topology of our circuit, the RTD has one leg tied to GND, so it must be measured single-ended. I don't expect this to be significant.

    - You have a 4-wire RTD, our RTD is 2-wire but it's soldered to the same circuit board so the intermediate resistance should be small and constant.

    - We have a multi-microfarad cap in parallel with the RTD for noise suppression (I am presently investigating how much the soaking current could be throwing off our reading)

    - We adjust the PGA gain of the ADC for maximum dynamic range (although we are careful to keep the same PGA setting for the reference and RTD measurement)

    - The problematic measurements I'm seeing are on an extremely small form factor product. So creepage/clearance/leakage currents might be a factor here.

    So while I don't see a smoking gun that would account for the differences we see, I find your approach very reassuring, and it's leading me to believe I might be hunting an issue with the overall product/PCBA assembly, not the ADC intrinsically. Thank you again!

Reply
  • Hi hmolesworth, sorry for the slow reply, I hope you had a good holiday.

    Thank you so much for running these experiments. I'm studying what you did and it aligns almost exactly with what we're doing. We use ratiometric measurements and are presently seeing about a ±2C spread in our final temperature values. This is roughly ±0.8ohm at the RTD. The resistance offset is consistent over time, and it varies from unit to unit. So one unit might be off by +0.4ohms consistently, another by -0.2ohms, another is dead on like your demo, etc.

    Main differences:

    - You are measuring both the reference resistor and RTD in differential mode. Due to the topology of our circuit, the RTD has one leg tied to GND, so it must be measured single-ended. I don't expect this to be significant.

    - You have a 4-wire RTD, our RTD is 2-wire but it's soldered to the same circuit board so the intermediate resistance should be small and constant.

    - We have a multi-microfarad cap in parallel with the RTD for noise suppression (I am presently investigating how much the soaking current could be throwing off our reading)

    - We adjust the PGA gain of the ADC for maximum dynamic range (although we are careful to keep the same PGA setting for the reference and RTD measurement)

    - The problematic measurements I'm seeing are on an extremely small form factor product. So creepage/clearance/leakage currents might be a factor here.

    So while I don't see a smoking gun that would account for the differences we see, I find your approach very reassuring, and it's leading me to believe I might be hunting an issue with the overall product/PCBA assembly, not the ADC intrinsically. Thank you again!

Children
  • Good to hear from you, hopefully you got a good break.

    "The resistance offset is consistent over time, and it varies from unit to unit" is almost certainly a calibration issue, more about this below.

    "the RTD has one leg tied to GND, so it must be measured single-ended. I don't expect this to be significant" This is a huge issue, given that reversing the current flow allowing automatic residual offset compensation is not available. This means that SAADC calibration is essential, and the calibration must be performed at the same gain and sampling values as each measurement. SAADC offset calibration is known to be slightly dodgy, and must be repeated for any gain or temperature change. The best solution is to not tie the RTD to GND, unless the RTD itself is physically manufactured with a GND connection, in itself highly suspicious.

    "it's soldered to the same circuit board so the intermediate resistance should be small and constant" Pretty irrelevant, what matters is any common-mode current (current to/from other devices) using the same copper/vias as the RTD and reference resistor measurement. The SAADC, in addition, does not have a dedicated Gnd which makes things worse.

    "We have a multi-microfarad cap in parallel with the RTD" How many uF and what type? Any capacitor across the RTD requires an identical capacitor (repeat identical) across the reference; it can not be Tant or electrolytic and if ceramic must nave a voltage rating at least double the anticipated working voltage. Anything above 100nF is pointless; balanced capacitance on RTD and reference is much more important. Internal RTD capacitance must also be accounted for.

    "We adjust the PGA gain of the ADC for maximum dynamic range" The best performance (my opinion) is with gain at 1/6; anything higher degrades SAADC performance. Regardless, the SAADC calibration must be performed at the chosen gain as the SAADC offset changes with gain.

    "So creepage/clearance/leakage currents might be a factor" doubtful.

    Assuming the Gnd is fixed in the RTD and is immovable, use 3-wire sensing instead. I'll change my circuit to RTD Gnd and 3-wire and post the results and code later if I get time.

    As an endnote, ratiometric doesn't just mean comparing reference with RTD, but also comparing forward- and reverse-current measurements of reference and RTD since the SAADC (as with all ADCs) offsets are never really eliminated without doing this. Also it is often preferred to use VDD/4 and remove all capacitance across RTD and reference resistors to get true ratiometric results, regardless of reference voltage. In all cases, best to trigger the SAADC to start while the CPU and Radio are both quiet, a case for PPI.

    It would be helpful to see the front-end schematic and SAADC settings. Good luck :-)

  • I tested with 2-wire RTD and as expected the results are much poorer and vary from calibration to calibration as the residual offset is not handled. There are two ways to address that.

    Edit: 3 ways. I got quite good results by simply by alternating reversing the differential pin selection on the SAADC (no hardware change) to compensate for the residual SAADC offset: Updated results

    Measured 14-bit value of 905.0 Ohm 2-Wire test resistor using 128 samples is 904.9 Ohms with -0.01% error and much better stability/repeatability:

    // 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
    // ====== ======= ======  ===== ===== =====  ======  ====== ====== ======
    //   2973    1008     38   2934   911    -3  3110.1  6885.8  904.0 -0.11%
    //   2974    1008     39   2934   911    -3  3109.1  6887.3  903.5 -0.17%
    //   2979    1007     41   2937   916     2  3108.0  6881.3  904.0 -0.12%
    //   2974    1007     41   2932   911    -2  3108.3  6881.4  904.0 -0.11%
    //   2978    1008     39   2938   916     1  3107.9  6884.1  903.5 -0.16%
    //   2975    1007     41   2933   911    -3  3107.6  6882.4  903.7 -0.14%
    //   2974    1008     40   2933   911    -3  3108.0  6883.0  903.7 -0.14%
    //   2976    1008     42   2933   909    -2  3109.5  6883.9  904.0 -0.11%
    //
    // 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
    // ====== ======= ======  ===== ===== =====  ======  ====== ====== ======
    //   2973    1010     39   2933   909    -4  3122.9  6897.1  906.2  0.13%
    //   2973    1010     39   2933   910    -3  3123.3  6897.5  906.3  0.14%
    //   2975    1010     41   2933   912    -4  3121.4  6896.1  905.9  0.10%
    //   2972    1009     39   2932   911    -3  3120.0  6895.0  905.6  0.07%
    //   2975    1010     39   2935   909    -3  3122.3  6897.5  906.0  0.11%
    //   2972    1010     39   2932   910    -3  3122.3  6897.4  906.0  0.11%
    //   2971    1009     35   2935   910    -3  3120.9  6895.6  905.8  0.09%
    //   2975    1010     41   2933   911    -3  3122.3  6896.8  906.1  0.12%
    //
    // Measured 14-bit value of 905.0 Ohm 2-Wire test resistor using 128 samples is 904.9 Ohms with -0.01% error
    

    The schematic/Circuit diagram:

    // RTD Measurement - 2- and 3-wire Unidirectional current
    //                          + ----------------------------------------------------------------------+
    //                          |  nRF52832/nRF52833/nRF52840                                           |
    //                          |                                                                       |
    //                          |    VDD               VDD                 VDD                          |
    //                          |   --#--             --#--               --#--                         |
    //                          |     |                 |                   |                           |
    //                          |    _|__               |   Excitation      |   Option                  |
    //                          |    / \                +-|   On            +-|   13k                   |
    //                          |   /-+-\                 |<- H0   H1         |<- Pullup                |
    //                          |     |                 +-|   160R 35R      +-|   (not used)            |
    //         Excitation  P0.04|     |                 |                   |                           |
    //         +----------------O-----#------#----------#-------------------#------------ In            |
    //         |                |     |      |          |   Excitation      |   Option                  |
    //         |                |   __|__  -----        +-|   Off           +-|   13k                   |
    //         |                |    / \   -----          |<- L0   L1         |<- Pulldown              |
    //         |                |   /-+-\    |3pF       +-|   160R 35R      +-|   (not used)            |
    //         |                |     |      |          |                   |                           |
    //         |                |   =====  =====      =====               =====                         |
    //         |                |    ===    ===        ===                 ===                          |
    //         |                |     =      =          =                   =                           |
    //         |                |                                                                       |
    //         |                |    VDD                                         VDD                    |
    //         |                |   --#--                                       --#--                   |
    //         |                |    _|__                    Auto-adjust:         |   160k              |
    //         |                |    / \                     Bias inputs at VDD/2 +-| Bias Hi/Lo/Both   |
    //         |                |   /-+-\                     VDD1_2                |<-                 |
    //         |                |     |   PIN_VOLTAGE_1                           +-|                   |
    //         |          P0.28 |     |   AIN4          |----'T'-Mux-P---|        |            P SAADC  |
    //         #--------#-------O-----#------#-------#--+ +----#-------+ +--#-----#------#------#-->    |
    //         |        |       |     |      |       |  | |    |       | |  |     |      |      |       |
    //         |        |       |   __|__  -----     |  ----   +-|    ----- |     +-|   +++     |       |
    //         |        |       |    / \   -----     |   |       |<--+  |   |       |<- | |   -----     |
    //  Rref  +++ 100nF |       |   /-+-\    | 3pF   |   |     +-|   |  |   |     +-|   | |   -----     |
    //  2k0   | |     -----     |     |      |       |   +---- |--------#   |     |     +++     | 2.5pF |
    //  1%    | |     -----     |   =====  =====     |         |     |  |   |     |      |1M0   |       |
    // (0.1%) +++       |       |    ===    ===      |       =====   |  |   |   =====  =====  =====     |
    //         |        |       |     =      =       |        ===    |  |   |    ===    ===    ===      |
    //         |        |       |                    |         =    Select  |     =      =      =       |
    //         |        |       |                    |                      |                           |
    //         |        |       |                    |                      |    VDD                    |
    //         |        |       |                    |  |----'T'-Mux-N---|  |   --#--                   |
    //         |        |       |                    +--+ +----#-------+ +----+   |   160k              |
    //         |        |       |                                           | |   +-| Bias Hi/Lo/Both   |
    //         |        |       |         PIN_VOLTAGE_2                     | |     |<-                 |
    //         |        | P0.29 |         AIN5          |----'T'-Mux-----|  | |   +-|                   |
    //         #--------#-------O-----#------#-------#--+ +----#-------+ +--# |   |            N SAADC  |
    //         |        |       |                    +--+ +----#-------+ +----#---#------#------#-->    |
    //    RTD +++ 100nF |       |                                           | |   |      |      |       |
    //        | |     -----     |                                           | |   +-|   +++     |       |
    //        | |     -----     |          Note V3 can be negative          | |     |<- | |   -----     |
    //        +++       |       |          PIN_VOLTAGE_3                    | |   +-|   | |   -----     |
    //         |        | P0.31 |          AIN7         |----'T'-Mux-----|  | |   |     +++     | 2.5pF |
    //         #--------#-------O-----#------#-------#--+ +----#-------+ +--# |   |      |1M0   |       |
    //         |                |                    +--+ +----#-------+ +----# =====  =====  =====     |
    //         |                |                                           | |  ===    ===    ===      |
    //         |                |                                           | |   =      =      =       |
    //         |                |                                           | |                         |
    //         |        VDD     |    VDD                                    | |   P: Single-ended       |
    //         |       --#--    |     |                                     | |   P&N: Differential     |
    //         |         +------O-----#   PIN_VOLTAGE_0                     | |                         |
    //         |                |     |   AIN_VDD       |----'T'-Mux-----|  | |                         |
    //         |                |     +-----#--------#--+ +----#-------+ +--+ |                         |
    //         |                |                    +--+ +----#-------+ +----#                         |
    //         |                |                                             |                         |
    //         |                |    GND                |----'T'-Mux-----|    |                         |
    //         #----------------O-----#-----------------+ +----#-------+ +----+                         |
    //         |                |     |                                                                 |
    //       =====              |   =====                                                               |
    //        ===               |    ===                                                                |
    //         =                |     =                                                                 |
    //                          +-----------------------------------------------------------------------+
    //
    // Capacitor min value = (3pF+2.5pF)*2^14=80nF
    // Vref = differential voltage V1 and V2 in SAADC counts
    // Vrtd = differential voltage V2 and V3 in SAADC counts
    // Rrtd = (Vrtd * Rref)/Vref Ohms

    The code:

    #define SAADC_RESOLUTION_VAL   SAADC_RESOLUTION_VAL_14bit  // 12- or 14-bit in this code
    #define PIN_EXCITATION_A  4 // Red lead A to remote RTD
    //      PIN_VOLTAGE_0   VDD // VDD as source of excitation
    #define PIN_VOLTAGE_1    28 // AIN4 White lead from remote RTD (PIN_EXCITATION_A end)
    #define PIN_VOLTAGE_2    29 // AIN5 White lead from remote RTD (PIN_EXCITATION_B end)
    #define PIN_VOLTAGE_3    31 // AIN7 Red lead B to remote RTD and Reference resistor
    #define PIN_VOLTAGE_4    30 // AIN6 Reference resistor and PIN_EXCITATION_B
    
    // Input range = (VDD/4V)/(1/6) = 4.5 V with VDD=3V VDD/4 Ref do not use 100nF caps
    // Input range = (0.6 V)/(1/6)  = 3.6 V with 100nF caps regardless of VDD
    // Differential mode has 1 bit less than single-ended due to sign bit
    #if (SAADC_RESOLUTION_VAL == SAADC_RESOLUTION_VAL_12bit)
     #define SAADC_COUNTS_PER_VOLT_SE   1138  // 12-bit Mode Singe-ended:  4096/3.6V
     #define SAADC_COUNTS_PER_VOLT_DIFF  569  // 12-bit Mode Differential: 2048/3.6V
     const uint8_t SAADC_ResolutionStr[] = "12-bit";
    #elif (SAADC_RESOLUTION_VAL == SAADC_RESOLUTION_VAL_14bit)
     #define SAADC_RESOLUTION_VAL SAADC_RESOLUTION_VAL_14bit
     #define SAADC_COUNTS_PER_VOLT_SE   4551  // 14-bit Mode Singe-ended: 16384/3.6V
    // #define SAADC_COUNTS_PER_VOLT_DIFF 2276  // 14-bit Mode Differential: 8192/3.6V
     #define SAADC_COUNTS_PER_VOLT_DIFF 3413  // 14-bit Mode Differential: 8192/2.4V
     const uint8_t SAADC_ResolutionStr[] = "14-bit";
    #else
     #error SAADC_RESOLUTION_VAL   not 12- or 14-bit in this code
    #endif
    
    // Do an initial calibration as the offset shows up in comparing +ve and -ve current measurements
    static bool mAmCalibrated = false;
    static float mMeasurementError = 0.0f;
    
    static void RequestCalibration(void)
    {
       // Request a recalibration on any gain or mode changes
       mAmCalibrated = false;
    }
    
    // The accumulator is controlled in the OVERSAMPLE register. When using oversampling, 2^OVERSAMPLE input
    // samples are averaged before the sample result is transferred to memory. Hence, the SAMPLE task must be
    // triggered 2^OVERSAMPLE times for each output value
    static int16_t GetRTD_Voltage(uint32_t AnalogueChannelP, uint32_t AnalogueChannelN)
    {
       uint16_t result = 9999;
       uint32_t timeout = 512;
       volatile int16_t buffer[8];
       uint32_t i=0;  // SAADC Channel
       // Can use internal ref SAADC_CH_CONFIG_REFSEL_Internal with 100nF caps or VDD ratiometric SAADC_CH_CONFIG_REFSEL_VDD1_4 with no caps
       // Check if single-ended or differential SAADC mode
       if (AnalogueChannelN == SAADC_CH_PSELN_PSELN_NC)
       {
          // Configure SAADC singled-ended channel, Internal reference (0.6V) and 1/6 gain (range 3.6V), no pull-up or pull-down
          NRF_SAADC->CH[i].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_6    << SAADC_CH_CONFIG_GAIN_Pos)   |
                                    (SAADC_CH_CONFIG_MODE_SE         << SAADC_CH_CONFIG_MODE_Pos)   |
                                    (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos)   |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESP_Pos)   |
                                    (SAADC_CH_CONFIG_TACQ_10us       << SAADC_CH_CONFIG_TACQ_Pos);
       }
       else
       {
          // Configure SAADC differential channel, Internal reference (0.6V) and 1/4 gain (range 2.4V), no pull-up or pull-down
          NRF_SAADC->CH[i].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_4    << SAADC_CH_CONFIG_GAIN_Pos)   |
                                    (SAADC_CH_CONFIG_MODE_Diff       << SAADC_CH_CONFIG_MODE_Pos)   |
                                    (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos)   |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESP_Pos)   |
                                    (SAADC_CH_CONFIG_TACQ_10us       << SAADC_CH_CONFIG_TACQ_Pos);
       }
       NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL << SAADC_RESOLUTION_VAL_Pos;
       // Analog positive and negative input channels have strange numbering:
       //  PSELP_NC           0UL  Not connected
       //  PSELP_AnalogInput0 1UL  AIN0
       //  PSELP_AnalogInput1 2UL  AIN1
       //  PSELP_AnalogInput2 3UL  AIN2
       //  PSELP_AnalogInput3 4UL  AIN3
       //  PSELP_AnalogInput4 5UL  AIN4
       //  PSELP_AnalogInput5 6UL  AIN5
       //  PSELP_AnalogInput6 7UL  AIN6
       //  PSELP_AnalogInput7 8UL  AIN7
       //  PSELP_VDD          9UL  VDD
       NRF_SAADC->CH[i].PSELP = AnalogueChannelP << SAADC_CH_PSELP_PSELP_Pos;
       NRF_SAADC->CH[i].PSELN = AnalogueChannelN << SAADC_CH_PSELN_PSELN_Pos;
       // Note the SAMPLE task must be triggered 2^OVERSAMPLE times for each output value
       NRF_SAADC->OVERSAMPLE = SAADC_OVERSAMPLE_OVERSAMPLE_Over32x;
       // Enable SAADC
       NRF_SAADC->ENABLE = 1;
       NRF_SAADC->RESULT.PTR = (uint32_t)buffer;
       NRF_SAADC->RESULT.MAXCNT = 1;
       //NRF_SAADC->RESULT.AMOUNT
       // Do an initial calibration as the offset shows up in comparing +ve and -ve current measurements
       // Any gain changes require recalibration
       if(!mAmCalibrated)
       {
          NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
          NRF_SAADC->TASKS_CALIBRATEOFFSET = 1;
          while (!NRF_SAADC->EVENTS_CALIBRATEDONE) ;
          mAmCalibrated = true;
          // Clear event, though maybe not actually necessary
          NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
       }
       NRF_SAADC->EVENTS_END = 0;
       NRF_SAADC->EVENTS_DONE = 0;
       NRF_SAADC->EVENTS_RESULTDONE = 0;
       NRF_SAADC->TASKS_START = 1;
       // Assume could be up to 256 samples, but don't bother to check actual number
       mSaadcSampleCount = 0;
       while(true)
       {
          NRF_SAADC->TASKS_SAMPLE = 1;
          __DSB();
          while(!NRF_SAADC->EVENTS_DONE) ;
          // Update sample count for reports
          mSaadcSampleCount++;
          if (NRF_SAADC->EVENTS_RESULTDONE || timeout == 0) break;
          NRF_SAADC->EVENTS_DONE = 0;
          timeout--;
       }
       // Don't need NRF_SAADC->EVENTS_END for now
       NRF_SAADC->TASKS_STOP = 1;
       NRF_SAADC->EVENTS_STARTED = 0;
       NRF_SAADC->EVENTS_END = 0;
       NRF_SAADC->EVENTS_DONE = 0;
       NRF_SAADC->EVENTS_RESULTDONE = 0;
       // Disable command
       NRF_SAADC->ENABLE = 0;
       if (timeout != 0)
       {
          // Calculate mVolt value with rounding, assume 16-bit unsigned for prints
          result = (uint16_t)((((int32_t)buffer[0] * 1000L)+(SAADC_COUNTS_PER_VOLT_SE/2)) / SAADC_COUNTS_PER_VOLT_SE);
       }
       if (AnalogueChannelN == SAADC_CH_PSELN_PSELN_NC)
       {
          // Display in mVolts
          return result;
       }
       else
       {
          // Display and calculate in signed SAADC counts for best resolution
          return buffer[0];
       }
    }
    
    #define AVERAGING_NUMBER  4 // Averaging now also done with SAADC oversampling
    #define REPORTS_NUMBER    8 // Numer of printed results for analysis
    const float Rref_Ohm  = 2001.4f;  // Ohm
    const float Rtest_Ohm =  905.0f;  //  905.0 Ohm, 1597.3 Ohm, 2984.6 Ohm.
    float Rrtd = 0.0f;
    float Rrtd_Sum = 0.0f;
    float Vrtd_f = 0.0f;
    float Vref_f = 0.0f;
    
    void Test2WireRTD_ResistanceMeasurement(void)
    {
       char RtdInfoPacket[240] = "?";
       int16_t BatteryVolts0 = 0, BatteryVolts1 = 0, BatteryVolts2 = 0, BatteryVolts3 = 0;
       mClocksInit();
       mRtcInit();
       // Take some samples forward current and then some reverse
       for (uint32_t SampleCount=0; SampleCount<REPORTS_NUMBER; SampleCount++)
       {
          int32_t Vrtd = 0;
          int32_t Vref = 0;
          uint32_t FET_RdsOnA = 1;
          // Configuration               Direction                Input                          Pullup                 Drive Level        Sense Level
          // ==========================  =======================  =============================  =====================  =================  =======================
          nrf_gpio_cfg(PIN_EXCITATION_A, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,   NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
          // Turn on forward current through RTD and Reference
          nrf_gpio_pin_set(PIN_EXCITATION_A);
          if (SampleCount == 0)
          {
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "\r\n// Internal 0.6v Ref %s S0S1 2-Wire RTD actual value %0.1f Ohm (reference %0.1f Ohm) - Pos-Neg ADC\r\n", SAADC_ResolutionStr, Rtest_Ohm, Rref_Ohm);
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "// VDD mV Iref uA  RdsOn  V1 mV V2 mV V3 mV    Vrtd    Vref   Rrtd  Error\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "// ====== ======= ======  ===== ===== =====  ======  ====== ====== ======\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
          }
          else if (SampleCount == REPORTS_NUMBER/2)
          {
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "//\r\n// Internal 0.6v Ref %s S0S1 2-Wire RTD actual value %0.1f Ohm (reference %0.1f Ohm) - Neg-Pos ADC\r\n", SAADC_ResolutionStr, Rtest_Ohm, Rref_Ohm);
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "// VDD mV Iref uA  RdsOn  V1 mV V2 mV V3 mV    Vrtd    Vref   Rrtd  Error\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "// ====== ======= ======  ===== ===== =====  ======  ====== ====== ======\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
          }
          // Short arbitrary delay to allow current to stabalise after turning on
          for (uint32_t i=0; i<10000UL; i++) {}
          // Differential RTD and reference resistor voltage values
          Vrtd = 0;
          Vref = 0;
          // Request a recalibration on any gain or mode changes
          RequestCalibration();
          for (uint32_t i=0; i<AVERAGING_NUMBER; i++)
          {
             // Differential measurements Pos-Neg or Neg-Pos
             if (SampleCount < REPORTS_NUMBER/2)
             {
                Vrtd += GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput5, SAADC_CH_PSELP_PSELP_AnalogInput7);
                Vref += GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput4, SAADC_CH_PSELP_PSELP_AnalogInput5);
             }
             else
             {
                Vrtd += GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput7, SAADC_CH_PSELP_PSELP_AnalogInput5);
                Vref += GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput5, SAADC_CH_PSELP_PSELP_AnalogInput4);
             }
          }
          Vrtd_f = Vrtd /(float)AVERAGING_NUMBER;
          Vref_f = Vref /(float)AVERAGING_NUMBER;
          // Keep using integer value for current
          Vref /= AVERAGING_NUMBER;
          // Differential measurements Pos-Neg or Neg-Pos, compensate for pseudo-negative
          if (SampleCount >= REPORTS_NUMBER/2)
          {
             Vrtd_f = 0.0f - Vrtd_f;
             Vref_f = 0.0f - Vref_f;
             Vref   = 0 - Vref;
          }
          // Request a recalibration on any gain or mode changes
          RequestCalibration();
          // Single-ended measurement voltages for testing
          BatteryVolts0 = GetRTD_Voltage(SAADC_CH_PSELP_PSELP_VDD, SAADC_CH_PSELN_PSELN_NC);
          BatteryVolts1 = GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput4, SAADC_CH_PSELN_PSELN_NC);
          BatteryVolts2 = GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput5, SAADC_CH_PSELN_PSELN_NC);
          BatteryVolts3 = GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput7, SAADC_CH_PSELN_PSELN_NC);
          // Rrtd = (Vrtd_f * Rref)/Vref Ohms
          float Rrtd = (Vrtd_f * Rref_Ohm)/Vref_f;
          // Accumulate for sum of both forward and reverse measurements
          Rrtd_Sum += Rrtd;
          // Calculate % error
          mMeasurementError = (Rrtd - Rtest_Ohm) * 100.0f/Rtest_Ohm;
          // Calculate mVolt value of Vref using rounding, differential mode
          uint32_t Vref_mV = (((Vref * 1000)+(SAADC_COUNTS_PER_VOLT_DIFF/2)) / SAADC_COUNTS_PER_VOLT_DIFF);
          // Current Iref = Vref/Rref  uA
          uint32_t Iref_uA = (uint32_t)roundf((Vref_mV * 1000)/Rref_Ohm);
          // RdsOn = Vref_mV/Iref_uA Ohms
          FET_RdsOnA = ((BatteryVolts0 - BatteryVolts1) * 1000) / Iref_uA;
          snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "//  %5hd   %5u  %5u  %5hd %5hd %5hd  %6.1f  %6.1f %6.1f % 0.2f%%\r\n", BatteryVolts0, Iref_uA, FET_RdsOnA, BatteryVolts1, BatteryVolts2, BatteryVolts3, Vrtd_f, Vref_f, Rrtd, mMeasurementError);
          uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
       }
       // Accumulate for sum of both forward and reverse measurements
       Rrtd_Sum /= (float)REPORTS_NUMBER;
       // Calculate % error
       mMeasurementError = (Rrtd_Sum - Rtest_Ohm) * 100.0f/Rtest_Ohm;
       // Measured 12-bit value of 905.0 Ohm test resistor is 905.2 Ohms with  0.02% error
       // Measured 12-bit value of 2984.6 Ohm test resistor is 2984.3 Ohms with -0.01% error
       snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "//\r\n// Measured %s value of %4.1f Ohm 2-Wire test resistor using %u samples is %4.1f Ohms with % 0.2f%% error\r\n", SAADC_ResolutionStr, Rtest_Ohm, mSaadcSampleCount*AVERAGING_NUMBER, Rrtd_Sum, mMeasurementError);
       uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
       // Hold
       while(true) {}
    }
    

    So the (other) two ways to improve 2-wire results are to either have a separate differential channel to measure another calibration resistor in both forward and reverse current directions which allows calculation of the residual offset, and the best way I'll post separately with a schematic/circuit diagram as this post is getting a bit long. This last method gives the best results available for the SAADC in 4-wire or 2-wire mode (tomorrow maybe).

  • I updated the schematic on the previous post to include both P & N SAADC paths plus pin capacitance to explain the choice of 100nF.

    The final schematic/circuit diagram shows the use of flying capacitors to further isolate external influences and provide the option of using bias to re-center input voltages, which can now be beyond rail, for example at 11 volts. 2- and 3-wire modes require a triple SPDT and 4-wire mode requires a quad SPDT, although I would suggest using a quad SPDT as that converts 2- and 3-wire into 4-wire mode pretty well. If I get around to it I'll post some results.

    // RTD Measurement - 2- and 3-wire Unidirectional current with flying Capacitor Sample/Hold
    //
    //                                +-----------------------------------------------------------------------+
    //                                |  nRF52832/nRF52833/nRF52840                                           |
    //                                |                                                                       |
    //                                |    VDD               VDD                 VDD  Excitation Supply       |
    //                                |   --#--             --#--               --#--                         |
    //                                |     |                 |                   |                           |
    //                                |    _|__               |   Excitation      |   Option                  |
    //                                |    / \                +-|   On            +-|   13k                   |
    //                                |   /-+-\                 |<- H0   H1         |<- Pullup                |
    //                                |     |                 +-|   160R 35R      +-|   (not used)            |
    //         Excitation       P0.04 |     |                 |                   |                           |
    //         +----------------------O-----#------#----------#-------------------#------------ In            |
    //         |                      |     |      |          |   Excitation      |   Option                  |
    //         |                      |   __|__  -----        +-|   Off           +-|   13k                   |
    //         |                      |    / \   -----          |<- L0   L1         |<- Pulldown              |
    //         |                      |   /-+-\    | 3pF      +-|   160R 35R      +-|   (not used)            |
    //         |                      |     |      |          |                   |                           |
    //         |                      |   =====  =====      =====               =====                         |
    //         |                      |    ===    ===        ===                 ===                          |
    //         |                      |     =      =          =                   =                           |
    //         |        S/H     P0.03 |                                                 Flying Capacitor      |
    //         |        +-------------O-----#------#----------#-------------------#---< Sample/Hold           |
    //         |        |             |                                                                       |
    //         |        |             |    VDD                                         VDD                    |
    //         |        |             |   --#--                                       --#--                   |
    //         |        |             |    _|__                    Auto-adjust:         |   160k              |
    //         |        |             |    / \                     Bias inputs at VDD/2 +-| Bias Hi/Lo/Both   |
    //         |        |             |   /-+-\                     VDD1_2                |<-                 |
    //         |        |             |     |   PIN_VOLTAGE_1                           +-|                   |
    //         |        |       P0.28 |     |   AIN4          |----'T'-Mux-P---|        |            P SAADC  |
    //         #------0 | 0-----------O-----#------#-------#--+ +----#-------+ +--#-----#------#------#-->    |
    //         |       \'             |     |      |       |  | |    |       | |  |     |      |      |       |
    //         |        \             |   __|__  -----     |  ----   +-|    ----- |     +-|   +++     |       |
    //         |        0             |    / \   -----     |   |       |<--+  |   |       |<- | |   -----     |
    //  Rref  +++       |             |   /-+-\    | 3pF   |   |     +-|   |  |   |     +-|   | |   -----     |
    //  2k0   | |     -----           |     |      |       |   +---- |--------#   |     |     +++     | 2.5pF |
    //  1%    | |     -----           |   =====  =====     |         |     |  |   |     |      |1M0   |       |
    // (0.1%) +++       | 100nF       |    ===    ===      |       =====   |  |   |   =====  =====  =====     |
    //         |        |             |     =      =       |        ===    |  |   |    ===    ===    ===      |
    //         |        +---+         |                    |         =    Select  |     =      =      =       |
    //         |            |         |                    |                      |                           |
    //         |            |         |                    |                      |    VDD                    |
    //         |            |         |                    |  |----'T'-Mux-N---|  |   --#--                   |
    //         |            |         |                    +--+ +----#-------+ +----+   |   160k              |
    //         |            |         |                                           | |   +-| Bias Hi/Lo/Both   |
    //         |            |         |         PIN_VOLTAGE_2                     | |     |<-                 |
    //         |            |   P0.29 |         AIN5          |----'T'-Mux-----|  | |   +-|                   |
    //         #------0   0-----------O-----#------#-------#--+ +----#-------+ +--# |   |            N SAADC  |
    //         |       \    |         |                    +--+ +----#-------+ +----#---#------#------#-->    |
    //         |        \   |         |                                           | |   |      |      |       |
    //         |        0   |         |                                           | |   +-|   +++     |       |
    //         |        |   |         |                                           | |     |<- | |   -----     |
    //         |        #---+         |                                           | |   +-|   | |   -----     |
    //    RTD +++       |             |                                           | |   |     +++     | 2.5pF |
    //        | |     -----           |                                           | |   |      |1M0   |       |
    //        | |     -----           |                                           | | =====  =====  =====     |
    //        +++       | 100nF       |                                           | |  ===    ===    ===      |
    //         |        0             |         Note V3 can be negative           | |   =      =      =       |
    //         |        /             |         PIN_VOLTAGE_3                     | |   P: Single-ended       |
    //         |       /        P0.31 |         AIN7          |----'T'-Mux-----|  | |   P&N: Differential     |
    //         #------0   0-----------O-----#-----#--------#--+ +----#-------+ +--# |                         |
    //         |                      |                    +--+ +----#-------+ +----#                         |
    //         |                      |                                           | |                         |
    //         |              VDD     |    VDD                                    | |                         |
    //         |             --#--    |     |                                     | |                         |
    //         |               +------O-----#   PIN_VOLTAGE_0                     | |                         |
    //         |                      |     |   AIN_VDD       |----'T'-Mux-----|  | |                         |
    //         |                      |     +-----#--------#--+ +----#-------+ +--+ |                         |
    //         |                      |                    +--+ +----#-------+ +----#                         |
    //         |                      |                                             |                         |
    //         |                      |    GND                |----'T'-Mux-----|    |                         |
    //         #----------------------O-----#-----------------+ +----#-------+ +----+                         |
    //         |                      |     |                                                                 |
    //       =====                    |   =====                                                               |
    //        ===                     |    ===                                                                |
    //         =                      |     =                                                                 |
    //                                +-----------------------------------------------------------------------+
    //
    // Capacitor min value = (3pF+2.5pF)*2^14=80nF
    // Vref = differential voltage V1 and V2 in SAADC counts
    // Vrtd = differential voltage V2 and V3 in SAADC counts
    // Rrtd = (Vrtd * Rref)/Vref Ohms
    //
    // SPDT Switch, select one with single-supply, rail-to-rail inputs (and negative input voltage capability if needed)
    //  https://www.analog.com/media/en/technical-documentation/data-sheets/max4762-max4765.pdf
    //  https://www.analog.com/media/en/technical-documentation/data-sheets/MAX4781-MAX4783.pdf
    //  https://www.analog.com/media/en/technical-documentation/data-sheets/MAX4780-MAX4784.pdf
    //  https://www.analog.com/media/en/technical-documentation/data-sheets/MAX4780-MAX4784.pdf
    //

  • I added the ENOB value for the 2-wire mode, looks pretty good though 4-wire still better; I'll test with the flying capacitors later this week.

    Measured 14-bit value of 905.0 Ohm 2-Wire test resistor using 128 samples is 905.1 Ohms with  0.02% error (12.6 ENOB)

    // 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
    // ====== ======= ======  ===== ===== =====  ======  ====== ====== ======
    //   2978    1008     41   2936   911     0  3110.6  6883.4  904.4 -0.06%
    //   2977    1008     41   2935   913    -2  3109.0  6883.9  903.9 -0.12%
    //   2978    1008     41   2936   911    -4  3110.5  6887.0  903.9 -0.12%
    //   2976    1008     38   2937   911    -3  3112.1  6888.9  904.2 -0.09%
    //   2979    1009     40   2938   913    -1  3112.0  6892.5  903.6 -0.15%
    //   2981    1008     41   2939   918     1  3110.9  6885.8  904.2 -0.09%
    //   2978    1008     42   2935   912    -2  3109.6  6884.8  904.0 -0.11%
    //   2982    1008     40   2941   918     2  3108.1  6883.4  903.7 -0.14%
    //
    // 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
    // ====== ======= ======  ===== ===== =====  ======  ====== ====== ======
    //   2978    1010     41   2936   911    -3  3125.9  6902.3  906.4  0.15%
    //   2975    1010     38   2936   912    -3  3125.0  6900.1  906.4  0.16%
    //   2982    1010     41   2940   918     2  3124.4  6899.5  906.3  0.15%
    //   2975    1009     39   2935   913    -3  3123.5  6895.6  906.6  0.17%
    //   2975    1010     39   2935   914    -2  3126.0  6901.4  906.5  0.17%
    //   2978    1010     41   2936   912    -2  3123.9  6901.5  905.9  0.10%
    //   2978    1010     38   2939   915    -1  3124.1  6901.6  906.0  0.11%
    //   2976    1010     39   2936   913    -3  3124.8  6900.9  906.2  0.14%
    //
    // Measured 14-bit value of 905.0 Ohm 2-Wire test resistor using 128 samples is 905.1 Ohms with  0.02% error (12.6 ENOB)
    

  • Signing off on this; I tested with the flying-capacitor S/H but with long leads the results are no better than the simple 2- or 3-wire presented previously. I left the code in however.

    2- or 3-wire schematic

    // RTD Measurement - 2- and 3-wire Unidirectional current
    //                          + ----------------------------------------------------------------------+
    //                          |  nRF52832/nRF52833/nRF52840                                           |
    //                          |                                                                       |
    //                          |    VDD               VDD                 VDD                          |
    //                          |   --#--             --#--               --#--                         |
    //                          |     |                 |                   |                           |
    //                          |    _|__               |   Excitation      |   Option                  |
    //                          |    / \                +-|   On            +-|   13k                   |
    //                          |   /-+-\                 |<- H0   H1         |<- Pullup                |
    //                          |     |                 +-|   160R 35R      +-|   (not used)            |
    //         Excitation  P0.04|     |                 |                   |                           |
    //         +----------------O-----#------#----------#-------------------#------------ In            |
    //         |                |     |      |          |   Excitation      |   Option                  |
    //         |                |   __|__  -----        +-|   Off           +-|   13k                   |
    //         |                |    / \   -----          |<- L0   L1         |<- Pulldown              |
    //         |                |   /-+-\    |3pF       +-|   160R 35R      +-|   (not used)            |
    //         |                |     |      |          |                   |                           |
    //         |                |   =====  =====      =====               =====                         |
    //         |                |    ===    ===        ===                 ===                          |
    //         |                |     =      =          =                   =                           |
    //         |                |                                                                       |
    //         |                |    VDD                                         VDD                    |
    //         |                |   --#--                                       --#--                   |
    //         |                |    _|__                    Auto-adjust:         |   160k              |
    //         |                |    / \                     Bias inputs at VDD/2 +-| Bias Hi/Lo/Both   |
    //         |                |   /-+-\                     VDD1_2                |<-                 |
    //         |                |     |   PIN_VOLTAGE_1                           +-|                   |
    //         |          P0.28 |     |   AIN4          |----'T'-Mux-P---|        |            P SAADC  |
    //         #--------#-------O-----#------#-------#--+ +----#-------+ +--#-----#------#------#-->    |
    //         |        |       |     |      |       |  | |    |       | |  |     |      |      |       |
    //         |        |       |   __|__  -----     |  ----   +-|    ----- |     +-|   +++     |       |
    //         |        |       |    / \   -----     |   |       |<--+  |   |       |<- | |   -----     |
    //  Rref  +++ 100nF |       |   /-+-\    | 3pF   |   |     +-|   |  |   |     +-|   | |   -----     |
    //  2k0   | |     -----     |     |      |       |   +---- |--------#   |     |     +++     | 2.5pF |
    //  1%    | |     -----     |   =====  =====     |         |     |  |   |     |      |1M0   |       |
    // (0.1%) +++       |       |    ===    ===      |       =====   |  |   |   =====  =====  =====     |
    //         |        |       |     =      =       |        ===    |  |   |    ===    ===    ===      |
    //         |        |       |                    |         =    Select  |     =      =      =       |
    //         |        |       |                    |                      |                           |
    //         |        |       |                    |                      |    VDD                    |
    //         |        |       |                    |  |----'T'-Mux-N---|  |   --#--                   |
    //         |        |       |                    +--+ +----#-------+ +----+   |   160k              |
    //         |        |       |                                           | |   +-| Bias Hi/Lo/Both   |
    //         |        |       |         PIN_VOLTAGE_2                     | |     |<-                 |
    //         |        | P0.29 |         AIN5          |----'T'-Mux-----|  | |   +-|                   |
    //         #--------#-------O-----#------#-------#--+ +----#-------+ +--# |   |            N SAADC  |
    //         |        |       |                    +--+ +----#-------+ +----#---#------#------#-->    |
    //    RTD +++ 100nF |       |                                           | |   |      |      |       |
    //        | |     -----     |                                           | |   +-|   +++     |       |
    //        | |     -----     |          Note V3 can be negative          | |     |<- | |   -----     |
    //        +++       |       |          PIN_VOLTAGE_3                    | |   +-|   | |   -----     |
    //         |        | P0.31 |          AIN7         |----'T'-Mux-----|  | |   |     +++     | 2.5pF |
    //         #--------#-------O-----#------#-------#--+ +----#-------+ +--# |   |      |1M0   |       |
    //         |                |                    +--+ +----#-------+ +----# =====  =====  =====     |
    //         |                |                                           | |  ===    ===    ===      |
    //         |                |                                           | |   =      =      =       |
    //         |        VDD     |    VDD                                    | |                         |
    //         |       --#--    |   --#--                                   | |   P: Single-ended       |
    //         |         |      |     |                                     | |   P&N: Differential     |
    //         |         +------O-----#   PIN_VOLTAGE_0                     | |                         |
    //         |                |     |   AIN_VDD       |----'T'-Mux-----|  | |                         |
    //         |                |     +-----#--------#--+ +----#-------+ +--+ |                         |
    //         |                |                    +--+ +----#-------+ +----#                         |
    //         |                |                                             |                         |
    //         |                |    GND                |----'T'-Mux-----|    |                         |
    //         #----------------O-----#-----------------+ +----#-------+ +----+                         |
    //         |                |     |                                                                 |
    //       =====              |   =====                                                               |
    //        ===               |    ===                                                                |
    //         =                |     =                                                                 |
    //                          +-----------------------------------------------------------------------+
    //
    // Capacitor min value = (3pF+2.5pF)*2^14=80nF
    // Vref = differential voltage V1 and V2 in SAADC counts
    // Vrtd = differential voltage V2 and V3 in SAADC counts
    // Rrtd = (Vrtd * Rref)/Vref Ohms
    

    Measured 14-bit value of 905.0 Ohm 2-Wire test resistor using 128 samples is 905.2 Ohms with 0.02% error (ENOB is 12.2)

    // 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
    // ====== ======= ======  ===== ===== =====  ======  ====== ====== ======
    //   2973    1008     37   2935   910    -3  3112.3  6884.7  904.8 -0.03%
    //   2973    1007     39   2933   911    -4  3110.3  6879.0  904.9 -0.01%
    //   2973    1007     45   2927   910    -3  3108.5  6881.6  904.1 -0.10%
    //   2975    1008     43   2931   911    -4  3108.5  6883.4  903.8 -0.13%
    //   2973    1007     41   2931   913    -2  3110.6  6881.3  904.7 -0.03%
    //   2973    1007     39   2933   908    -4  3108.4  6881.2  904.1 -0.10%
    //   2972    1007     38   2933   911    -2  3107.4  6882.4  903.6 -0.15%
    //   2976    1008     41   2934   911    -3  3110.3  6883.1  904.4 -0.07%
    //
    // 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
    // ====== ======= ======  ===== ===== =====  ======  ====== ====== ======
    //   2972    1008     39   2932   911    -5  3119.1  6888.9  906.2  0.13%
    //   2972    1009     37   2934   913    -3  3120.1  6892.4  906.0  0.11%
    //   2975    1009     39   2935   910    -2  3120.8  6890.1  906.5  0.17%
    //   2971    1009     43   2927   911    -6  3119.6  6890.3  906.1  0.12%
    //   2971    1009     38   2932   912    -3  3119.5  6891.5  906.0  0.11%
    //   2972    1009     38   2933   913    -1  3120.9  6892.0  906.3  0.14%
    //   2975    1009     41   2933   911    -4  3121.0  6894.6  906.0  0.11%
    //   2975    1009     40   2934   914    -6  3118.6  6891.1  905.7  0.08%
    //
    // Measured 14-bit value of 905.0 Ohm 2-Wire test resistor using 128 samples is 905.2 Ohms with 0.02% error (ENOB is 12.2)
    

    The code:

    //#define FEATURE_FLYING_CAPACITOR // Uncomment this line to use eternal S/H with TS3A5018 or similaar
    #define SAADC_RESOLUTION   SAADC_RESOLUTION_VAL_14bit  // 12- or 14-bit in this code
    #define PIN_EXCITATION_A   4  // Red lead A to remote RTD
    #define PIN_EXCITATION_B   3  // Connected via Reference resistor to Red lead B to remote RTD (4-wire)
    //      PIN_VOLTAGE_0    VDD  // VDD as source of excitation
    #define PIN_VOLTAGE_1     28  // AIN4 White lead from remote RTD (PIN_EXCITATION_A end)
    #define PIN_VOLTAGE_2     29  // AIN5 White lead from remote RTD (PIN_EXCITATION_B end)
    #define PIN_VOLTAGE_3     31  // AIN7 Red lead B to remote RTD and Reference resistor
    #define PIN_VOLTAGE_4     30  // AIN6 Reference resistor and PIN_EXCITATION_B                 (4-wire)
    #if defined(FEATURE_FLYING_CAPACITOR)
     #define PIN_SAMPLE_HOLD  25  // Flying capacitor SPDT switch controls
     #define SH_DELAY        10UL // Flying capacitor settle time, may not need
    #endif
    
    // Input range = (VDD/4V)/(1/6) = 4.5 V with VDD=3V VDD/4 Ref do not use 100nF caps
    // Input range = (0.6 V)/(1/6)  = 3.6 V with 100nF caps regardless of VDD
    // Differential mode has 1 bit less than single-ended due to sign bit
    #if (SAADC_RESOLUTION_VAL == SAADC_RESOLUTION_VAL_12bit)
     #define SAADC_COUNTS_PER_VOLT_SE   1138L  // 12-bit Mode Singe-ended:  4096/3.6V
     #define SAADC_COUNTS_PER_VOLT_DIFF  569L  // 12-bit Mode Differential: 2048/3.6V
     const uint8_t SAADC_ResolutionStr[] = "12-bit";
    #elif (SAADC_RESOLUTION == SAADC_RESOLUTION_VAL_14bit)
     #define SAADC_COUNTS_PER_VOLT_SE   4551L  // 14-bit Mode Singe-ended: 16384/3.6V
    // #define SAADC_COUNTS_PER_VOLT_DIFF 2276L  // 14-bit Mode Differential: 8192/3.6V
     #define SAADC_COUNTS_PER_VOLT_DIFF 3413L  // 14-bit Mode Differential: 8192/2.4V
     const uint8_t SAADC_ResolutionStr[] = "14-bit";
    #else
     #error SAADC_RESOLUTION  not 12- or 14-bit in this code
    #endif
    
    // Do an initial calibration as the offset shows up in comparing +ve and -ve current measurements
    static bool mAmCalibrated = false;
    static float mMeasurementError = 0.0f;
    static uint32_t mSaadcSampleCount = 1UL;
    static void RequestCalibration(void)
    {
       // Request a recalibration on any gain or mode changes
       mAmCalibrated = false;
    }
    
    // The accumulator is controlled in the OVERSAMPLE register. When using oversampling, 2^OVERSAMPLE input
    // samples are averaged before the sample result is transferred to memory. Hence, the SAMPLE task must be
    // triggered 2^OVERSAMPLE times for each output value
    //
    // SAADC on nRF54L15
    // =================
    // CH[n].CONFIG.BURST can be enabled to avoid setting SAMPLE task 2^OVERSAMPLE times. With BURST = 1 the
    // ADC will sample the input 2^OVERSAMPLE times as fast as it can (actual timing: <(tACQ+tCONV)*2^OVERSAMPLE).
    // ie similar to one-shot mode
    // Noise shaping is implemented by using the SAR-ADC in a first-order delta-sigma loop and the filtering
    // is done with the use of FIR filters. The sampling rate in these modes is 1MS/s, and only 12 and 14 bit
    // resolution are supported. To enable noise shaping use NOISESHAPE
    // The ADC can use different reference voltages VREF, controlled in the REFSEL field of the CH[n].CONFIG.
    // These are:
    //   Internal reference, VREF = 0.9 V
    //   External reference, VREF provided by the EXTREF pin
    //    Note: The external reference voltage should be close the internal reference voltage, typ 1.2V
    static uint32_t mSAADC_InterruptCount = 0UL;
    void SAADC_IRQHandler(void)
    {
       NRF_SAADC->EVENTS_END = 0;
       __DSB();
       mSAADC_InterruptCount++;
    }
    static int32_t GetRTD_Voltage(uint32_t AnalogueChannelP, uint32_t AnalogueChannelN)
    {
       int32_t result = 9999L;
       uint32_t timeout = 512UL;
       volatile int16_t buffer[8] = {0};
       uint32_t i=0;  // SAADC Channel
    #if defined(FEATURE_FLYING_CAPACITOR)
       // Short arbitrary delay to allow sample 100nF to charge
       //for (uint32_t i=0; i<SH_DELAY; i++) {}
       // Connnect Flying Capacitor SPDT switch to SAADC - NO contacts
       nrf_gpio_pin_set(PIN_SAMPLE_HOLD);
    #endif
       // Can use internal ref SAADC_CH_CONFIG_REFSEL_Internal with 100nF caps or VDD ratiometric SAADC_CH_CONFIG_REFSEL_VDD1_4 with no caps
       // Check if single-ended or differential SAADC mode
       if (AnalogueChannelN == SAADC_CH_PSELN_PSELN_NC)
       {
          // Configure SAADC singled-ended channel, Internal reference (0.6V) and 1/6 gain (range 3.6V), no pull-up or pull-down
          // nRF54L15 has different settings ToDo
          NRF_SAADC->CH[i].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_6    << SAADC_CH_CONFIG_GAIN_Pos)   |
                                    (SAADC_CH_CONFIG_MODE_SE         << SAADC_CH_CONFIG_MODE_Pos)   |
                                    (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos)   |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESP_Pos)   |
                                    (SAADC_CH_CONFIG_TACQ_3us        << SAADC_CH_CONFIG_TACQ_Pos);
       }
       else
       {
          // Configure SAADC differential channel, Internal reference (0.6V) and 1/4 gain (range 2.4V), no pull-up or pull-down
          NRF_SAADC->CH[i].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_4    << SAADC_CH_CONFIG_GAIN_Pos)   |
                                    (SAADC_CH_CONFIG_MODE_Diff       << SAADC_CH_CONFIG_MODE_Pos)   |
                                    (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESN_Pos)   |
                                    (SAADC_CH_CONFIG_RESN_Bypass     << SAADC_CH_CONFIG_RESP_Pos)   |
                                    (SAADC_CH_CONFIG_TACQ_3us        << SAADC_CH_CONFIG_TACQ_Pos);
       }
       NRF_SAADC->RESOLUTION = SAADC_RESOLUTION << SAADC_RESOLUTION_VAL_Pos;
       // Analog positive and negative input channels have strange numbering:
       //  PSELP_NC           0UL  Not connected
       //  PSELP_AnalogInput0 1UL  AIN0
       //  PSELP_AnalogInput1 2UL  AIN1
       //  PSELP_AnalogInput2 3UL  AIN2
       //  PSELP_AnalogInput3 4UL  AIN3
       //  PSELP_AnalogInput4 5UL  AIN4
       //  PSELP_AnalogInput5 6UL  AIN5
       //  PSELP_AnalogInput6 7UL  AIN6
       //  PSELP_AnalogInput7 8UL  AIN7
       //  PSELP_VDD          9UL  VDD
       NRF_SAADC->CH[i].PSELP = AnalogueChannelP << SAADC_CH_PSELP_PSELP_Pos;
       NRF_SAADC->CH[i].PSELN = AnalogueChannelN << SAADC_CH_PSELN_PSELN_Pos;
       // Note the SAMPLE task must be triggered 2^OVERSAMPLE times for each output value
       NRF_SAADC->OVERSAMPLE = SAADC_OVERSAMPLE_OVERSAMPLE_Over8x;
       // Setup interrupts if required
       NRF_SAADC->INTENSET = SAADC_INTENSET_END_Set << SAADC_INTENSET_END_Pos;
       NVIC_SetPriority(SAADC_IRQn, 6);
       NVIC_ClearPendingIRQ(SAADC_IRQn);
       NVIC_EnableIRQ(SAADC_IRQn);
       // Enable SAADC
       NRF_SAADC->ENABLE = 1;
       NRF_SAADC->RESULT.PTR = (uint32_t)buffer;
       NRF_SAADC->RESULT.MAXCNT = 1;
       //NRF_SAADC->RESULT.AMOUNT
       // Do an initial calibration as the offset shows up in comparing +ve and -ve current measurements
       // Any gain changes require recalibration. This reduces but does not eliminate SAADC offset
       if(!mAmCalibrated)
       {
          NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
          NRF_SAADC->TASKS_CALIBRATEOFFSET = 1;
          while (!NRF_SAADC->EVENTS_CALIBRATEDONE) ;
          mAmCalibrated = true;
          // Clear event, though maybe not actually necessary
          NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
       }
       NRF_SAADC->EVENTS_END = 0;
       NRF_SAADC->EVENTS_DONE = 0;
       NRF_SAADC->EVENTS_RESULTDONE = 0;
       NRF_SAADC->TASKS_START = 1;
       // Assume could be up to 256 samples, but don't bother to check actual number
       mSaadcSampleCount = 0;
       while(true)
       {
          NRF_SAADC->TASKS_SAMPLE = 1;
          __DSB();
          while(!NRF_SAADC->EVENTS_DONE) ;
          // Update sample count for reports
          mSaadcSampleCount++;
          if (NRF_SAADC->EVENTS_RESULTDONE || timeout == 0) break;
          NRF_SAADC->EVENTS_DONE = 0;
          timeout--;
       }
       // Don't need NRF_SAADC->EVENTS_END for now
       NRF_SAADC->TASKS_STOP = 1;
       NRF_SAADC->EVENTS_STARTED = 0;
       NRF_SAADC->EVENTS_END = 0;
       NRF_SAADC->EVENTS_DONE = 0;
       NRF_SAADC->EVENTS_RESULTDONE = 0;
       // Disable command
       NRF_SAADC->ENABLE = 0;
    #if defined(FEATURE_FLYING_CAPACITOR)
       // Connnect Flying Capacitor SPDT switch to Rrtd and Rref - NC contacts
       nrf_gpio_pin_clear(PIN_SAMPLE_HOLD);
    #endif
       // Process result
       if (AnalogueChannelN == SAADC_CH_PSELN_PSELN_NC)
       {
          // Display in mVolts, single-ended values are only for info
          if (timeout != 0)
          {
             // Calculate mVolt value with rounding, later assume convert to 16-bit signed for prints
             result = (((int32_t)buffer[0] * 1000L)+(SAADC_COUNTS_PER_VOLT_SE)/2L) / (SAADC_COUNTS_PER_VOLT_SE);
          }
          return result;
       }
       else
       {
          // Display and calculate differential values in signed SAADC counts for best resolution
          return (int32_t)buffer[0];
       }
    }
    
    #define AVERAGING_NUMBER  16 // Averaging now also done with SAADC oversampling
    #define REPORTS_NUMBER    16 // Numer of printed results for analysis
    const float Rref_Ohm  = 2001.4f;  // Ohm
    const float Rtest_Ohm =  905.0f;  //  905.0 Ohm, 1597.3 Ohm, 2984.6 Ohm.
    float Rrtd     = 0.0f;
    float Rrtd_Sum = 0.0f;
    float Vrtd_f = 0.0f;
    float Vref_f = 0.0f;
    
    #include <math.h>
    // x^y = pow(x,y)
    inline int root(int input, int n)
    {
       return round(pow(input, 1./n));
    }
    
    void Test3WireRTD_ResistanceMeasurement(void)
    {
       char RtdInfoPacket[240] = "?";
       int16_t BatteryVolts0 = 0, BatteryVolts1 = 0, BatteryVolts2 = 0, BatteryVolts3 = 0;
       // Take some differential samples normal and then some samples with reversed SAADC connection
       for (uint32_t SampleCount=0; SampleCount<REPORTS_NUMBER; SampleCount++)
       {
          int32_t Vrtd = 0;
          int32_t Vref = 0;
          uint32_t FET_RdsOnA = 1;
          // Configuration               Direction                Input                          Pullup                 Drive Level        Sense Level
          // ==========================  =======================  =============================  =====================  =================  =======================
          nrf_gpio_cfg(PIN_EXCITATION_A, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,   NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
          // Turn on forward current through RTD and Reference
          nrf_gpio_pin_set(PIN_EXCITATION_A);
    #if defined(FEATURE_FLYING_CAPACITOR)
          nrf_gpio_cfg(PIN_SAMPLE_HOLD,  NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,   NRF_GPIO_PIN_S0S1, NRF_GPIO_PIN_NOSENSE);
          // Connnect Flying Capacitor SPDT switch to Rrtd and Rref - NC contacts
          nrf_gpio_pin_clear(PIN_SAMPLE_HOLD);
    #endif
          if (SampleCount == 0)
          {
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "\r\n// Internal 0.6v Ref %s S0S1 2-Wire RTD actual value %0.1f Ohm (reference %0.1f Ohm) - Pos-Neg ADC\r\n", SAADC_ResolutionStr, Rtest_Ohm, Rref_Ohm);
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "// VDD mV Iref uA  RdsOn  V1 mV V2 mV V3 mV    Vrtd    Vref   Rrtd  Error\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "// ====== ======= ======  ===== ===== =====  ======  ====== ====== ======\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
          }
          else if (SampleCount == REPORTS_NUMBER/2)
          {
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "//\r\n// Internal 0.6v Ref %s S0S1 2-Wire RTD actual value %0.1f Ohm (reference %0.1f Ohm) - Neg-Pos ADC\r\n", SAADC_ResolutionStr, Rtest_Ohm, Rref_Ohm);
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "// VDD mV Iref uA  RdsOn  V1 mV V2 mV V3 mV    Vrtd    Vref   Rrtd  Error\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
             snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "// ====== ======= ======  ===== ===== =====  ======  ====== ====== ======\r\n");
             uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
          }
          // Differential RTD and reference resistor voltage values
          Vrtd = 0;
          Vref = 0;
          // Request a recalibration on any gain or mode changes
          RequestCalibration();
          for (uint32_t i=0; i<AVERAGING_NUMBER; i++)
          {
             // Differential measurements Pos-Neg or Neg-Pos
             if (SampleCount < REPORTS_NUMBER/2)
             {
                Vrtd += GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput5, SAADC_CH_PSELN_PSELN_AnalogInput7);
                Vref += GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput4, SAADC_CH_PSELN_PSELN_AnalogInput5);
             }
             else
             {
                Vrtd += GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput7, SAADC_CH_PSELN_PSELN_AnalogInput5);
                Vref += GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput5, SAADC_CH_PSELN_PSELN_AnalogInput4);
             }
          }
          Vrtd_f = Vrtd /(float)AVERAGING_NUMBER;
          Vref_f = Vref /(float)AVERAGING_NUMBER;
          // Keep using integer value for current
          Vref /= AVERAGING_NUMBER;
          // Differential measurements Pos-Neg or Neg-Pos, compensate for pseudo-negative
          if (SampleCount >= REPORTS_NUMBER/2)
          {
             Vrtd_f = 0.0f - Vrtd_f;
             Vref_f = 0.0f - Vref_f;
             Vref   = 0 - Vref;
          }
          // Request a recalibration on any gain or mode changes
          RequestCalibration();
          // Single-ended measurement voltages for testing
          BatteryVolts0 = (int16_t)GetRTD_Voltage(SAADC_CH_PSELP_PSELP_VDD, SAADC_CH_PSELN_PSELN_NC);
          BatteryVolts1 = (int16_t)GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput4, SAADC_CH_PSELN_PSELN_NC);
          BatteryVolts2 = (int16_t)GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput5, SAADC_CH_PSELN_PSELN_NC);
          BatteryVolts3 = (int16_t)GetRTD_Voltage(SAADC_CH_PSELP_PSELP_AnalogInput7, SAADC_CH_PSELN_PSELN_NC);
          // Rrtd = (Vrtd_f * Rref)/Vref Ohms
          float Rrtd = (Vrtd_f * Rref_Ohm)/Vref_f;
          // Accumulate for sum of both forward and reverse measurements
          Rrtd_Sum += Rrtd;
          // Calculate % error
          mMeasurementError = (Rrtd - Rtest_Ohm) * 100.0f/Rtest_Ohm;
          // Calculate mVolt value of Vref using rounding, differential mode
          uint32_t Vref_mV = (((Vref * 1000)+(SAADC_COUNTS_PER_VOLT_DIFF/2)) / SAADC_COUNTS_PER_VOLT_DIFF);
          // Current Iref = Vref/Rref  uA
          uint32_t Iref_uA = (uint32_t)roundf((Vref_mV * 1000)/Rref_Ohm);
          // RdsOn = Vref_mV/Iref_uA Ohms
          FET_RdsOnA = ((BatteryVolts0 - BatteryVolts1) * 1000) / Iref_uA;
          snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "//  %5hd   %5u  %5u  %5hd %5hd %5hd  %6.1f  %6.1f %6.1f % 0.2f%%\r\n", BatteryVolts0, Iref_uA, FET_RdsOnA, BatteryVolts1, BatteryVolts2, BatteryVolts3, Vrtd_f, Vref_f, Rrtd, mMeasurementError);
          uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
       }
       // Accumulate for sum of both forward and reverse measurements
       Rrtd_Sum /= (float)REPORTS_NUMBER;
       // Calculate % error
       mMeasurementError = (Rrtd_Sum - Rtest_Ohm) * 100.0f/Rtest_Ohm;
       // Check ENOB using x^y = pow(x,y)
       float fENOB_Estimate, fIteration = 0.01f, fLimit = 100.0f/mMeasurementError;
       if (fLimit < 0.0F) fLimit = 0.0f - fLimit;
       for (fENOB_Estimate=10.0f; fENOB_Estimate<=14.0f; fENOB_Estimate += fIteration)
       {
          if (pow(2.0f, fENOB_Estimate) > fLimit)
          {
              fENOB_Estimate -= fIteration;
              break;
          }
       }
       // Measured 14-bit value of 905.0 Ohm 2-Wire test resistor using 128 samples is 905.2 Ohms with 0.02% error (ENOB is 12.1)
       snprintf(RtdInfoPacket, sizeof(RtdInfoPacket)-1, "//\r\n// Measured %s value of %4.1f Ohm 2-Wire test resistor using %u samples is %4.1f Ohms with %0.2f%% error (ENOB is %0.1f)\r\n", SAADC_ResolutionStr, Rtest_Ohm, mSaadcSampleCount*AVERAGING_NUMBER, Rrtd_Sum, mMeasurementError, fENOB_Estimate);
       uartSend(RtdInfoPacket, strlen(RtdInfoPacket));
       // Hold in sleep
       // Turn off forward current through RTD and Reference
       nrf_gpio_pin_clear(PIN_EXCITATION_A);
       while(true)
       {
         __WFE(); __NOP(); __NOP(); __NOP(); __NOP();
       }
    }
    

Related