Boron Low ADC Readings

@rickkas7 - should we expect any variance from 4095 when analogRead()ing a pin grabbing 3.3V from itself?

@marekparticle, the ADC reference is the 3.3v rail so measuring 3.3v on an analog pin should result in only a one bit error at most.

2 Likes

@peekay123, understood. But, Iā€™m struck by the fact that this has occurred on more than one Boron. Iā€¦ must be missing something.

@marekparticle, it is incredibly odd. It may be worth looking at the 3.3v rail to make sure it is clean. It might be worth testing with a known-clean external 3.3v source. You may also want to look at the boards for any manufacturing anomalies like excess flux, etc. My understanding is that all things being equal (DeviceOS, app), ā€œolderā€ Borons work fine but now these ā€œnewā€ ones. So software issues can most likely be eliminated.

1 Like

@Rftop - can you PM me device IDs so I can check mfg date?

1 Like

I tried this out on a Boron LTE running 1.4.2. I connected A2 to 3V3 and the values are not 4095.

0000012309 [app] INFO: ADC: 4090
0000019311 [app] INFO: ADC: 4087
0000026312 [app] INFO: ADC: 4092
0000033314 [app] INFO: ADC: 4094
0000040316 [app] INFO: ADC: 4087
0000047318 [app] INFO: ADC: 4091
0000054319 [app] INFO: ADC: 4093
0000061321 [app] INFO: ADC: 4092
0000068323 [app] INFO: ADC: 4094
0000075325 [app] INFO: ADC: 4089
0000082326 [app] INFO: ADC: 4095
0000089327 [app] INFO: ADC: 4087

This was also the case with the other ADC inputs, and also using D2 as an output.

By the way, using a GPIO as a voltage reference is not guaranteed to work with the nRF52, because the output voltage spec says VOH,SD can be from VDD-0.4 to VDD volts. In other words, GPIO HIGH may not be exactly VDD.

However, after forcing the calibration of the nRF52 SAADC, I got much better values. There were a few outliers, but they were generally close to 4095 and less frequent:

0000003070 [app] INFO: calibration complete
0000003072 [app] INFO: A2=4095
0000004074 [app] INFO: A2=4095
0000005076 [app] INFO: A2=4095
0000006077 [app] INFO: A2=4094
0000007079 [app] INFO: A2=4095
0000008080 [app] INFO: A2=4095
0000009082 [app] INFO: A2=4079
0000010084 [app] INFO: A2=4095
0000011085 [app] INFO: A2=4095
0000012087 [app] INFO: A2=4093
0000013088 [app] INFO: A2=4095
0000014090 [app] INFO: A2=4095
0000015091 [app] INFO: A2=4095
0000016093 [app] INFO: A2=4095
0000017095 [app] INFO: A2=4095
0000018097 [app] INFO: A2=4095
0000019099 [app] INFO: A2=4095
0000020101 [app] INFO: A2=4095
0000021102 [app] INFO: A2=4095
0000022104 [app] INFO: A2=4093
0000023106 [app] INFO: A2=4095

Hereā€™s the code:

#include "Particle.h"

#include "nrfx_saadc.h"

SerialLogHandler logHandler;

SYSTEM_MODE(MANUAL);


void adcCalibrate();

void setup() {

	// Wait for a USB serial connection for up to 10 seconds
	waitFor(Serial.isConnected, 10000);

	// Calibrate the ADC at startup. You might want to do this after large temperature
	// changes as well.
	adcCalibrate();
}

void loop() {
	int a2 = analogRead(A2);

	Log.info("A2=%d", a2);
	delay(1000);
}


static void adcCallback(nrfx_saadc_evt_t const *event) {
	// We just poll for done during calibration instead of using this event but the
	// callback still needs to be defined.
}

void adcCalibrate() {
	nrfx_err_t err;

	// IMPORTANT: You must detach the IRQ handler from the system firmware and re-attach it to
	// user firmware, or this won't work right.
	attachInterruptDirect(SAADC_IRQn, nrfx_saadc_irq_handler, false);

	nrfx_saadc_config_t saadcConfig = NRFX_SAADC_DEFAULT_CONFIG;
	saadcConfig.low_power_mode = false;
	saadcConfig.resolution = NRF_SAADC_RESOLUTION_12BIT;
	saadcConfig.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;
	saadcConfig.interrupt_priority = APP_IRQ_PRIORITY_LOW;

	// Initialize SAADC
	err = nrfx_saadc_init(&saadcConfig, adcCallback);
	if (err) {
		Log.error("nrfx_saadc_init err=%lu", err);
		return;
	}

	// Now calibrate it. This does not require a channel be initialized first.
	err = nrfx_saadc_calibrate_offset();
	if (err == NRFX_SUCCESS) {
		while(nrfx_saadc_is_busy()) {
			delay(10);
		}
		Log.info("calibration complete");
	}
	else {
		Log.info("calibrate failed err=%lu", err);
	}
}

