This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF52810 SAADC + S112 - ADC previously sampled value is affecting the next channel

Hi everyone,

I'm trying to use nRF52810 with SoftDevice S112 to sample five ADC channels.
I'm finding this very weird issue, where first channel is showing correct value (i.e. 4050), but then next channels should
be around 0-100 range, however they are slowly decreasing from previous channel value.

It's almost as if previously sampled value is affecting the next channel.
Also, if I start decreasing voltage on first channel, others seem to decrease as well...

<info> app: ADC  4055  1303   688   494   389
<info> app: ADC  4054  1281   656   443   318
<info> app: ADC  4054  1296   673   463   346
<info> app: ADC  4056  1298   672   470   360
<info> app: ADC  4058  1293   659   445   312
<info> app: ADC  4057  1299   686   484   374
<info> app: ADC  4053  1279   654   433   306
<info> app: ADC  4058  1304   686   485   388
<info> app: ADC  4057  1271   648   425   284
<info> app: ADC  4052  1303   691   495   397
<info> app: ADC  4055  1275   644   428   292
<info> app: ADC  4057  1299   683   482   376
<info> app: ADC  4055  1289   663   460   340
<info> app: ADC  4058  1294   663   450   318
<info> app: ADC  4056  1301   683   484   383
<info> app: ADC  4056  1284   651   432   297
<info> app: ADC  4053  1299   689   492   396
<info> app: ADC  4059  1279   643   430   289
<info> app: ADC  4058  1302   688   491   400
<info> app: ADC  4055  1286   653   441   315
<info> app: ADC  4057  1293   662   454   323

Now, if I move the voltage from channel 0 to channel 1, I will see that channel 0 is showing correct value but ch 2, ch 3 and ch 4 are affected.

I have tried changing sampling time from min to max, it does not change anything.
Also, tried over-sampling, using PPI, triggering manually by timer but the issue still persists.

I'm running out of ideas on what to try, and also I haven't used SAADC on nRF52810.
All channels have pull-down enabled. Also, when I connect load to every channel, it performs a little better but I'm still concerned that I might get issues (false positive/negative) when there is nothing connected to the ADC pin.


Here is the code I'm using:

#include <math.h>
#include "common.h"
#include "saadc.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

// Definitions
#define ADC_PRINT                   1
#define SAMPLES_IN_BUFFER           5           // Number of channels being sampled
#define SAADC_SAMPLE_RATE           500         // SAADC sample rate in ms
// #define APP_ADC_RMS_MAX_SAMPLES     100         // MAX number of samples for RMS calculation
#define APP_ADC_RMS_MAX_SAMPLES     120         // MAX number of samples for RMS calculation

#define SAADC_CH1	NRF_SAADC_INPUT_AIN5
#define SAADC_CH2	NRF_SAADC_INPUT_AIN7
#define SAADC_CH3	NRF_SAADC_INPUT_AIN0
#define SAADC_CH4	NRF_SAADC_INPUT_AIN1
#define SAADC_CH5	NRF_SAADC_INPUT_AIN2

#define RMS_WINDOW_ON_SINGLE_THRESH   512

// Check if we will overflow with the current config
// FIXME: Doesn't work currently!
#define RMS_MAX_SUM_VALUE   (APP_ADC_RMS_MAX_SAMPLES * 4095*4095)
#if (RMS_MAX_SUM_VALUE >= 0xFFFFFFFF)
#error "RMS Window Size will overflow UINT32"
#endif

// Global variables
static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(1);
static nrf_saadc_value_t     m_buffer_pool[2][SAMPLES_IN_BUFFER];
static nrf_ppi_channel_t     m_ppi_channel;
static uint32_t              m_adc_evt_counter;
static uint32_t              arru32_ADCsamples[SAMPLES_IN_BUFFER] = {0};    // ADC^2 SUM samples
static uint32_t              nADC_currentSample = 0;                        // ADC sample counter
static uint32_t              nRMS_Channel[SAMPLES_IN_BUFFER] = {0};         // RMS values for each channel
static bool                  bCalcRMSPending = false;                       // RMS calc pending flag

// Local function prototypes
static void saadc_callback(nrf_drv_saadc_evt_t const * p_event);
static void timer_handler(nrf_timer_event_t event_type, void * p_context);
static bool rmsCalc(uint32_t nRMSWindowSUM, uint32_t *pResult);



