Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SAADC V2 sampling using the RTC

Hi there,

I'm having some trouble piecing together an app to use V2 of the NRFX SAADC API, compounded with this being my first time using the SAADC functionality on an nRF board.

In summary, my program receives an "ERROR 8 [NRF_ERROR_INVALID_STATE]" when calling the nrfx_saadc_mode_trigger function.

My understanding is that we first initialise SAADC:

static void saadc_init()
{
    ret_code_t err_code;

    nrfx_saadc_adv_config_t config = {
        .oversampling = NRF_SAADC_OVERSAMPLE_DISABLED,
        .burst = false,
        .internal_timer_cc = 0,
        .start_on_end = false,
    };

    err_code = nrfx_saadc_init(NRFX_SAADC_CONFIG_IRQ_PRIORITY);
    APP_ERROR_CHECK(err_code);
    err_code = nrfx_saadc_channels_config(m_accel, 3);
    APP_ERROR_CHECK(err_code);

    err_code = nrfx_saadc_advanced_mode_set((1 << 0) | (1 << 1) | (1 << 2),
                                            NRF_SAADC_RESOLUTION_8BIT,
                                            &config,
                                            on_saadc_in_evt);
    APP_ERROR_CHECK(err_code);

    err_code = nrfx_saadc_buffer_set(m_accel_sample_bufs[m_accel_current_buffer], ACCEL_SAMPLES_PER_BUFFER);
    APP_ERROR_CHECK(err_code);
}

My intention is to use the RTC to sample three analogue pins previously defined as:

nrfx_saadc_channel_t m_accel[] = {
    NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN5, 0),
    NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN6, 1),
    NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN7, 2)};

A timer is created and, when it fires, it then triggers the sampling:

static void accel_sampler_timer(void *p_context)
{
    ret_code_t err_code = nrfx_saadc_mode_trigger();
    APP_ERROR_CHECK(err_code);
}

...and that's when the invalid state is reported.

The SAADC event handler is as follows:

static void on_saadc_in_evt(nrfx_saadc_evt_t const *p_event)
{
    ret_code_t err_code;

    switch (p_event->type)
    {
    case NRFX_SAADC_EVT_DONE:
        // TODO: Use p_event->data.done.p_buffer[0];
        break;
    case NRFX_SAADC_EVT_BUF_REQ:
        m_accel_current_buffer = 1 - m_accel_current_buffer;
        err_code = nrfx_saadc_buffer_set(m_accel_sample_bufs[m_accel_current_buffer], ACCEL_SAMPLES_PER_BUFFER);
        APP_ERROR_CHECK(err_code);
        break;
    default:
        break;
    }
}

Am I under the false impression that the timer should trigger the sampling?

Thanks for your guidance.

Parents
  • So if I ignore the invalid state then during the timer handler, I'm not yet able to see any sampling come through i.e. I don't see an NRFX_SAADC_EVT_DONE raised. Anything else obviously missing from my setup?

    Related: Should I avoid the V2 API given the lack of a good example to guide me? As I'm developing a new device, I'd like to adopt the most contemporary APIs, but perhaps I'm jumping the gun on this one...

    Thanks.

  • Hello Christopher,

    Christopher Hunt said:
    Thanks for the reply, Karl.

    No problem at all, I am happy to help!

    Christopher Hunt said:
    Gosh, I missed that description of the return code having even inspected the source! So, I should ignore the invalid state code perhaps during my timer event, as it is quite legitimate to be in this invalid state?

    I am not sure that I understand exactly what you mean by this, but if this error code is returned then that means the peripheral indeed is in an invalid state for beginning sampling. If you just ignore this error, then you will "lose" this sample ( i.e no sample will be taken at this time, causing a gap in your sampling ).

    Christopher Hunt said:
    So if I ignore the invalid state then during the timer handler, I'm not yet able to see any sampling come through i.e. I don't see an NRFX_SAADC_EVT_DONE raised.

    I would say this is as expected - if your buffer size is 3, and you attempt to do 3 calls to _sample, with 1 or more returning an error code != NRF_SUCCESS, then the buffer will not fill, and the DONE event thus will not be generated.

    Christopher Hunt said:
    The timer is firing once per 100ms i.e. 10 times per second.

    Thank you for clarifying.
    Could you show me the code in which you setup and start this timer? I just want to rule out that this in fact is happening at a different interval.

    Christopher Hunt said:
    Related: Should I avoid the V2 API given the lack of a good example to guide me? As I'm developing a new device, I'd like to adopt the most contemporary APIs, but perhaps I'm jumping the gun on this one...

    While not in the SDK, there exists some short examples to demonstrate the usage of the V2 driver, please see the SAADC section of the nrfx changelog for this. In this case, the Simple mode with IRQ example might be especially useful to take a look at.
    It is hard for me to advise on whether you could stick to the old SAADC driver, or should make the change to V2 since I do not know about the specifics of your project, but I definitely recommend getting familiar with the V2, as it is a improvement of the old driver. You might also want to consider having your sampling trigger by PPI, instead of by CPU intervention - especially if it is important that sampling is not interrupted or delayed due to the SoftDevice controlling the CPU.

    Please do not hesitate to ask if anything should be unclear, or if you should encounter any issues or questions! :) 

    Best regards,
    Karl

  • Just digesting the other bits, but here's my timer code. I can confirm that the timer is firing.

    Meanwhile, to declare the timer:

    APP_TIMER_DEF(m_hand_sanitizer_accel_sampler_timer_id);
    

    To create the timer:

        err_code = app_timer_create(&m_hand_sanitizer_accel_sampler_timer_id, APP_TIMER_MODE_REPEATED, hand_sanitizer_accel_sampler_timer);
        APP_ERROR_CHECK(err_code);

    To enable the timer:

        err_code = app_timer_start(m_hand_sanitizer_accel_sampler_timer_id, APP_TIMER_TICKS(ACCEL_SAMPLER_RESOLUTION_MS), NULL);
        APP_ERROR_CHECK(err_code);

    To handler the timer:

    static void hand_sanitizer_accel_sampler_timer(void *p_context)
    {
        nrfx_saadc_mode_trigger();
    }
    

Reply
  • Just digesting the other bits, but here's my timer code. I can confirm that the timer is firing.

    Meanwhile, to declare the timer:

    APP_TIMER_DEF(m_hand_sanitizer_accel_sampler_timer_id);
    

    To create the timer:

        err_code = app_timer_create(&m_hand_sanitizer_accel_sampler_timer_id, APP_TIMER_MODE_REPEATED, hand_sanitizer_accel_sampler_timer);
        APP_ERROR_CHECK(err_code);

    To enable the timer:

        err_code = app_timer_start(m_hand_sanitizer_accel_sampler_timer_id, APP_TIMER_TICKS(ACCEL_SAMPLER_RESOLUTION_MS), NULL);
        APP_ERROR_CHECK(err_code);

    To handler the timer:

    static void hand_sanitizer_accel_sampler_timer(void *p_context)
    {
        nrfx_saadc_mode_trigger();
    }
    

Children
No Data
Related