ADC converter not reading correctly

I have a nRF52832 custom board derived from the 52832DK board. All is working perfectly with the exception of the ADC converter. I am only using AIN0 which is on pin 0.02. At P0.02 I can see the voltage changing from 0.0V to 1.67V based on a battery monitor pin value. I do not see the correct value being seen by the ADC. I have used the ADC converter in the nRF9160 many times for many projects and all worked fine. This is my first project using the nRF52832. as shown below I expect 1.67V when enabled and 0.0V when disabled. that is what I see on P 0.02 (AIN0). I am sure it is something I have set up incorrectly.

cli CMD: adc

0: ADC raw value: 88 0x0058 ret: 0
Measured voltage: 0.31 V
BAT_MON Enabled ADC: 0.31


0: ADC raw value: 79 0x004F ret: 0
Measured voltage: 0.28 V
BAT_MON Disabled ADC: 0.28

prj.conf

#
# Copyright (c) 2018 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

# Enable the UART driver
CONFIG_UART_ASYNC_API=y
CONFIG_NRFX_UARTE=y

# iUART0 Enabled
CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_LOG=y

# UART0 Disabled
#CONFIG_SERIAL=n
#CONFIG_CONSOLE=n
#CONFIG_UART_CONSOLE=n
#CONFIG_LOG=n

CONFIG_PM_DEVICE=y

CONFIG_GPIO=y

# Make sure printk is printing to the UART console

CONFIG_HEAP_MEM_POOL_SIZE=2048

CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
#CONFIG_BT_DEVICE_NAME="Nordic_UART_Service"
#CONFIG_BT_DEVICE_NAME="Data_Logger_BLE_"
CONFIG_BT_DEVICE_NAME="DL_"
CONFIG_BT_DEVICE_APPEARANCE=833
CONFIG_BT_MAX_CONN=1
CONFIG_BT_MAX_PAIRED=1

# Watchdog
CONFIG_WDT_LOG_LEVEL_DBG=y
CONFIG_WDT_LOG_LEVEL_DBG=y
CONFIG_WATCHDOG=y
CONFIG_WDT_DISABLE_AT_BOOT=n

# Enable the NUS service
CONFIG_BT_NUS=y

# Enable bonding
CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y

# Enable DK LED and Buttons library
CONFIG_DK_LIBRARY=y

# This example requires more stack
CONFIG_MAIN_STACK_SIZE=1152
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

# Config logger
CONFIG_USE_SEGGER_RTT=y
CONFIG_LOG_BACKEND_RTT=y
CONFIG_LOG_BACKEND_UART=n
CONFIG_LOG_PRINTK=n
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y

CONFIG_ASSERT=y

#TJM Added
CONFIG_I2C=y
CONFIG_ADC=y
CONFIG_ADC_ASYNC=y
CONFIG_ADC_LOG_LEVEL_INF=y
#CONFIG_ADC_LOG_LEVEL_DBG=y
CONFIG_ADC_NRFX_SAADC=y
CONFIG_NRFX_SAADC=y
CONFIG_SPI=y
CONFIG_SPI_ASYNC=y
CONFIG_SPI_LOG_LEVEL_INF=y
CONFIG_WATCHDOG_LOG_LEVEL_DBG=y
CONFIG_LOG_DEFAULT_LEVEL=4

CONFIG_REBOOT=y

main.c


/* start ADC test */
#include <math.h>
#include <nrfx_saadc.h>
#include <zephyr/drivers/adc.h>
#define ADC_BUFFER_SIZE 1 //!< number of ADC channels to read
// #define ADC_BUFFER_SIZE 4 //!< number of ADC channels to read

#if DT_NODE_HAS_STATUS(DT_ALIAS(adc), okay) //!< check if adc alias is okay in devicetree and set ADC_DEV_NODE to adc alias
#define ADC_DEV_NODE DT_ALIAS(adc)          //!< if adc alias is okay, set ADC_DEV_NODE to adc alias
#else
#error "Please set the correct ADC device"
#endif