static void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
    // NRF_LOG_INFO("p_event->type ==%d", (uint8_t)p_event->type);
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        ret_code_t err_code;
        uint8_t i =0;

        // If RMS calculation is pending/in-progress, discard any new samples
        if(bCalcRMSPending)
        {
            return;
        }

        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, p_event->data.done.size);
        APP_ERROR_CHECK(err_code);

        #if (ADC_PRINT!=0)
        // NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);
        #endif

        // Check if invalid state
        if(nADC_currentSample >= APP_ADC_RMS_MAX_SAMPLES)
        {

            NRF_LOG_ERROR("%s: nADC_currentSample >= APP_ADC_RMS_MAX_SAMPLES (%d >= %d)", __FUNCTION__, nADC_currentSample, APP_ADC_RMS_MAX_SAMPLES);
            return;
        }

        // Loop through samples
        for (i = 0; i < SAMPLES_IN_BUFFER; i++)
        {
            // SUM current ADC value
            arru32_ADCsamples[i] += (p_event->data.done.p_buffer[i]*p_event->data.done.p_buffer[i]);
        }

        #if (ADC_PRINT!=0)
        NRF_LOG_INFO("ADC %5d %5d %5d %5d %5d", 
            p_event->data.done.p_buffer[0],
            p_event->data.done.p_buffer[1],
            p_event->data.done.p_buffer[2],
            p_event->data.done.p_buffer[3],
            p_event->data.done.p_buffer[4]
        );
        #endif

        return;

        // Housekeeping before exiting
        nADC_currentSample++;

        // Check if we should calculate RMS
        if(nADC_currentSample >= APP_ADC_RMS_MAX_SAMPLES)
        {
            nADC_currentSample = 0;
            bCalcRMSPending = true;     // Set flag to reject any new samples until RMS calc is complete

            NRF_LOG_INFO("bCalcRMSPending");
            for(i=0; i< SAMPLES_IN_BUFFER; i++)
            {
                rmsCalc(arru32_ADCsamples[i], &nRMS_Channel[i]);
                machine_UpdateRMS(i, nRMS_Channel[i]);
                nRMS_Channel[i] = 0;
                arru32_ADCsamples[i] = 0;
            }
            bCalcRMSPending = false;
        }

        #if (ADC_PRINT!=0)
        NRF_LOG_INFO("\r\n");
        #endif
        m_adc_evt_counter++;
    }
}

static void timer_handler(nrf_timer_event_t event_type, void * p_context)
{
    if (event_type == NRF_TIMER_EVENT_COMPARE0)
    {
        // Trigger another sample
        ret_code_t err_code;
        err_code = nrfx_saadc_sample();
        if(err_code !=NRF_SUCCESS)
        {
            NRF_LOG_ERROR("Error: 0x%x", err_code);
        }
        APP_ERROR_CHECK(err_code);
        // nrf_saadc_value_t adc[5] = {0};

        // err_code = nrfx_saadc_sample_convert(0, &adc[0]);
        // APP_ERROR_CHECK(err_code);
        // while(nrfx_saadc_is_busy());
        // err_code = nrfx_saadc_sample_convert(1, &adc[1]);
        // APP_ERROR_CHECK(err_code);
        // while(nrfx_saadc_is_busy());
        // err_code = nrfx_saadc_sample_convert(2, &adc[2]);
        // APP_ERROR_CHECK(err_code);
        // while(nrfx_saadc_is_busy());
        // err_code = nrfx_saadc_sample_convert(3, &adc[3]);
        // APP_ERROR_CHECK(err_code);
        // while(nrfx_saadc_is_busy());
        // err_code = nrfx_saadc_sample_convert(4, &adc[4]);
        // APP_ERROR_CHECK(err_code);
        // while(nrfx_saadc_is_busy());
        // NRF_LOG_INFO("ADC %5d %5d %5d %5d %5d", adc[0], adc[1], adc[2], adc[3], adc[4]);

    }
}

// Calculate rms based on nRMSWindowSUM and store value in pResult
// Return true if RMS is over set RMS_WINDOW_ON_SINGLE_THRESH to indicate machine is on
static bool rmsCalc(uint32_t nRMSWindowSUM, uint32_t *pResult)
{
    if(pResult == NULL)
    {
        NRF_LOG_ERROR("Error: Passing NULL pointer to rmsCalc!!");
        return false;
    }
    if(nRMSWindowSUM == 0)
    {
        *pResult = 0;
        return false;
    }

    return true;
}

void saadc_sampling_event_init(void)
{
    ret_code_t err_code;

#if 1
    // err_code = nrfx_saadc_sample();
    // if(err_code !=NRF_SUCCESS)
    // {
    //     NRF_LOG_ERROR("Error: 0x%x", err_code);
    // }
    // APP_ERROR_CHECK(err_code);
    // err_code = nrf_drv_ppi_init();
    // APP_ERROR_CHECK(err_code);

    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
    APP_ERROR_CHECK(err_code);

    /* setup m_timer for compare event every 400ms */
    uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, SAADC_SAMPLE_RATE);
    nrf_drv_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
    nrf_drv_timer_enable(&m_timer);

#else
    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer, NRF_TIMER_CC_CHANNEL0);
    uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();

    /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, timer_compare_event_addr, saadc_sample_task_addr);
    APP_ERROR_CHECK(err_code);
