This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

nrf52840 SDK 16 - SAADC events

Hi everyone,

I try to understand when the SAADC events are generated and received by the IRQ based on the documentation. To my understanding:

NRF_SAADC_EVENT_END: is generated when the buffer is full (all the samples have been written to the memory). For example, if the buffer size is 0, the NRF_SAADC_EVENT_END event will be generated at the same rate as NRF_SAADC_EVENT_DONE events? However, when for instance the buffer size is 5, the NRF_SAADC_EVENT_END event will be generated after 5 x NRF_SAADC_EVENT_DONE events?

So the NRF_SAADC_EVENT_END (HAL layer) and the NRFX_SAADC_EVT_DONE (NRFX layer) serve the same purpose?

NRF_SAADC_EVENT_DONEis generated for every input sample taken (completed conversion). This is event is generated before the sampling value been copied to the RAM right?

I tried the saadc example of SDK16 to verify that events are generated as intended:

I have included the DONE event into the IRQ handler just to monitor when this event is received and I've enabled the debug logging.

void nrfx_saadc_irq_handler(void) {

  if (nrf_saadc_event_check(NRF_SAADC_EVENT_DONE)) {
    NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_SAADC_EVENT_DONE));
  }
  if (nrf_saadc_event_check(NRF_SAADC_EVENT_END)) {
    nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
    NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_SAADC_EVENT_END));
.......
.....
...
..
}

I didn't modify the example. I've just set the buffer size to 5 and the timer compare event every 1s. My problem is that I receive the NRF_SAADC_EVENT_DONE twice and the NRF_SAADC_EVENT_END after each sampling (as you can see from the logs below). So in total before the buffer gets full I receive 10 x DONE events and 5 x END events. I expected to receive the 5 x DONE events and 1 x END event.

<info> SAADC: Function: nrfx_saadc_init, error code: NRF_SUCCESS.
<info> SAADC: Channel initialized: 0.
<info> SAADC: Function: nrfx_saadc_channel_init, error code: NRF_SUCCESS.
<info> SAADC: Function: nrfx_saadc_buffer_convert, buffer length: 5, active channels: 1.
<info> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
<warning> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
<info> app: SAADC HAL simple example started.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<warning> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
<info> app: ADC event number: 0
<info> app: 4
<info> app: 3
<info> app: 3
<info> app: 4
<info> app: 3
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<warning> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
<info> app: ADC event number: 1
<info> app: 3
<info> app: 3
<info> app: 3
<info> app: 4
<info> app: 4
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<warning> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
<info> app: ADC event number: 2
<info> app: 3
<info> app: 3
<info> app: 4
<info> app: 4
<info> app: 3
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<warning> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
<info> app: ADC event number: 3
<info> app: 3
<info> app: 3
<info> app: 4
<info> app: 3
<info> app: 3
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
<debug> SAADC: Event: NRF_SAADC_EVENT_END.
<warning> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
<info> app: ADC event number: 4
<info> app: 4
<info> app: 3
<info> app: 4
<info> app: 4
<info> app: 3

The other problem is that after enabling oversampling I've got the same behavior (10 x DONE events and 5 x END events), while the documentation says that if the oversampling is enabled "END event is generated every 2OVERSAMPLE time the DONE event is generated"

Last but not least, the NRF_SAADC_INT_DONE interrupt isn't enable. How is possible that the IRQ interrupts on NRF_SAADC_EVENT_DONE event??

Could you confirm the following and guide me based on the saadc example of SDK16?

Thanks

