Hello,
Project Description
in our project we have multiple nRF52DK and one nRF52840DK. We are using the SAADC on the nRF52DK. The SAADC is configured with 4 inputs in differential mode. The sample rate is 1 kHz (1 ms) and the resolution is 10 Bit. The samples get transmitted to the nRF52840DK via NUS. The nRF52840DK prints the received values into the virtual COM port over USB for further processing on the PC.
Test Scenario
A few days ago we tested this setup. We used a function generator and applied a 1.6 V_pp Sinuswave with a frequency of 20 Hz to all 4 differential inputs of the SAADC. So we expected to get identical results for all four inputs/channels of the SAADC.
Test Results
The picture below show the results of this test. The event/trigger signal at the bottom of the graph is not relevant. As you can see, the SAADC channels/inputs 1, 2 and 3 are equal and fine. When you take a look to channel 4 you can see that this channel 4 has a constant 1 ms delay compared to channel 1,2 and 3. The black vertical marker shows this well.

This is not the result what we expected.
SAADC configuration
The following code snippets show how the SAADC is configured and used:
#define SAADC_SAMPLES_IN_BUFFER 4
#define TRANSMISSION_OFFSET 512
static nrf_saadc_value_t m_buffer_pool[2][SAADC_SAMPLES_IN_BUFFER];
static nrf_ppi_channel_t m_ppi_channel;
static const nrfx_timer_t m_timer = NRFX_TIMER_INSTANCE(1);
static uint16_t adc_value_ch1;
static uint16_t adc_value_ch2;
static uint16_t adc_value_ch3;
static uint16_t adc_value_ch4;
void timer_handler(nrf_timer_event_t event_type, void* p_context)
{
}
void saadc_sampling_event_init(void)
{
ret_code_t err_code;
err_code = nrf_drv_ppi_init();
APP_ERROR_CHECK(err_code);
nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG;
timer_config.frequency = NRF_TIMER_FREQ_16MHz;
err_code = nrfx_timer_init(&m_timer, &timer_config, timer_handler);
APP_ERROR_CHECK(err_code);
/* setup m_timer for compare event */
uint32_t ticks = nrfx_timer_ms_to_ticks(&m_timer,1);
nrfx_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
nrfx_timer_enable(&m_timer);
uint32_t timer_compare_event_addr = nrfx_timer_compare_event_address_get(&m_timer, NRF_TIMER_CC_CHANNEL0);
uint32_t saadc_sample_event_addr = nrfx_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_event_addr);
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;
nrfx_saadc_config_t saadc_config = NRFX_SAADC_DEFAULT_CONFIG;
saadc_config.resolution = NRF_SAADC_RESOLUTION_10BIT;
nrf_saadc_channel_config_t channel_0_config =
NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN4,NRF_SAADC_INPUT_AIN0);
channel_0_config.gain = NRF_SAADC_GAIN1_2;
channel_0_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
nrf_saadc_channel_config_t channel_1_config =
NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN5,NRF_SAADC_INPUT_AIN0);
channel_1_config.gain = NRF_SAADC_GAIN1_2;
channel_1_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
nrf_saadc_channel_config_t channel_2_config =
NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN6,NRF_SAADC_INPUT_AIN0);
channel_2_config.gain = NRF_SAADC_GAIN1_2;
channel_2_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
nrf_saadc_channel_config_t channel_3_config =
NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN7,NRF_SAADC_INPUT_AIN0);
channel_3_config.gain = NRF_SAADC_GAIN1_2;
channel_3_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
err_code = nrfx_saadc_init(&saadc_config, saadc_callback);
APP_ERROR_CHECK(err_code);
err_code = nrfx_saadc_channel_init(0, &channel_0_config);
APP_ERROR_CHECK(err_code);
err_code = nrfx_saadc_channel_init(1, &channel_1_config);
APP_ERROR_CHECK(err_code);
err_code = nrfx_saadc_channel_init(2, &channel_2_config);
APP_ERROR_CHECK(err_code);
err_code = nrfx_saadc_channel_init(3, &channel_3_config);
APP_ERROR_CHECK(err_code);
err_code = nrfx_saadc_buffer_convert(m_buffer_pool[0],SAADC_SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
err_code = nrfx_saadc_buffer_convert(m_buffer_pool[1],SAADC_SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
}
void saadc_callback(nrfx_saadc_evt_t const * p_event)
{
if (p_event->type == NRFX_SAADC_EVT_DONE)
{
ret_code_t err_code;
// set buffers
err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, SAADC_SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
adc_value_ch1 = p_event->data.done.p_buffer[0] + TRANSMISSION_OFFSET;
adc_value_ch2 = p_event->data.done.p_buffer[1] + TRANSMISSION_OFFSET;
adc_value_ch3 = p_event->data.done.p_buffer[2] + TRANSMISSION_OFFSET;
adc_value_ch4 = p_event->data.done.p_buffer[3] + TRANSMISSION_OFFSET;
// send the adc_values every 40ms via NUS
...
}
...
}
int main(void)
{
...
saadc_sampling_event_init();
saadc_init();
saadc_sampling_event_enable();
...
}
Please let me know if you need any further information.
Own Findings
When I use defined values (which I increase every 1 second for example) for the adc_value_ch variables:
adc_value_ch1 = defined_value + TRANSMISSION_OFFSET; adc_value_ch2 = defined_value + TRANSMISSION_OFFSET; adc_value_ch3 = defined_value + TRANSMISSION_OFFSET; adc_value_ch4 = defined_value + TRANSMISSION_OFFSET;
instead of:
adc_value_ch1 = p_event->data.done.p_buffer[0] + TRANSMISSION_OFFSET;
adc_value_ch2 = p_event->data.done.p_buffer[1] + TRANSMISSION_OFFSET;
adc_value_ch3 = p_event->data.done.p_buffer[2] + TRANSMISSION_OFFSET;
adc_value_ch4 = p_event->data.done.p_buffer[3] + TRANSMISSION_OFFSET;
the 1 ms offset of the channel 4 is gone. Maybe there is an issue when collection the samples from the buffer when calling "p_event->data.done.p_buffer[...]".
Questions
1. Do you have any idea what could cause this behaviour?
2. Is there a systematic error in using the SAADC?
3. Can you provide us some help, how to solve this issue?
Thank you very much in advance.
Best regards,
Michael