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:

    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.

    This is better, but please keep in mind that the nrfx_saadc_calibrate_offset function is non-blocking, so the program will move on before the calibration necessarily is finished.
    Could you attempt to add these lines right below your while(!=NRF_SUCCESS) call?

    while(!nrf_saadc_event_check(NRF_SAADC_EVENT_CALIBRATEDONE ))
    {
        nrf_pwr_mgmt_run();
    }


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

    Is it a particular number they are shifted by?
    Some samples may be placed in the buffer during / following calibration, so please make sure that you supply new buffers to the SAADC following a completed calibration.

    Best regards,
    Karl

  • Instead of nrf_pwr_mgmt_run(), I used sd_app_evt_wait(). I think they do the same thing when the Soft Device is used.

        for (;;)
        {
            //SAADC Calibration
            if(saadc_calibrate_stage == 1) {
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Calling nrf_drv_saadc_abort()\n");
                nrf_drv_saadc_abort();
                saadc_calibrate_stage = 2;
            }
            else if(saadc_calibrate_stage == 4) {
                saadc_calibrate_stage++;
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Starting new SAADC calibration\n");
                while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //trigger calibration task
                while(!nrf_saadc_event_check(NRF_SAADC_EVENT_CALIBRATEDONE)) {
                    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "a\n");
                    //nrf_pwr_mgmt_run();
                    (void)sd_app_evt_wait();
                }
            }
            (void)sd_app_evt_wait();
        }

    After initiating the first recalibration, It gets stuck in that new while loop and never gets out. Everything looks like it's still running since it's no different than the 'outer' sd_app_evt_wait(). Except that it doesn't do any more recalibrations after the first time because it doesn't get out to check 'saadc_calibration_stage'.

    But, this didn't make a difference because the values are still shifted after calibration:

    Right before Calibration
    00> saadc_value_current[0]: 987
    00> saadc_value_current[1]: 2384
    00> saadc_value_current[2]: 0
    00> saadc_value_current[3]: 381

    Right after calibration
    00> saadc_value_current[0]: 986
    00> saadc_value_current[1]: 986
    00> saadc_value_current[2]: 2383
    00> saadc_value_current[3]: 0

    2nd reading after calibration, buffer values are shifted by one spot
    00> saadc_value_current[0]: 381
    00> saadc_value_current[1]: 986
    00> saadc_value_current[2]: 2384
    00> saadc_value_current[3]: 4294967295

    Stays shifted by one spot (even after subsequent recalibrations)
    00> saadc_value_current[0]: 381
    00> saadc_value_current[1]: 987
    00> saadc_value_current[2]: 2384
    00> saadc_value_current[3]: 0

    I just realized, isn't nrf_drv_saadc_calibrate_offset() being non-blocking already being taken care of in the saadc_event_handler() event handler. This is where I reset the buffers and restart the timer.

        else if(p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE) {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "SAADC calibration complete\n");
            saadc_calibrate_stage = 0; //reset
    
            //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,

    Thank you for the clear illustration.

    ftjandra said:
    Instead of nrf_pwr_mgmt_run(), I used sd_app_evt_wait(). I think they do the same thing when the Soft Device is used.

    Yes, using sd_app_evt_wait will function just as well - the CPU will be sleeping until an application event is generated.

    ftjandra said:
    I just realized, isn't nrf_drv_saadc_calibrate_offset() being non-blocking already being taken care of in the saadc_event_handler() event handler. This is where I reset the buffers and restart the timer.

    Yes, come to think of it this should already be taken care of, actually. Keenly spotted.
    Then, only the _abort needs to be called from the main context, not the wait_for_calibrate_done.

    A colleague of mine mentioned that he had experienced something similar when testing some combination of burst + scan + oversampling and LP modes.
    Are you currently using the SAADC Low Power mode? -If not, could you set it to 1 and see if this changes anything in regards to your buffers?

    Perhaps you could share you project with me, so that I could attempt to replicate and debug this on my end?
    I can convert the ticket to private if you would like me to, just let me know.

    Best regards,
    Karl

  • Ok, I started from a fresh light switch server and only added what was needed to test this. The project is attached (SDK16.0.0 + Mesh SDK 4.1.0)

    With low_power_mode set to 0, you can see that the buffer gets offset by one after the first calibration.

    With low_power_mode set to 1, it looks like the buffer stays correct, but the timing is not working anymore. It's not sampling every second, but instead it seems to be sampling continuously as fast as possible? What is low power mode?

    Thanks.


    server_light_switch_saadc.zip

  • Hello,

    ftjandra said:
    Ok, I started from a fresh light switch server and only added what was needed to test this. The project is attached (SDK16.0.0 + Mesh SDK 4.1.0)

    Thank you for providing me with this project. I have allocated time to test this tomorrow.

    I have just gotten back from conferring with our SAADC expert, and he had some thoughts on the matter:

    First, on the subject of Low-Power mode negating the buffer shift:
    This might be because Low-Power mode stops the SAADC between every sample, which in turn ensures that the workaround for the Errata 237 is in place.

    To test if this is the case, could you define DEBUG in your preprocessor defines ( like showed in the included picture ) and see if your call to _abort asserts due to hardware timeout when waiting for the SAADC to be stopped?
    In this case, it points to the fact that the SAADC never truly was stopped, and thus the calibration is called as described in the errata I mentioned earlier, leaving an unexpected sample in the buffer.

    ftjandra said:
    What is low power mode?

    The documentation is indeed a little sparse on this, so it is good that you ask! Low-power mode stops the SAADC between each sample.
    It is ideal for low-frequency sampling, however it does add some time to the conversion time.

    ftjandra said:
    With low_power_mode set to 1, it looks like the buffer stays correct, but the timing is not working anymore.

    What is your sampling frequency? Since you are using PPI, there is a change when using Low-Power mode. Instead of the PPI trigger TASK_SAMPLE, it will instead trigger TASK_START, which will add some time to your conversion time.

    Best regards,
    Karl

Related