We should probably put ADC calibration right into Device OS, but this is probably a reasonable thing to play around with and see if it helps in your case to be sure thatā€™s what the problem is.

4 Likes

@rickkas7 @marekparticle
I tried the code on two different Borons and still am getting mixed results:
Boron #1
0000002139 [app] INFO: A2=4028
0000003140 [app] INFO: A2=4023
0000004141 [app] INFO: A2=4026
0000005143 [app] INFO: A2=4029
0000006144 [app] INFO: A2=4026
0000007146 [app] INFO: A2=4028
0000008147 [app] INFO: A2=4019
0000009148 [app] INFO: A2=4026
0000010149 [app] INFO: A2=4023
0000011150 [app] INFO: A2=4025
0000012152 [app] INFO: A2=4024
0000013153 [app] INFO: A2=4020
0000014155 [app] INFO: A2=4023

Boron #2
0000002058 [app] INFO: A2=4067
0000003059 [app] INFO: A2=4067
0000004060 [app] INFO: A2=4070
0000005061 [app] INFO: A2=4072
0000006062 [app] INFO: A2=4067
0000007064 [app] INFO: A2=4070
0000008065 [app] INFO: A2=4068
0000009066 [app] INFO: A2=4066
0000010067 [app] INFO: A2=4063
0000011069 [app] INFO: A2=4065
0000012070 [app] INFO: A2=4066

Ideas?

Is it printing out the calibration complete message before outputting the samples? Or is there an error code? It looks like it didnā€™t successfully calibrate.

I cut that piece of the output by accident. I just ran it again:
0000009280 [app] INFO: calibration complete
0000009282 [app] INFO: A2=4066
0000010283 [app] INFO: A2=4067
0000011284 [app] INFO: A2=4065
0000012286 [app] INFO: A2=4070
0000013287 [app] INFO: A2=4064
0000014288 [app] INFO: A2=4069
0000015289 [app] INFO: A2=4068
0000016291 [app] INFO: A2=4063
0000017292 [app] INFO: A2=4066

Any ideas?

@Particle9 I ran across this post as I am experiencing similar issues.

I currently have a Boron running 1.5.0-rc.1 and when connecting A0 to Boron 3.3V i am getting ~4068.

Did you ever get a solution to this issue?

I just ran it again with 1.4.4 with A0 fed from Boron 3.3 and still get low numbers. I just ordered another Boron and will try it again when it comes in.
0000010477 [app] INFO: ADC: 4029
0000012478 [app] INFO: ADC: 4027
0000014479 [app] INFO: ADC: 4023
0000016481 [app] INFO: ADC: 4025
0000018482 [app] INFO: ADC: 4033
0000020484 [app] INFO: ADC: 4030
0000022485 [app] INFO: ADC: 4026

@Particle9 thanks for checking it again. Also, based on rickkas7 comment

By the way, using a GPIO as a voltage reference is not guaranteed to work with the nRF52, because the output voltage spec says VOH,SD can be from VDD-0.4 to VDD volts. In other words, GPIO HIGH may not be exactly VDD.

I used one of these adafruit voltage reference breakouts to perform a ADC reading on A0 with the same low reading as before.

@rickkas7 or @marekparticle is there a know cause or potential resolution for this?

the 12bit ADC is a perfect fit for my application, but due to the inaccuracies I am concerned I may need to look into an alternative solutions such as integrating an external ADC.

Thank you.

I just got a brand new Boron in and tested with the same low results. @marekparticle @rickkas7 Any ideas? I have about 4 Borons with a brand new one doing the same thing.

0000108533 [app] INFO: ADC: 4061
0000110534 [app] INFO: ADC: 4061
0000112535 [app] INFO: ADC: 4055
0000114536 [app] INFO: ADC: 4059
0000116537 [app] INFO: ADC: 4059
0000118539 [app] INFO: ADC: 4061
0000120540 [app] INFO: ADC: 4064
0000122541 [app] INFO: ADC: 4059
0000124543 [app] INFO: ADC: 4060
0000126544 [app] INFO: ADC: 4062
0000128546 [app] INFO: ADC: 4061
0000130547 [app] INFO: ADC: 4055
0000132548 [app] INFO: ADC: 4057

