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

SAADC Cross-Channel Sensitivity Issue

I'm having trouble with the SAADC in scan and double buffer mode with 4 channels enabled, where 3 channels are external inputs and the 4th is the internal VDD. 

It appears that when a voltage is presented to one of the SAADC channels, the values reported for all channels change. For example, applying 3.0V to channel 1, and leaving channels two and three floating gives a reading of 3413 (expected) but channel 2 shows 2011 and channel 3 shows 897. The internal VDD channel however, does not change and remains at the expected value. The same happens when I apply 3.0V to channel 2 (7, 3413, 1123). 

I'm developing with the nRF52810 on a custom board and initially thought this was a hardware related issue (solder shorts etc) and so tested the project on the nRF52832 DK in emulation mode (PCA10040e), where the issue persists. I've also looked into PAN-86, which does not seem apply to the nRF52810. 

The following is my SAADC configuration code and a MWE for main. Due to the eventual application being in an environment with changeable temperature, the SAADC is calibrated each time the system wakes from sleep (every 30s) to compensate for these fluctuations. 

#define TOTAL_SAMPLES               100u
#define SAADC_SAMP_INTERVAL_MSECS   10u

void saadc_sampling_event_init(void)
{
    ret_code_t 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, NULL);
    APP_ERROR_CHECK(err_code);

    /* setup m_timer for compare event every 10ms */
    uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, SAADC_SAMP_INTERVAL_MSECS);
    nrf_drv_timer_extended_compare(&m_timer,
                                   NRF_TIMER_CC_CHANNEL2,
                                   ticks,
                                   NRF_TIMER_SHORT_COMPARE2_CLEAR_MASK,
                                   false);
    nrf_drv_timer_enable(&m_timer);

    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer,
                                                                                NRF_TIMER_CC_CHANNEL2);
    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);

}

void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
    uint32_t adcRetrieve;
        
    ret_code_t err_code;
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)  
    {
			
        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAADC_SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
        
        adcRetrieve = (uint32_t)p_event->data.done.p_buffer[0];
        uint_circular_buffer_push(&g_buffer1, adcRetrieve);

        adcRetrieve = (uint32_t)p_event->data.done.p_buffer[1];
        uint_circular_buffer_push(&g_buffer2, adcRetrieve);

        adcRetrieve = (uint32_t)p_event->data.done.p_buffer[2];
        uint_circular_buffer_push(&g_buffer3, adcRetrieve);

        adcRetrieve = (uint32_t)p_event->data.done.p_buffer[3];
        uint_circular_buffer_push(&g_bufferBattery, adcRetrieve);

        // increase global count of ADC samples
        no_samples++;

    }

    else if(p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
    {
      // re-setup for buffers:
      err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAADC_SAMPLES_IN_BUFFER);
      APP_ERROR_CHECK(err_code);

      err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAADC_SAMPLES_IN_BUFFER);
      APP_ERROR_CHECK(err_code);
    }
    
}

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;

    nrf_drv_saadc_config_t saadc_config;
    nrf_saadc_channel_config_t channel_config;

    //Configure SAADC
    saadc_config.low_power_mode = true;                     //Enable low power mode.
    saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;   
    saadc_config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;                                          
    saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW; 

    nrf_saadc_channel_config_t channel_config0; 
    nrf_saadc_channel_config_t channel_config1; 
    nrf_saadc_channel_config_t channel_config2; 
    nrf_saadc_channel_config_t channel_config3; 

    channel_config0.reference = SAADC_CH_CONFIG_REFSEL_Internal; // 0.6V internal ref
    channel_config0.gain = NRF_SAADC_GAIN1_6;
    channel_config0.acq_time = NRF_SAADC_ACQTIME_40US;
    channel_config0.mode = NRF_SAADC_MODE_SINGLE_ENDED;
    channel_config0.pin_p = NRF_SAADC_INPUT_AIN1;
    channel_config0.pin_n = NRF_SAADC_INPUT_DISABLED;
    channel_config0.resistor_p = NRF_SAADC_RESISTOR_DISABLED;
    channel_config0.resistor_n = NRF_SAADC_RESISTOR_DISABLED;

    channel_config1.reference = SAADC_CH_CONFIG_REFSEL_Internal; // 0.6V internal ref
    channel_config1.gain = NRF_SAADC_GAIN1_6;
    channel_config1.acq_time = NRF_SAADC_ACQTIME_40US;
    channel_config1.mode = NRF_SAADC_MODE_SINGLE_ENDED;
    channel_config1.pin_p = NRF_SAADC_INPUT_AIN2;
    channel_config1.pin_n = NRF_SAADC_INPUT_DISABLED;
    channel_config1.resistor_p = NRF_SAADC_RESISTOR_DISABLED;
    channel_config1.resistor_n = NRF_SAADC_RESISTOR_DISABLED;

    channel_config2.reference = SAADC_CH_CONFIG_REFSEL_Internal; // 0.6V internal ref
    channel_config2.gain = NRF_SAADC_GAIN1_6;
    channel_config2.acq_time = NRF_SAADC_ACQTIME_40US;
    channel_config2.mode = NRF_SAADC_MODE_SINGLE_ENDED;
    channel_config2.pin_p = NRF_SAADC_INPUT_AIN3;
    channel_config2.pin_n = NRF_SAADC_INPUT_DISABLED;
    channel_config2.resistor_p = NRF_SAADC_RESISTOR_DISABLED;
    channel_config2.resistor_n = NRF_SAADC_RESISTOR_DISABLED;

    channel_config3.reference = SAADC_CH_CONFIG_REFSEL_Internal; // 0.6V internal ref
    channel_config3.gain = NRF_SAADC_GAIN1_6;
    channel_config3.acq_time = NRF_SAADC_ACQTIME_40US;
    channel_config3.mode = NRF_SAADC_MODE_SINGLE_ENDED;
    channel_config3.pin_p = NRF_SAADC_INPUT_VDD;
    channel_config3.pin_n = NRF_SAADC_INPUT_DISABLED;
    channel_config3.resistor_p = NRF_SAADC_RESISTOR_DISABLED;
    channel_config3.resistor_n = NRF_SAADC_RESISTOR_DISABLED;

    err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(0, &channel_config0);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(1, &channel_config1);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(2, &channel_config2);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(3, &channel_config3);
    APP_ERROR_CHECK(err_code);

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

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

}

