SAADC EVENTS_END after EVENTS_STOPPED?

Hi all, 

I read in some thread (that I am unable to find now), that when stopping the SAADC via STOP Task, the STOPPED event fires (which is expected), however the END event might also fire unexpectedly after that if the ADC was busy sampling while receiving the STOP Task. Questions:

  1. Can someone confirm this please?
  2. Is it a documented Anomaly? Is it related to this Anomaly [178]: SAADC: END event firing too early?
  3. On which devices can it occur? (mainly interested in nrf52840)
  4. Any known workarounds?

My intention: I need to know if it's possible to occur on my devices, so I need to implement and test the workaround. Otherwise, I might just skip it if it's not relevant. 

Thank you!

Parents
  • I do not see anywhere the link between STOPPED and END event right after someone have triggered the STOP task. The 178 Anamoly is the END event comes early than expected and is not connected to the STOP task. If you can show me the source of the information you got, then I can try to dig a bit deeper.

  • Well, I moved to the implementation and I did face the issue indeed with my nRF52840-DK.

    Here is a snapshot of my logs coming from my SAADC ISR Handler:

    I am expecting an END event with roughly 200ms interval, which is the case. Then, I trigger the TASK_STOP of the ADC and I expect the STOPPED event to occur. This is also the case (the last line -1). A few microseconds later, the END event also occurs (last line)

    static void saadc_handler(void)
    {
        if (NRF_SAADC->EVENTS_STOPPED) {
            NRF_SAADC->EVENTS_STOPPED = 0;
    
            full_buffer_idx ^= 1;
    
            data_item_type data;
            data.timestamp = unixtime_get(NULL);
            LOG_WRN("ADC STOP, COUNT=%d    TS: %lld", NRF_SAADC->RESULT.AMOUNT, data.timestamp % 1000000);
    
            k_msgq_put(&my_msgq, &data, K_NO_WAIT);
        }
    
        if (NRF_SAADC->EVENTS_END) {
            NRF_SAADC->EVENTS_END = 0;
    
            full_buffer_idx ^= 1;
    
            data_item_type data;
            data.timestamp = unixtime_get(NULL);
            LOG_WRN("ADC END , COUNT=%d    TS: %lld", NRF_SAADC->RESULT.AMOUNT, data.timestamp % 1000000);
    
            k_msgq_put(&my_msgq, &data, K_NO_WAIT);
        }
    
        if (NRF_SAADC->EVENTS_STARTED) {
            NRF_SAADC->EVENTS_STARTED = 0;
    
            //toggle buffer index and update sample buffer for next adc start
            next_buffer_idx ^= 1;
            nrf_saadc_buffer_pointer_set(NRF_SAADC, (nrf_saadc_value_t *) m_sample_buffer[next_buffer_idx]);
        }
    }
    Here is my handler:

    I will try to find the thread, however, it is now obvious, that it happens also in my case.

    So, the main question remains: is it documented somewhere and how to solve it / workaround it?

    Update:

    I did find out, where I read this information: It is in nrfx_saadc.c driver.

    --> Follow up question: what is meant by "ongoing conversion": does it mean that ADC is actively doing a conversion, or the buffer is still not full (RESULT.AMOUNT < RESULT.MAXCNT)?

  • Hi Ianwer,

    Sorry for the late response. It is correct that the STOP task will also trigger an END event if the STOP task is triggered during an ongoing transaction. That brings to your second question

    lanwer said:
    what is meant by "ongoing conversion": does it mean that ADC is actively doing a conversion, or the buffer is still not full (RESULT.AMOUNT < RESULT.MAXCNT)?

    The "ongoing conversation" that a SAMPLE task has been triggered but an event is not generated saying that the EasyDMA is done with the transaction/transfer.

  • For future reference:

    I simplified my test setup because I consistently observed that EVENT_END occurs after triggering the STOP task and receiving EVENT_STOPPED when RESULT.AMOUNT < RESULT.MAXCNT. To isolate the behavior, I removed all timing dependencies (timers and PPI channels) and reduced the setup to the following minimal example:

    // Init ADC
    sensor_adc_init();
    
    // Start the test sequence
    LOG_ERR("TEST START");
    
    LOG_WRN("ADC START & SLEEP 1s");
    nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_START);
    k_sleep(K_MSEC(1000));
    
    LOG_WRN("ADC SAMPLE & SLEEP 1s");
    nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE);
    k_sleep(K_MSEC(1000));  
    
    LOG_WRN("ADC STOP & SLEEP 1s");
    nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_STOP);
    k_sleep(K_MSEC(1000));
    
    LOG_ERR("TEST END");

    This is the my saadc handler implementation:

    static void saadc_handler(void)
    {
        if (NRF_SAADC->EVENTS_STOPPED) {
            NRF_SAADC->EVENTS_STOPPED = 0;
    
            // If there was ongoing conversion the STOP task also triggers the END event
            /* fall-through to the END event handler */
    
            LOG_WRN("-> ADC STOPPED_EVENT, COUNT=%d, MAX_COUNT=%d", NRF_SAADC->RESULT.AMOUNT, NRF_SAADC->RESULT.MAXCNT);
        }
    
        if (NRF_SAADC->EVENTS_END) {
            NRF_SAADC->EVENTS_END = 0;
            
            LOG_WRN("-> ADC END_EVENT, COUNT=%d, MAX_COUNT=%d", NRF_SAADC->RESULT.AMOUNT, NRF_SAADC->RESULT.MAXCNT);
        }
    
        if (NRF_SAADC->EVENTS_STARTED) {
            NRF_SAADC->EVENTS_STARTED = 0;
            
            LOG_WRN("-> ADC STARTED_EVENT, COUNT=%d, MAX_COUNT=%d", NRF_SAADC->RESULT.AMOUNT, NRF_SAADC->RESULT.MAXCNT);
        }
    }

    And this is the corresponding output:

    Conclusion: 

    With this simple setup and by removing all timing dependencies, I am fairly confident that the adc does always generate an END_EVENT after the STOPPED_EVENT when the SAADC is stopped with RESULT.AMOUNT < RESULT.MAXCNT, even if the ADC was not actively converting at the time the STOP task was triggered. I tried to rule out stopping during an active conversion by waiting one second after triggering the SAMPLE task before triggering the STOP task.