Is that with running the calibration code posted above or not?

@rickkas7 I just reran the code with calibration and get simliar results. Could it be a bad batch of Borons?
0000010608 [app] INFO: calibration complete
0000010609 [app] INFO: ADC: 4069
0000012611 [app] INFO: ADC: 4070
0000014612 [app] INFO: ADC: 4067
0000016613 [app] INFO: ADC: 4063
0000018615 [app] INFO: ADC: 4068
0000020616 [app] INFO: ADC: 4072
0000022617 [app] INFO: ADC: 4069
0000024618 [app] INFO: ADC: 4063
0000026620 [app] INFO: ADC: 4067
0000028621 [app] INFO: ADC: 4072

Particle sent me a replacement board and the results below are with the calibration code. Better resultsā€¦
000010400 [app] INFO: calibration complete
0000010401 [app] INFO: ADC: 4083
0000012402 [app] INFO: ADC: 4085
0000014403 [app] INFO: ADC: 4083
0000016404 [app] INFO: ADC: 4089
0000018406 [app] INFO: ADC: 4089
0000020407 [app] INFO: ADC: 4087
0000022408 [app] INFO: ADC: 4085
0000024409 [app] INFO: ADC: 4082
0000026411 [app] INFO: ADC: 4086
0000028412 [app] INFO: ADC: 4088
0000030413 [app] INFO: ADC: 4086
0000032414 [app] INFO: ADC: 4083
0000034415 [app] INFO: ADC: 4088
0000036416 [app] INFO: ADC: 4085
0000038417 [app] INFO: ADC: 4086
0000040418 [app] INFO: ADC: 4083
0000042419 [app] INFO: ADC: 4083
0000044420 [app] INFO: ADC: 4094

FYIā€¦ I performed a test using a 2.048V precision voltage reference. Adafruit product #2200 based on LM4040. Results were:

Raw ADC max delta = 32 or 1.3% error
Filtered ADC max delta = 17 or 0.7% error

Filtering was an exponential filter.


Particle library install: gbjFilterExponential

Despite my best efforts, I was unable to figure out how to bundle dependencies and then include the nrfx_saadc.h library to calibrate the nRF52 SAADC. Too bad, as it looks like it makes a big difference for anyone doing analog input.

Now that you have a voltage reference you can try to calibrate the 3.3V. See this post by @bko.
That would minimize supply inaccuracies. To also compensate for noise, the ADC readings would have to be simultaneous.
The nRF52840 also has an internal 0.6V reference, but I donā€™t know how to enable it. Also, it looks like you would lose some resolution this way. The 0.6V reference gives you a 3.6V range, but the pins are only rated to 3.3V.

If that doesnā€™t help, it might be worth exploring hardware band-aids.
The reference breakout has no bypassing. I would add a pair of 0.1uF and 10uF 25V ceramic caps.
You could try to solder the caps right on the Boron module, provided they have a good ground connection.
How is the reference wired to the Boron? I would use a thin coax, as short as possible. Ground the coax shield only at the reference module, leave the other end open. Make a good ground connection between the Boron and the reference module.

I agree figuring out ADC cal would be very handy.

1 Like

With a precision voltage reference connected to A0 and A1, my raw ADC values were only one (1) off from the reference value.

26 pubCount, 123093 samples, 1 ADC0 avg delta, 1 ADC1 avg delta
27 pubCount, 123091 samples, 1 ADC0 avg delta, 1 ADC1 avg delta
28 pubCount, 123092 samples, 1 ADC0 avg delta, 1 ADC1 avg delta
29 pubCount, 123092 samples, 1 ADC0 avg delta, 1 ADC1 avg delta
30 pubCount, 123093 samples, 1 ADC0 avg delta, 1 ADC1 avg delta
31 pubCount, 123091 samples, 1 ADC0 avg delta, 1 ADC1 avg delta

When I offset the ADC by the delta of 1, I get the exact reference value. Still, one ADC value off isnā€™t bad. Keep in mind when converting to mV or V that the ADC only has four significant digits, so donā€™t expect any more decimal places than 2048 mV or 2.048 V.

I wrote two applications, one to determine the calibrated ADC offset, and another to apply it. See them at my website link listed below. My measurements were on my Xenon.

www.SavvyMicrocontrolerSolutions.com

Hope this helps someone.

1 Like