////#define ADC_DEVICE_NAME DT_ADC_0_NAME
#define ADC_RESOLUTION 10 //!< resolution for ADC readings in bits (e.g., 10 bits for values from 0 to 1023)
#define ADC_GAIN                                                                                                                           \
  ADC_GAIN_1_6 //!< gain setting for ADC readings, set to 1/6 to allow for a wider input voltage range (e.g., up to 3.6V with a 3.3V
               //!< reference)
#define ADC_REFERENCE                                                                                                                      \
  ADC_REF_INTERNAL //!< reference voltage for ADC readings, set to internal reference for stable and consistent measurements
#define ADC_ACQUISITION_TIME                                                                                                               \
  ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10) //!< acquisition time for ADC readings, set to 10 microseconds to allow for accurate sampling
                                              //!< of the input signal while minimizing noise and power consumption
#define ADC_1ST_CHANNEL_ID 0 //!< channel ID for the first ADC channel to read, set to 0 for the first channel (e.g., AIN0 on nRF52832)
#define ADC_1ST_CHANNEL_INPUT                                                                                                              \
  NRF_SAADC_INPUT_AIN0 //!< input selection for the first ADC channel, set to AIN0 for the first analog input on nRF52832 (e.g., connected
                       //!< to a sensor output or battery voltage)
#define ADC_2ND_CHANNEL_ID 1 //!< channel ID for the second ADC channel to read, set to 1 for the second channel (e.g., AIN1 on nRF52832)
#define ADC_2ND_CHANNEL_INPUT                                                                                                              \
  NRF_SAADC_INPUT_AIN1 //!< input selection for the second ADC channel, set to AIN1 for the second analog input on nRF52832 (e.g., connected
                       //!< to a different sensor output or reference voltage)
#define ADC_3RD_CHANNEL_ID 2 //!< channel ID for the third ADC channel to read, set to 2 for the third channel (e.g., AIN2 on nRF52832)
#define ADC_3RD_CHANNEL_INPUT                                                                                                              \
  NRF_SAADC_INPUT_AIN2 //!< input selection for the third ADC channel, set to AIN2 for the third analog input on nRF52832 (e.g., connected
                       //!< to a different sensor output or reference voltage)
#define ADC_4TH_CHANNEL_ID 3 //!< channel ID for the fourth ADC channel to read, set to 3 for the fourth channel (e.g., AIN3 on nRF52832)
#define ADC_4TH_CHANNEL_INPUT                                                                                                              \
  NRF_SAADC_INPUT_AIN3 //!< input selection for the fourth ADC channel, set to AIN3 for the fourth analog input on nRF52832 (e.g., connected
                       //!< to a different sensor output or reference voltage)
#define ADC_5TH_CHANNEL_ID 4 //!< channel ID for the fifth ADC channel to read, set to 4 for the fifth channel (e.g., AIN4 on nRF52832)
#define ADC_5TH_CHANNEL_INPUT                                                                                                              \
  NRF_SAADC_INPUT_AIN4 //!< input selection for the fifth ADC channel, set to AIN4 for the fifth analog input on nRF52832 (e.g., connected
                       //!< to a different sensor output or reference voltage)
#define ADC_6ST_CHANNEL_ID 5 //!< channel ID for the sixth ADC channel to read, set to 5 for the sixth channel (e.g., AIN5 on nRF52832)
#define ADC_6ST_CHANNEL_INPUT                                                                                                              \
  NRF_SAADC_INPUT_AIN5 //!< input selection for the sixth ADC channel, set to AIN5 for the sixth analog input on nRF52832 (e.g., connected
                       //!< to a different sensor output or reference voltage)

const struct device *const adc_dev = DEVICE_DT_GET(ADC_DEV_NODE); //!< get the device structure for the ADC device defined by ADC_DEV_NODE

#if 0
static const struct adc_channel_cfg m_4th_channel_cfg = //!< ADC config for ADC input
    {
        .gain = ADC_GAIN,
        .reference = ADC_REFERENCE,
        .acquisition_time = ADC_ACQUISITION_TIME,
        .channel_id = ADC_4TH_CHANNEL_ID,
#if defined(CONFIG_ADC_CONFIGURABLE_INPUTS)
        .input_positive = ADC_4TH_CHANNEL_INPUT,
#endif
};
#endif

