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.
#include <Arduino.h> #include <Adafruit_TinyUSB.h> // for Serial 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> #include <Adafruit_TinyUSB.h> // for Serial #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); }
Text editor powered by tinymce.