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

How to implement max sampling with SAADC

I would like to implement a simple code to use SAADC on NRF52 to sample one channel at maximum sample rate (theoretical 200KHz). All the examples I have found use limiting factors such as using function "nrf_drv_timer_ms_to_ticks", slow clocks or others. I intend on sending the measurements using UART. I'm looking for either an example that already exists, or an explanation on how to modify existing examples. Thanks

  • Instead of nrf_drv_timer_ms_to_ticks you can use nrf_drv_timer_us_to_ticks. To sample at 200kHz you have to set the acquisition time to 5us or less. This you do in the nrf_saadc_channel_config_t::acq_time. For example in the saadc example code:

    nrf_saadc_channel_config_t channel_config =
                NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
    channel_config.acq_time = NRF_SAADC_ACQTIME_3US;
    

    Be aware of the maximum input resistance, see here.

    EDIT 2016.07.28: Using the "free running mode" the sample time is acquisition time + conversion time which will not result in 200KHz. Also updated the code so that it actually works.

    You can also skip using the timer if you configure the SAADC in "Free running mode" (an expression used in AVR chips); when one sample is done the next sample starts immidiately. This can be done by linking the SAMPLE event with the DONE or RESULTDONE event through PPI:

    void saadc_sampling_event_init(void)
    {
        ret_code_t err_code;
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code);
        
        uint32_t saadc_sample_event_addr = nrf_drv_saadc_sample_task_get();
    
        err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
        APP_ERROR_CHECK(err_code);
        
        err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, (uint32_t)&NRF_SAADC->EVENTS_RESULTDONE, saadc_sample_event_addr);
        APP_ERROR_CHECK(err_code);
    }
    
    void saadc_sampling_event_enable(void)
    {
        ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
        APP_ERROR_CHECK(err_code);
    }
    

    Start the first sample with nrf_drv_saadc_sample().

  • Thanks for the response. I tried only using nrf_drv_timer_us_to_ticks as well as changing the acquisition time. There were problems with this. After reaching "SAMPLES_IN_BUFFER" you lose measurements, that means that I can't get a constant time difference between measurements which prevents me from analyzing the data as I need to. I can get around this by increasing SAMPLES_IN_BUFFER as high as it can go, as well as UART_TX_BUF_SIZE. However at some point (depending on size of buffers) there is an overflow and they lose sync. I also tried the code you gave however there is no output, either because data isn't transferred to UART or there is no actual sampling. What do I need to add to it to get it working?

  • I did not think about that the total SAADC sample+convert takes more than the acqusition time, so you should set the acqusition time to 3us. I was able to get sample every 5us doing this and triggering the sample using timer at every 5us. Set the SAMPLES_IN_BUFFER to the amount of samples you want to get before sending them over UART, and be aware that it may take time to send all the data over UART so that you don't miss SAADC interrupts because of this.

    Because the convertion time for a sample is not exactly 5us, you can not use the "free running mode" to get samples at a specific frequency, I will remove this from my answer. The reason it did not work is probably because you have to start it by triggering the sample task at the start: nrf_drv_saadc_sample() and you must use RESULTDONE or DONE event instead of END event (if SAMPLES_IN_BUFFER is greater than 1).

Related