Reply
  • For future reference:

    I simplified my test setup because I consistently observed that EVENT_END occurs after triggering the STOP task and receiving EVENT_STOPPED when RESULT.AMOUNT < RESULT.MAXCNT. To isolate the behavior, I removed all timing dependencies (timers and PPI channels) and reduced the setup to the following minimal example:

    // Init ADC
    sensor_adc_init();
    
    // Start the test sequence
    LOG_ERR("TEST START");
    
    LOG_WRN("ADC START & SLEEP 1s");
    nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_START);
    k_sleep(K_MSEC(1000));
    
    LOG_WRN("ADC SAMPLE & SLEEP 1s");
    nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE);
    k_sleep(K_MSEC(1000));  
    
    LOG_WRN("ADC STOP & SLEEP 1s");
    nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_STOP);
    k_sleep(K_MSEC(1000));
    
    LOG_ERR("TEST END");

    This is the my saadc handler implementation:

    static void saadc_handler(void)
    {
        if (NRF_SAADC->EVENTS_STOPPED) {
            NRF_SAADC->EVENTS_STOPPED = 0;
    
            // If there was ongoing conversion the STOP task also triggers the END event
            /* fall-through to the END event handler */
    
            LOG_WRN("-> ADC STOPPED_EVENT, COUNT=%d, MAX_COUNT=%d", NRF_SAADC->RESULT.AMOUNT, NRF_SAADC->RESULT.MAXCNT);
        }
    
        if (NRF_SAADC->EVENTS_END) {
            NRF_SAADC->EVENTS_END = 0;
            
            LOG_WRN("-> ADC END_EVENT, COUNT=%d, MAX_COUNT=%d", NRF_SAADC->RESULT.AMOUNT, NRF_SAADC->RESULT.MAXCNT);
        }
    
        if (NRF_SAADC->EVENTS_STARTED) {
            NRF_SAADC->EVENTS_STARTED = 0;
            
            LOG_WRN("-> ADC STARTED_EVENT, COUNT=%d, MAX_COUNT=%d", NRF_SAADC->RESULT.AMOUNT, NRF_SAADC->RESULT.MAXCNT);
        }
    }

    And this is the corresponding output:

    Conclusion: 

    With this simple setup and by removing all timing dependencies, I am fairly confident that the adc does always generate an END_EVENT after the STOPPED_EVENT when the SAADC is stopped with RESULT.AMOUNT < RESULT.MAXCNT, even if the ADC was not actively converting at the time the STOP task was triggered. I tried to rule out stopping during an active conversion by waiting one second after triggering the SAMPLE task before triggering the STOP task.

Children
No Data
Related