We want to read the GPIO voltage level. Per the documentation this should be possible by using input 9 and the internal reference. What I am observing is that probing the pin with a multimeter reads 1.78 V. The ADC is giving me 1.68 - 1.72V. I've tweaked several of the configurations and this is the best I am getting.
Is there any special considerations for reading the GPI voltage this way?
Implementation below:
// CODE TO READ THE VDD_GPIO // With internal reference, single ended input (grounded negative input), and a gain of 1/6 the input range // will be: // Input range = (0.6 V)/(1/6) = 3.6 V #include <device.h> #include <drivers/gpio.h> #include <drivers/adc.h> #include <hal/nrf_saadc.h> #define ADC_DEVICE_NODE DT_INST(0, nordic_nrf_saadc) #define VRAIL_ADC_GAIN ADC_GAIN_1_4 #define ADC_REFERENCE ADC_REF_INTERNAL #define ADC_ACQUISITION_TIME ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40) #define ADC_CHANNEL_INPUT NRF_SAADC_INPUT_VDD #define ADC_CHAN_ID 0 #define ADC_RESOLUTION (14) #define NUMBER_OF_EXTRA_SAMPLES (1) static bool adc_calibrate_do = true; struct device* adc_dev; struct adc_channel_cfg adc_channel_config = { .gain = VRAIL_ADC_GAIN, .reference = ADC_REFERENCE, .acquisition_time = ADC_ACQUISITION_TIME, .channel_id = ADC_CHAN_ID, .differential = false, .input_positive = ADC_CHANNEL_INPUT, }; const struct adc_sequence_options sequence_opts = { .interval_us = 10, .callback = NULL, .user_data = NULL, .extra_samplings = NUMBER_OF_EXTRA_SAMPLES, }; bool util_set_up_rail_adc(void) { bool ret = true; adc_dev = DEVICE_DT_GET(ADC_DEVICE_NODE); if (!adc_dev) { LOG_ERR("device_get_binding ADC_0 failed\n"); ret = false; } int err = adc_channel_setup(adc_dev, &adc_channel_config); if (err) { LOG_ERR("Error in adc setup: %d\n", err); ret = false; } return ret; } int util_get_rail_millivolts(int* adc_read_val) { int ret = -ENOENT; int16_t raw_sample_buffer[NUMBER_OF_EXTRA_SAMPLES + 1]; const struct adc_sequence sequence = { .options = &sequence_opts, .channels = BIT(ADC_CHAN_ID), .buffer = raw_sample_buffer, .buffer_size = sizeof(raw_sample_buffer), .resolution = ADC_RESOLUTION, .oversampling = 4, .calibrate = adc_calibrate_do, }; if (!adc_dev) { return ret; } ret = adc_read(adc_dev, &sequence); // Set to false so subsequence calls to util_get_rail_millivolts do not // trigger calibration. adc_calibrate_do = false; // Do a rolling average of all samples. int avg_mv_reading = -1; for (int x = 0; x < (NUMBER_OF_EXTRA_SAMPLES + 1); x++) { int adc_mv = raw_sample_buffer[x]; ret = adc_raw_to_millivolts(adc_ref_internal(adc_dev), adc_channel_config.gain, sequence.resolution, &adc_mv); if (ret >= 0) { LOG_DBG("RAIL RAW %d ~ %d mV", raw_sample_buffer[x], adc_mv); if (x == 0) { avg_mv_reading = adc_mv; } else { avg_mv_reading = (avg_mv_reading + adc_mv)/2; } } } if (ret >= 0) { *adc_read_val = avg_mv_reading; LOG_INF("RAIL AVG %d mV", avg_mv_reading); } return ret; }