#if 0
static const struct adc_channel_cfg m_3rd_channel_cfg = {
    .gain = ADC_GAIN,
    .reference = ADC_REFERENCE,
    .acquisition_time = ADC_ACQUISITION_TIME,
    .channel_id = ADC_3RD_CHANNEL_ID,
#if defined(CONFIG_ADC_CONFIGURABLE_INPUTS)
    .input_positive = ADC_3RD_CHANNEL_INPUT,
#endif
};
#endif

#if 0
static const struct adc_channel_cfg m_2nd_channel_cfg = //!< ADC config for Battery input
    {
        .gain = ADC_GAIN,
        .reference = ADC_REFERENCE,
        .acquisition_time = ADC_ACQUISITION_TIME,
        .channel_id = ADC_2ND_CHANNEL_ID,
#if defined(CONFIG_ADC_CONFIGURABLE_INPUTS)
        .input_positive = ADC_2ND_CHANNEL_INPUT,
#endif
};
#endif

#if 1
/*!
 * \brief ADC configuration for the first channel
 * This structure defines the configuration for the first ADC channel, including gain, reference voltage, acquisition time, channel ID, and
 * input selection. It is used to set up the ADC for reading from the specified channel with the desired settings.
 */
static const struct adc_channel_cfg m_1st_channel_cfg = {
    .gain = ADC_GAIN,
    .reference = ADC_REFERENCE,
    .acquisition_time = ADC_ACQUISITION_TIME,
    .channel_id = ADC_1ST_CHANNEL_ID,
#if defined(CONFIG_ADC_CONFIGURABLE_INPUTS)
    .input_positive = ADC_1ST_CHANNEL_INPUT,
#endif
};
#endif

static int16_t m_sample_buffer[ADC_BUFFER_SIZE]; //!< buffer to receive ADC data from configured channels

#define ADC_REF_MV 3600U
#define ADC_DIV_MV 1000U
#define ADC_MAX_VAL ((1U << ADC_RESOLUTION) - 1U)

static const float adc_volts_per_step = (float)ADC_REF_MV / ((float)ADC_MAX_VAL * (float)ADC_DIV_MV);

/** \fn static inline float adc_raw_to_voltage(uint16_t raw)
 *
 * \brief Convert raw ADC reading to voltage in volts.
 *
 * \param raw Raw ADC sample value (integer from 0..ADC_MAX_VAL)
 * \return Voltage corresponding to the raw ADC value in volts (float)
 *
 * Uses the precomputed adc_volts_per_step factor which incorporates the
 * reference voltage (ADC_REF_MV), ADC resolution and any divider (ADC_DIV_MV).
 */
static inline float adc_raw_to_voltage(uint16_t raw) { return (float)raw * adc_volts_per_step; }

/*!
 * \fn int adc_sample(void)
 *
 * \brief get a single reading from ADC channel
 *
 * will read ADC_1ST_CHANNEL_ID\n
 * resoultion set to ADC_RESOLUTION in millivolts\n
 * will printout floating point value based on 3.6V max\n
 *
 * \return ADC_RESOLUTION value on ADC_1ST_CHANNEL_ID if pass or -1 if failed\n
 */
int adc_sample(void) {
#if 1
  int ret;
  const struct adc_sequence sequence = {
      .channels = BIT(ADC_1ST_CHANNEL_ID),
      .buffer = m_sample_buffer,
      .buffer_size = sizeof(m_sample_buffer),
      .resolution = ADC_RESOLUTION,
  };

  if (!adc_dev) {
    return -ENODEV;
  }

  ret = adc_read(adc_dev, &sequence);
  if (ret != 0) {
    myPrintkE("ADC read err: %d\n", ret);
    return ret;
  }

  for (int i = 0; i < ADC_BUFFER_SIZE; i++) {
    myPrintkI("%d: ADC raw value: %d 0x%04X ret: %d\n", i, m_sample_buffer[i], m_sample_buffer[i], ret);
    adc_voltage[i] = adc_raw_to_voltage(m_sample_buffer[i]);
    printf("Measured voltage: %3.2f V\n", (double)adc_voltage[i]);
  }

  return 0;
}
Related