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?