Nick

  • Hi,

    It looks to me like yo have a buffer with room for 2 samples (so the driver set MAXCNT accordingly)? This is the default behavior of the SAADC example in the nRF5 SDK. If so, you will get a DONE event for each completed sampling, and a END event when the buffer is full.

    Regarding oversampling I wonder if perhaps you have not enabled oversampling even though that was the intention? Can you show your code?

  • Hi,

    It looks to me like yo have a buffer with room for 2 samples

    No I have set a buffer with size 5

    Regarding oversampling I wonder if perhaps you have not enabled oversampling even though that was the intention?

    I enable oversampling from the SDK (I use the nrfx implementation and I have removed all the nrf legacy definitions)

    // <o> NRFX_SAADC_CONFIG_OVERSAMPLE  - Sample period
     
    // <0=> Disabled 
    // <1=> 2x 
    // <2=> 4x 
    // <3=> 8x 
    // <4=> 16x 
    // <5=> 32x 
    // <6=> 64x 
    // <7=> 128x 
    // <8=> 256x 
    
    #ifndef NRFX_SAADC_CONFIG_OVERSAMPLE
    #define NRFX_SAADC_CONFIG_OVERSAMPLE 2
    #endif

    This is my code (that is more or less the saadc example in SDK16

    #include "app_error.h"
    #include "app_util_platform.h"
    #include "boards.h"
    #include "nrf.h"
    #include "nrf_delay.h"
    #include "nrfx_ppi.h"
    #include "nrfx_saadc.h"
    #include "nrfx_timer.h"
    #include "nrf_pwr_mgmt.h"
    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    
    
    #define SAMPLES_IN_BUFFER 5
    volatile uint8_t state = 1;
    
    static const nrfx_timer_t m_timer = NRFX_TIMER_INSTANCE(0);
    static nrf_saadc_value_t m_buffer_pool[2][SAMPLES_IN_BUFFER];
    static nrf_ppi_channel_t m_ppi_channel;
    static uint32_t m_adc_evt_counter;
    
    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 = nrfx_ppi_init();
      //APP_ERROR_CHECK(err_code);
    
      nrfx_timer_config_t timer_cfg = NRFX_TIMER_DEFAULT_CONFIG;
      timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
      err_code = nrfx_timer_init(&m_timer, &timer_cfg, timer_handler);
      APP_ERROR_CHECK(err_code);
    
      /* setup m_timer for compare event every 400ms */
      uint32_t ticks = nrfx_timer_ms_to_ticks(&m_timer, 1000);
      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_task_addr = nrfx_saadc_sample_task_get();
    
      /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
      err_code = nrfx_ppi_channel_alloc(&m_ppi_channel);
      APP_ERROR_CHECK(err_code);
    
      err_code = nrfx_ppi_channel_assign(m_ppi_channel,
          timer_compare_event_addr,
          saadc_sample_task_addr);
      APP_ERROR_CHECK(err_code);
    }
    
    void saadc_sampling_event_enable(void) {
      ret_code_t err_code = nrfx_ppi_channel_enable(m_ppi_channel);
    
      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;
    
        err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
    
        int i;
        NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);
    
        for (i = 0; i < SAMPLES_IN_BUFFER; i++) {
    
          NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
        }
        m_adc_evt_counter++;
      }
    }
    
    void saadc_init(void) {
      ret_code_t err_code;
    
      nrf_saadc_channel_config_t channel_config =
          NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0); //AIN0 is the pin P0.02 - https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpin.html
    
      err_code = nrfx_saadc_init(NULL, saadc_callback);
      APP_ERROR_CHECK(err_code);
    
      err_code = nrfx_saadc_channel_init(0, &channel_config);
      APP_ERROR_CHECK(err_code);
    
      err_code = nrfx_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
      APP_ERROR_CHECK(err_code);
    
      err_code = nrfx_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
      APP_ERROR_CHECK(err_code);
    }
    
    /**
     * @brief Function for main application entry.
     */
    int main(void) {
      uint32_t err_code = NRF_LOG_INIT(NULL);
      APP_ERROR_CHECK(err_code);
    
      NRF_LOG_DEFAULT_BACKENDS_INIT();
    
      ret_code_t ret_code = nrf_pwr_mgmt_init();
      APP_ERROR_CHECK(ret_code);
    
      saadc_init();
      saadc_sampling_event_init();
      saadc_sampling_event_enable();
      NRF_LOG_INFO("SAADC HAL simple example started.");
    
      
    
      while (1) {
        nrf_pwr_mgmt_run();
        NRF_LOG_FLUSH();
      }
    }

  • Hi,

    You will get an assert in nrfx_saadc_init() on line 214 in nrfx_saadc.c with this code in SDK 16 (and also any later nRF5 SDK version), as providing NULL to nrfx_saadc_init() as the first parameter is not legal. As you have not commented on this I assume this is either not the code you test, or that you have made some changes to the nrfx_saadc driver?

    As you do not populate the config this means that NRFX_SAADC_CONFIG_OVERSAMPLE is also never used, so no wonder it has no effect. I cannot say exactly what happens in your end though, as the code you have here clearly will not work.

    I modified your saadc_init() so that it looked like this, and with that it works. Also oversampling works as expected:

    void saadc_init(void) {
      ret_code_t err_code;
    
      nrf_saadc_channel_config_t channel_config =
          NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0); //AIN0 is the pin P0.02 - https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpin.html
    
      nrfx_saadc_config_t config = {
        .resolution = NRFX_SAADC_CONFIG_RESOLUTION,
        .oversample = NRFX_SAADC_CONFIG_OVERSAMPLE,
        .interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY,
        .low_power_mode = NRFX_SAADC_CONFIG_LP_MODE 
      };
    
      err_code = nrfx_saadc_init(&config, saadc_callback);
      APP_ERROR_CHECK(err_code);
    
      err_code = nrfx_saadc_channel_init(0, &channel_config);
      APP_ERROR_CHECK(err_code);
    
      err_code = nrfx_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
      APP_ERROR_CHECK(err_code);
    
      err_code = nrfx_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
      APP_ERROR_CHECK(err_code);
    }

  • Hi Einar,

    You will get an assert in nrfx_saadc_init() on line 214 in nrfx_saadc.c with this code in SDK 16 (and also any later nRF5 SDK version), as providing NULL to nrfx_saadc_init() as the first parameter is not legal.

    Sorry for the confusion, I didn't notice that the nrfx driver does not handle the NULL parameter, while the legacy driver does.

    I downloaded the SDK16 again just to ensure that there are no changes to the example.

    I have included just an if statement in the IRQ function to check for the DONE event

    void nrfx_saadc_irq_handler(void) {
    
      if (nrf_saadc_event_check(NRF_SAADC_EVENT_DONE)) {
        NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_SAADC_EVENT_DONE));
      }
    .....
    ...
    ..
    .
    }

    This is the log file

    <info> SAADC: Function: nrfx_saadc_init, error code: NRF_SUCCESS.
    <info> SAADC: Channel initialized: 0.
    <info> SAADC: Function: nrfx_saadc_channel_init, error code: NRF_SUCCESS.
    <info> SAADC: Function: nrfx_saadc_buffer_convert, buffer length: 5, active channels: 1.
    <info> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
    <warning> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
    <info> app: SAADC HAL simple example started.
    <debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
    <debug> SAADC: Event: NRF_SAADC_EVENT_END.
    <warning> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
    <info> app: ADC event number: 0
    <info> app: 18
    <info> app: 14
    <info> app: 16
    <info> app: 15
    <info> app: 14
    <debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
    <debug> SAADC: Event: NRF_SAADC_EVENT_END.
    <warning> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
    <info> app: ADC event number: 1
    <info> app: 15
    <info> app: 15
    <info> app: 15
    <info> app: 15
    <info> app: 14
    <debug> SAADC: Event: NRF_SAADC_EVENT_DONE.
    <debug> SAADC: Event: NRF_SAADC_EVENT_END.
    <warning> SAADC: Function: nrfx_saadc_buffer_convert, error code: NRF_SUCCESS.
    <info> app: ADC event number: 2
    <info> app: 15
    <info> app: 15
    <info> app: 13
    <info> app: 15
    <info> app: 13

    The END event is generated as it is expected (when the buffer is full). However, the DONE event is generated when the buffer is full as well...

    I have two questions:

    1. Why the DONE event is generated without the NRF_SAADC_INT_DONE interrupt being enabled?

    2. Why the DONE event is generated when the buffer is full? I expected the DONE event to be generated after each sample is taken.

  • Hi,

    Nikosant03 said:
    1. Why the DONE event is generated without the NRF_SAADC_INT_DONE interrupt being enabled?

    Events are something hat happens regardless if an interrupt is configured for it or not. To get an interrupt, that also needs to be enabled. This is general in the nRF, and not just related to the SAADC. See Peripheral interface. For this event in the SAADC specifically, that means that if you get this event in the CPU you have configured an interrupt for it, by setting the DONE field in INTEN to 1.Note that when you get the interrupt the SAADC driver checks all events (see implementation of nrfx_saadc_irq_handler() in modules\nrfx\drivers\src\nrfx_saadc.c). So if an event has occurred before, and then you get an new event for for which you have also enabled interrupts, both will be handled (and with debug logging enabled, you will get both printed). That is what is the case here, where you have interrupts enabled for the END event.

    Nikosant03 said:
    2. Why the DONE event is generated when the buffer is full? I expected the DONE event to be generated after each sample is taken.

    The DONE event is generated for every input sample, and the END event is generated when the buffer is full. But as you have not configured interrupts for the DONE event you will not see every time it is set.

Related