The nRF52 family includes an adjustable 'successive-approximation ADC' which can be configured to convert data with up to 14-bit resolution (0..16383), and the reference voltage can be adjusted up to 3.6V internally.

The default values for the ADC are 10-bit resolution (0..1023) with a 3.6V reference voltage, meaning every digit returned from the ADC = 3600mV/1024 = 3.515625mV.

Analog Reference Voltage

The internal reference voltage is 0.6V with a variable gain setting, and can be adjust via the analogReference(...) function, providing one of the following values:

  • AR_INTERNAL (0.6V Ref * 6 = 0..3.6V) <-- DEFAULT
  • AR_INTERNAL_3_0 (0.6V Ref * 5 = 0..3.0V)
  • AR_INTERNAL_2_4 (0.6V Ref * 4 = 0..2.4V)
  • AR_INTERNAL_1_8 (0.6V Ref * 3 = 0..1.8V)
  • AR_INTERNAL_1_2 (0.6V Ref * 2 = 0..1.6V)
  • AR_VDD4 (VDD/4 REF * 4 = 0..VDD)

For example:

// Set the analog reference to 3.0V (default = 3.6V)
analogReference(AR_INTERNAL_3_0);

Analog Resolution

The ADC resolution can be set to 8, 10, 12 or 14 bits using the analogReadResolution(...) function, with the default value being 10-bit:

// Set the resolution to 12-bit (0..4095)
analogReadResolution(12); // Can be 8, 10, 12 or 14

Default ADC Example (10-bit, 3.6V Reference)

The original source for this code is included in the nRF52 BSP and can be viewed online here.

int adcin    = A5;
int adcvalue = 0;
float mv_per_lsb = 3600.0F/1024.0F; // 10-bit ADC with 3.6V input range

void setup() {
  Serial.begin(115200);
  while ( !Serial ) delay(10);   // for nrf52840 with native usb
}

void loop() {
  // Get a fresh ADC value
  adcvalue = analogRead(adcin);

  // Display the results
  Serial.print(adcvalue);
  Serial.print(" [");
  Serial.print((float)adcvalue * mv_per_lsb);
  Serial.println(" mV]");

  delay(100);
}

Advanced Example (12-bit, 3.0V Reference)

The original source for this code is included in the nRF52 BSP and can be viewed online here.

#include <Arduino.h>

#if defined ARDUINO_NRF52840_CIRCUITPLAY
#define  PIN_VBAT          A8   // this is just a mock read, we'll use the light sensor, so we can run the test
#endif

uint32_t vbat_pin = PIN_VBAT;             // A7 for feather nRF52832, A6 for nRF52840

#define VBAT_MV_PER_LSB   (0.73242188F)   // 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096

#ifdef NRF52840_XXAA
#define VBAT_DIVIDER      (0.5F)          // 150K + 150K voltage divider on VBAT
#define VBAT_DIVIDER_COMP (2.0F)          // Compensation factor for the VBAT divider
#else
#define VBAT_DIVIDER      (0.71275837F)   // 2M + 0.806M voltage divider on VBAT = (2M / (0.806M + 2M))
#define VBAT_DIVIDER_COMP (1.403F)        // Compensation factor for the VBAT divider
#endif

#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)


float readVBAT(void) {
  float raw;

  // Set the analog reference to 3.0V (default = 3.6V)
  analogReference(AR_INTERNAL_3_0);

  // Set the resolution to 12-bit (0..4095)
  analogReadResolution(12); // Can be 8, 10, 12 or 14

  // Let the ADC settle
  delay(1);

  // Get the raw 12-bit, 0..3000mV ADC value
  raw = analogRead(vbat_pin);

  // Set the ADC back to the default settings
  analogReference(AR_DEFAULT);
  analogReadResolution(10);

  // Convert the raw value to compensated mv, taking the resistor-
  // divider into account (providing the actual LIPO voltage)
  // ADC range is 0..3000mV and resolution is 12-bit (0..4095)
  return raw * REAL_VBAT_MV_PER_LSB;
}

uint8_t mvToPercent(float mvolts) {
  if(mvolts<3300)
    return 0;

  if(mvolts <3600) {
    mvolts -= 3300;
    return mvolts/30;
  }

  mvolts -= 3600;
  return 10 + (mvolts * 0.15F );  // thats mvolts /6.66666666
}

void setup() {
  Serial.begin(115200);
  while ( !Serial ) delay(10);   // for nrf52840 with native usb

  // Get a single ADC sample and throw it away
  readVBAT();
}

void loop() {
  // Get a raw ADC reading
  float vbat_mv = readVBAT();

  // Convert from raw mv to percentage (based on LIPO chemistry)
  uint8_t vbat_per = mvToPercent(vbat_mv);

  // Display the results

  Serial.print("LIPO = ");
  Serial.print(vbat_mv);
  Serial.print(" mV (");
  Serial.print(vbat_per);
  Serial.println("%)");

  delay(1000);
}

This guide was first published on Mar 11, 2020. It was last updated on Mar 11, 2020.

This page (nRF52 ADC) was last updated on May 05, 2021.

Text editor powered by tinymce.