int main(void)
{

    // Start the ADCs
    saadc_init();

    // calibrate
    nrf_drv_saadc_abort();
    err_code = nrf_drv_saadc_calibrate_offset();
    nrf_delay_ms(10u);

    saadc_sampling_event_init();
    saadc_sampling_event_enable();
    
    while(no_samples < TOTAL_SAMPLES)
    {}
    
    no_samples = 0u;
    
    // Disable SAADC to save power
    nrf_drv_timer_disable(&m_timer);
    nrf_drv_timer_uninit(&m_timer);
    nrf_drv_ppi_channel_disable(m_ppi_channel);
    nrf_drv_ppi_uninit();
    nrf_drv_saadc_abort();
    nrf_drv_saadc_uninit();
    NVIC_ClearPendingIRQ(SAADC_IRQn);
    
    // sleep for 30s, return to main on wake

}

Could the issue be that the other inputs are floating when one is being sampled, or is there something wrong with my config and the way the data is sampled?

Parents
  • Thank you for your reply Kenneth. 

    Yes, I did mistakenly strip out the while loop where the code resides and the power management is called. I can confirm the chip does go to sleep after each set of 400 reads (4 channels, 100 samples). 

    I took a look at the code from the ticket you attached above and applied it to my program, particularly where the m_buffer_pool[0] is used in the conversion function, instead of converting the result buffer. I also set the SAADC into single buffering mode, as with a 10ms sampling interval and 400 samples I don't think single buffering would make an impact. 

    I also checked the original configuration without calibration and the same behavior was still occurring. 

    It would appear that the buffer conversion being done with only one channel connected to something and the rest floating was causing strange behavior, both for sampling and calibration. I tested your solution, and the original config with other all other inputs connected to ground and the values were all as expected. In short, don't leave other channels floating... simple really. 

    As an aside, the sensors these inputs are to be connected to have an impedance of 1MOhm. For the nRF52810, is this to be considered a *very* high impedance?

Reply
  • Thank you for your reply Kenneth. 

    Yes, I did mistakenly strip out the while loop where the code resides and the power management is called. I can confirm the chip does go to sleep after each set of 400 reads (4 channels, 100 samples). 

    I took a look at the code from the ticket you attached above and applied it to my program, particularly where the m_buffer_pool[0] is used in the conversion function, instead of converting the result buffer. I also set the SAADC into single buffering mode, as with a 10ms sampling interval and 400 samples I don't think single buffering would make an impact. 

    I also checked the original configuration without calibration and the same behavior was still occurring. 

    It would appear that the buffer conversion being done with only one channel connected to something and the rest floating was causing strange behavior, both for sampling and calibration. I tested your solution, and the original config with other all other inputs connected to ground and the values were all as expected. In short, don't leave other channels floating... simple really. 

    As an aside, the sensors these inputs are to be connected to have an impedance of 1MOhm. For the nRF52810, is this to be considered a *very* high impedance?

Children
Related