#endif
}


void saadc_sampling_event_enable(void)
{
    ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
    APP_ERROR_CHECK(err_code);
}


void saadc_init(void)
{
    ret_code_t err_code;
    // Setup ADC channels
    // -- CH1
    nrf_saadc_channel_config_t channel_CH1_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(SAADC_CH1);
    channel_CH1_config.resistor_p 	= NRF_SAADC_RESISTOR_PULLDOWN;
    channel_CH1_config.reference    = NRF_SAADC_REFERENCE_VDD4;
    channel_CH1_config.gain 		= NRF_SAADC_GAIN1_4;
    channel_CH1_config.burst        = NRF_SAADC_BURST_DISABLED;
    channel_CH1_config.acq_time 	= NRF_SAADC_ACQTIME_40US;

    // -- CH2
    nrf_saadc_channel_config_t channel_CH2_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(SAADC_CH2);
    channel_CH1_config.resistor_p   = NRF_SAADC_RESISTOR_PULLDOWN;
    channel_CH1_config.reference 	= NRF_SAADC_REFERENCE_VDD4;
    channel_CH1_config.gain 		= NRF_SAADC_GAIN1_4;
    channel_CH1_config.burst        = NRF_SAADC_BURST_DISABLED;
    channel_CH1_config.acq_time 	= NRF_SAADC_ACQTIME_40US;

    // -- CH3
    nrf_saadc_channel_config_t channel_CH3_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(SAADC_CH3);
    channel_CH1_config.resistor_p   = NRF_SAADC_RESISTOR_PULLDOWN;
    channel_CH1_config.reference 	= NRF_SAADC_REFERENCE_VDD4;
    channel_CH1_config.gain 		= NRF_SAADC_GAIN1_4;
    channel_CH1_config.burst        = NRF_SAADC_BURST_DISABLED;
    channel_CH1_config.acq_time 	= NRF_SAADC_ACQTIME_40US;

    // -- CH4
    nrf_saadc_channel_config_t channel_CH4_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(SAADC_CH4);
    channel_CH1_config.resistor_p   = NRF_SAADC_RESISTOR_PULLDOWN;
    channel_CH1_config.reference 	= NRF_SAADC_REFERENCE_VDD4;
    channel_CH1_config.gain 		= NRF_SAADC_GAIN1_4;
    channel_CH1_config.burst        = NRF_SAADC_BURST_DISABLED;
    channel_CH1_config.acq_time 	= NRF_SAADC_ACQTIME_40US;

    // -- CH5
    nrf_saadc_channel_config_t channel_CH5_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(SAADC_CH5);
    channel_CH1_config.resistor_p   = NRF_SAADC_RESISTOR_PULLDOWN;
    channel_CH1_config.reference 	= NRF_SAADC_REFERENCE_VDD4;
    channel_CH1_config.gain 		= NRF_SAADC_GAIN1_4;
    channel_CH1_config.burst        = NRF_SAADC_BURST_DISABLED;
    channel_CH1_config.acq_time 	= NRF_SAADC_ACQTIME_40US;


    // SAADC init
    nrf_drv_saadc_config_t nrfx_saadc_config_t 	= NRFX_SAADC_DEFAULT_CONFIG;
    nrfx_saadc_config_t.resolution              = NRF_SAADC_RESOLUTION_12BIT;
    nrfx_saadc_config_t.oversample              = NRF_SAADC_OVERSAMPLE_DISABLED;
    nrfx_saadc_config_t.interrupt_priority      = 6;
    nrfx_saadc_config_t.low_power_mode          = false;
    err_code = nrf_drv_saadc_init(&nrfx_saadc_config_t, saadc_callback);
    APP_ERROR_CHECK(err_code);

    // Initialize ADC channels
    // -- CH1
    err_code = nrf_drv_saadc_channel_init(0, &channel_CH1_config);
    APP_ERROR_CHECK(err_code);
    // -- CH2
    err_code = nrf_drv_saadc_channel_init(1, &channel_CH2_config);
    APP_ERROR_CHECK(err_code);
    // -- CH3
    err_code = nrf_drv_saadc_channel_init(2, &channel_CH3_config);
    APP_ERROR_CHECK(err_code);
    // -- CH4
    err_code = nrf_drv_saadc_channel_init(3, &channel_CH4_config);
    APP_ERROR_CHECK(err_code);
    // -- CH5
    err_code = nrf_drv_saadc_channel_init(4, &channel_CH5_config);
    APP_ERROR_CHECK(err_code);

    while(nrfx_saadc_is_busy());
    nrfx_saadc_calibrate_offset();
    APP_ERROR_CHECK(err_code);
    while(nrfx_saadc_is_busy());

    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);

    // err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
    // APP_ERROR_CHECK(err_code);

}

Related