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

Second init of saadc peripheral does not work correctly

We are switching between two modes of ADC sampling in our application:

  • single shot - Consists of a routine -> Init saadc -> convert a sample of one ADC channel -> uninit saadc, this is used at longer intervals, when we want to conserve power in the device's off mode.
  • scan mode - We init scan mode once, values are converted in saadc callback (copied from nrf5 sdk example) and saved into an internal buffer, where they are filtered and fetched when an application needs them. We use scan mode in on mode when we do not care about power and want fast sampling, we are sampling 4 channels every 10 ms.

As our device can go from off to on mode and back we need to be able to uninit scan mode, and after some time init it back again when the device is eventually turned on again.

The problem appears after second init procedure of scan mode, afterward the reported count values that we receive from adc call back are wrong, they are all around 3010 counts (3009, 3010, 3011, ..) and they change from sample to sample but not much.

I am guessing that we are using API in some incorrect way and that we are either not uninting or reuniting it correctly. Keep in mind that single shots after we uninited scan mode work correctly.

Here is a code snippet where we are init the scan mode:

void drv_adc_scan_init(nrfx_saadc_event_handler_t adc_event_handler)
{
    if (prv_adc_scan_disabled) {
        drv_adc_scan_enable();
        prv_adc_scan_disabled = false;
        return;
    }

    ret_code_t err_code;

    /* Set sample rate*/
    prv_set_sample_period(BOARD_ADC_SAMPLING_PERIOD_MS);

    /* Set general saadc config, resolution is 12 bits, we also set 
     * user defined event handler. */
    nrfx_saadc_config_t saadc_config = NRFX_SAADC_DEFAULT_CONFIG;
    saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;
    saadc_config.oversample = NRF_SAADC_OVERSAMPLE_64X;
    err_code = nrfx_saadc_init(&saadc_config, adc_event_handler);
    APP_ERROR_CHECK(err_code);

    nrf_saadc_channel_config_t channel_config;

    for (int i = 0; i < board_get_adc_size(); i++) {

        board_adc_t adc_channel = board_get_adc(i);

        if (UNUSED_FIELD == adc_channel.pin) {
            continue;
        }

        channel_config = prv_create_ch_conf(adc_channel.analog_pin,
                                            adc_channel.gain);
        err_code = nrfx_saadc_channel_init(i, &channel_config);
        APP_ERROR_CHECK(err_code);
    }
    /* Clean buffers before using them */
    /* For some reason below buffer has to be static, otherwise later 
     * counts won't be correct. */
    for (uint8_t i = 0; i < 2; i++) {
        err_code = nrfx_saadc_buffer_convert(prv_scanned_adc_counts,
                                             BOARD_NUM_ACTIVE_ADC_CHANNELS);
        APP_ERROR_CHECK(err_code);
    }

    prv_adc_scan_initialised = true;
    NRFX_ASSERT(err_code == NRF_SUCCESS);
}

And here we uninit the scan mode, we tried several things, with below snippet we do not get any asserts errors form the sdk

void drv_adc_uninit1(void)
{
    nrfx_saadc_abort();
    for (int i = 0; i < board_get_adc_size(); i++) {

        board_adc_t adc_channel = board_get_adc(i);

        if (UNUSED_FIELD == adc_channel.pin) {
            continue;
        }
        APP_ERROR_CHECK(nrfx_saadc_channel_uninit(i));
    }
    nrfx_saadc_uninit();
    prv_disable_ppi();
    APP_ERROR_CHECK(nrfx_ppi_channel_free(prv_ppi_adc_channel));
    nrfx_timer_disable(&prv_adc_timer);
    nrfx_timer_uninit(&prv_adc_timer);

    prv_adc_scan_initialised = false;
}

Here are our helper functions which eventually call nordic API:

static void prv_enable_ppi(void)
{
    nrf_drv_ppi_init();
}

static void prv_disable_ppi(void)
{
    APP_ERROR_CHECK(nrf_drv_ppi_uninit());
}

/* Just a stub function */
static void prv_timer_handler(nrf_timer_event_t event_type, void* p_context) 
{ 
}


static void prv_config_adc_timer(uint32_t sample_period_ms)
{
    ret_code_t err_code;
    nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG;
    timer_config.frequency = NRF_TIMER_FREQ_31250Hz;
    err_code = nrfx_timer_init(&prv_adc_timer, &timer_config, prv_timer_handler);
    APP_ERROR_CHECK(err_code);

    /* setup adc_timer for compare event */
    uint32_t ticks = nrfx_timer_ms_to_ticks(&prv_adc_timer, sample_period_ms);
    nrfx_timer_extended_compare(&prv_adc_timer, 
            NRF_TIMER_CC_CHANNEL0, 
            ticks, 
            NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, 
            false);
    nrfx_timer_enable(&prv_adc_timer);
}

static void prv_ppi_connect(uint32_t epp, uint32_t tep)
{
    ret_code_t err_code;
    err_code = nrfx_ppi_channel_alloc(&prv_ppi_adc_channel);
    APP_ERROR_CHECK(err_code);

    err_code = nrfx_ppi_channel_assign(prv_ppi_adc_channel, epp, tep);
    APP_ERROR_CHECK(err_code);
}

I am interested in the function call nrfx_saadc_buffer_convert, I noticed before that the given array needs to be declared static, otherwise the samples are not correct. Why does it need to be static and could this be connected with the described problem?

Related