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

SAADC scan + burst?

On SDK16.0.0 + Mesh SDK 4.1.0

I know that oversampling when using the SADDC in scan mode on multiple channels does not work because the buffer gets all out of order.

But I believe burst mode can be used in scan mode where it samples the channel multiple times as fast as possible, averages it, and then puts the result in the buffer in the right order. Burst mode uses the oversample setting for setting how many readings to average. Is this all correct?

Does this work in SDK16.0.0 by simply setting nrf_drv_saadc_config_t .oversample value and nrf_saadc_channel_config_t .burst value? I did this and everything seems to be working, but I don't know if it's actually doing it. I initially tried to use nrf_saadc_burst_set(channel, NRF_SAADC_BURST_ENABLED) to enable burst for each channel, but that did not work and the readings were all wrong.

Or do some modifications need to be made like in this thread? https://devzone.nordicsemi.com/f/nordic-q-a/26659/saacd-scan-oversample/

Thanks.

  • ftjandra said:
    I am now successfully triggering the sample via PPI.

    I am glad to hear that, well done!

    ftjandra said:
    lets say every 1000 samples?

    Is there a particular reason for why you would like a calibration to be done every 1000 samples? Is the temperature changing during this time?

    ftjandra said:
    What is now the correct process to trigger a calibration

    Which nRF52840 SoC revision are you working with?
    As I mentioned before, calibration should be done when you enable the SAADC and when the temperature has changed significantly.
    You will also need to make sure that CTASKS_ALIBRATEOFFSET is never called between TASKS_START and EVENT_END, due to Errata 237.
    So, to calibrate every 1000 sample you will need to make a counter that keeps track of the sampled number, and when it reaches 1000 samples the START task PPI timer is stopped, and when the END event is generated calibration can start.

    Best regards,
    Karl

  • ftjandra said:
    Also, when does saadc_timer_handler() get called. In the example it was empty and I see that it never gets called.

    I did not see this question when writing my last post, sorry.
    The timer handler can be empty since the CC event is directly tied to the SAADC START task through PPI, which is all it is meant to do.

    Best regards,
    Karl

  • My nRF52840 is rev2. Eventually I will trigger calibration on temperature change, but for testing it's easier to just do it every xx samples.

    I modified the saadc_event_handler() as follows:

    static void saadc_event_handler(nrf_drv_saadc_evt_t const * p_event) {
        static uint16_t saadc_event_counter = 1;
    
        if(p_event->type == NRF_DRV_SAADC_EVT_DONE) {
            //Read buffer
            for(int i=0; i<SAADC_SAMPLES_IN_BUFFER; i++) {
                saadc_value_current[i] = p_event->data.done.p_buffer[i];
            }
            
            if((saadc_event_counter % SAADC_CALIBRATION_INTERVAL) == 0) {
                saadc_event_counter = 0;    //reset
                nrf_drv_timer_disable(&m_saadc_timer);
                nrf_drv_saadc_abort();
                while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //trigger calibration task
            }
            else {
                //Set buffer, double-buffering
                ERROR_CHECK(nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAADC_SAMPLES_IN_BUFFER));
            
                //Print samples
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "\nsaadc_value_current[0]: %u\nsaadc_value_current[1]: %u\nsaadc_value_current[2] %u\nsaadc_value_current[3]: %u\n", saadc_value_current[0], saadc_value_current[1], saadc_value_current[2], saadc_value_current[3]);
            }
            
            saadc_event_counter++;
        }
        else if(p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE) {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "SAADC calibration complete\n");
    
            //Need to setup both buffers, as they were both removed with the call to nrf_drv_saadc_abort before calibration
            ERROR_CHECK(nrf_drv_saadc_buffer_convert(saadc_buffer_pool[0], SAADC_SAMPLES_IN_BUFFER));
            ERROR_CHECK(nrf_drv_saadc_buffer_convert(saadc_buffer_pool[1], SAADC_SAMPLES_IN_BUFFER));
    
            nrf_drv_timer_enable(&m_saadc_timer);
        }
    }

    Do I need to disable the timer, ppi channel, both? Seems like disabling the timer should be enough. I also moved enabling the timer to the NRF_DRV_SAADC_EVT_CALIBRATEDONE event instead of in saadc_init(). So the timer gets started after the initial calibration and after any re-calibration.

    But with the above code, it gets stuck in the while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS);

    Do I need the nrf_drv_saadc_abort()?

    I know you mentioned that I need to wait for the END event before calibrating, how do I do that?

    Edit: After trying the above, I followed this thread and stopped and restarted everything so it would start from the very beginning with the initial calibration, but the buffer was out of order again!

    https://devzone.nordicsemi.com/f/nordic-q-a/32982/can-t-stop-saadc/127979#127979

    static void saadc_event_handler(nrf_drv_saadc_evt_t const * p_event) {
        static uint16_t saadc_event_counter = 1;
    
        if(p_event->type == NRF_DRV_SAADC_EVT_DONE) {
            //Read buffer
            for(int i=0; i<SAADC_SAMPLES_IN_BUFFER; i++) {
                saadc_value_current[i] = p_event->data.done.p_buffer[i];
            }
            
            if((saadc_event_counter % SAADC_CALIBRATION_INTERVAL) == 0) {
                saadc_event_counter = 0;    //reset
                nrf_drv_timer_disable(&m_saadc_timer);
                nrf_drv_timer_uninit(&m_saadc_timer);
                nrf_drv_ppi_channel_disable(m_saadc_ppi_channel);
                nrf_drv_ppi_uninit();
                nrf_drv_saadc_abort();
                nrf_drv_saadc_uninit();
                while(nrf_drv_saadc_is_busy());
                saadc_init();
                saadc_sampling_event_init();
                saadc_sampling_event_enable();
            }
            else {
                //Set buffer, double-buffering
                ERROR_CHECK(nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAADC_SAMPLES_IN_BUFFER));
            
                //Print samples
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "\nsaadc_value_current[0]: %u\nsaadc_value_current[1]: %u\nsaadc_value_current[2] %u\nsaadc_value_current[3]: %u\n", saadc_value_current[0], saadc_value_current[1], saadc_value_current[2], saadc_value_current[3]);
            }
            
            saadc_event_counter++;
        }
        else if(p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE) {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "SAADC calibration complete\n");
    
            //Need to setup both buffers, as they were both removed with the call to nrf_drv_saadc_abort before calibration
            ERROR_CHECK(nrf_drv_saadc_buffer_convert(saadc_buffer_pool[0], SAADC_SAMPLES_IN_BUFFER));
            ERROR_CHECK(nrf_drv_saadc_buffer_convert(saadc_buffer_pool[1], SAADC_SAMPLES_IN_BUFFER));
    
            nrf_drv_timer_enable(&m_saadc_timer);
        }
    }

    Thanks.

  • Hello again,

    ftjandra said:
    My nRF52840 is rev2.

    Great, thank you for confirming this. There is only 2 SAADC erratas for the rev 2, one of which is not the issue in question here, and the other one which we have already spoken about.

    ftjandra said:
    Eventually I will trigger calibration on temperature change, but for testing it's easier to just do it every xx samples.

    I understand, this is all right during the testing and development phase.

    ftjandra said:
    Do I need to disable the timer, ppi channel, both? Seems like disabling the timer should be enough.

    This is correct, by disabling the task trigger you have disabled sampling.
    However, it seems you might be using the _abort incorrectly - please see the nrfx_saadc_abort documentation, it reads:

    Note
            NRFX_SAADC_EVT_DONE event will be generated if there is a conversion in progress. Event will contain number of words in the sample buffer.
    Warning
            This function must not be called from the context of event handler of the SAADC driver or from the context of interrupt with priority equal to or higher than priority of the SAADC interrupt.

    You can not call _abort from the event handler of the SAADC, or from an interrupt with priority equal to or higher than the SAADC interrupt.
    So, for instance, if you call it from main ( or anywhere else with lower priority ) then it will work.

    ftjandra said:
    Do I need the nrf_drv_saadc_abort()?

    Yes, but it can not be used as you are using it now.

    ftjandra said:
    I know you mentioned that I need to wait for the END event before calibrating, how do I do that?

    My apologies, when using the NRFX driver it is the DONE event you will have to wait for.
    The END event is from the HAL, which you should not use directly together with the NRFX driver, since this may place the driver in an invalid state.

    ftjandra said:
    After trying the above, I followed this thread and stopped and restarted everything so it would start from the very beginning with the initial calibration, but the buffer was out of order again!

    As I said, you can not call abort from the SAADC event handler context.
    Please resolve this, and let me know if this resolves the issue of getting stuck in the calibrate loop for earlier.

    Looking forward to resolving this issue together!

    Best regards,
    Karl 

  • Ok, still no luck.

    I am now calling nrf_drv_timer_disable(&m_saadc_timer) and setting a calibrate flag from within the SAADC event handler.

    Then in main() when the calibrate flag is set, I call nrf_drv_saadc_abort().

    Then 2 seconds later also from main I re-initiate the calibration with while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS). I picked two seconds to account for any pending NRFX_SAADC_EVT_DONE events.

    After the calibration everything in the buffer is still shifted by spot.

Related