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

Why is saadc reading taking more than expected time and why do i have to put delay between readings

Hi
i am trying to reduce the adc read times to a minimum we currently have it setup for 3us acq and 32 samples so expecting something close to 160us
Its getting around 250us.
Below is how I set it up and do the adc reads
You will see for each read I need to change gain and input pin

Q1 Am I doing the correct thing below (it works) or am i doing something unnecessary
and making the read cycles longer than necessary.
If my code is good any suggestions on how we can reduce the time

Q2 I am finding I have to put large delays in order of milliseconds to get good readings every time
if I dont some readings come back all FS IE 4095


FIRST TIME THROUGH

void saadc_adc_init(void) {

ret_code_t err_code;
nrf_drv_saadc_config_t adc_config;

adc_config.resolution = (nrf_saadc_resolution_t)NRF_SAADC_RESOLUTION_12BIT;
adc_config.oversample = (nrf_saadc_oversample_t)NRF_SAADC_OVERSAMPLE_DISABLED;
adc_config.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY;
adc_config.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE;

err_code = nrf_drv_saadc_init(&adc_config, saadc_callback);
APP_ERROR_CHECK(err_code);
}


CALL following function
to setup for the provided pin and gain

void saadc_channel_init(saadc_adc_input_t adc_input, saadc_adc_input_gain_t adc_ip_gain) {

ret_code_t err_code;
nrf_saadc_channel_config_t channel_config;

channel_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED;
channel_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED;
channel_config.gain = adc_ip_gain;
channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
channel_config.acq_time = NRF_SAADC_ACQTIME_3US; // Note this was 3us on the prototype
channel_config.mode = NRF_SAADC_MODE_SINGLE_ENDED;
channel_config.burst = NRF_SAADC_BURST_DISABLED;

channel_config.pin_p = (nrf_saadc_input_t)(adc_input);
channel_config.pin_n = NRF_SAADC_INPUT_DISABLED;


// Channel number can be 0 -7 We may wish to use same channel for each ADC we read
err_code = nrf_drv_saadc_channel_init(0, &channel_config);
APP_ERROR_CHECK(err_code);


err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], ADC_SAMPLES_BUFFER_LEN);
APP_ERROR_CHECK(err_code);

err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], ADC_SAMPLES_BUFFER_LEN);
APP_ERROR_CHECK(err_code);


}

Call the following to start ADC
ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
APP_ERROR_CHECK(err_code);

THEN ON SUBSEQUENT adc reads I do this

nrf_drv_saadc_uninit();

Call my function above
saadc_adc_init(..)
Call my function above to init with new pin and gain
saadc_channel_init(...

Then enable it
ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
APP_ERROR_CHECK(err_code);

  • Thanks Haakonsh I was looking at this approach and wondered if it would work,
    So using your code for each adc read  i would simply do this 
    NRF_SAADC->CH[<previous channel>].PSELP = 0;
    NRF_SAADC->CH[<next channel>].PSELP = <pin number>;
    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], ADC_SAMPLES_BUFFER_LEN);
    Then this to start the next read
    nrf_drv_ppi_channel_enable(m_ppi_channel);    

    Is that correct ?

    Why do I have to call  nrf_drv_saadc_buffer_convert(m_buffer_pool[0], ADC_SAMPLES_BUFFER_LEN);
    I have a call in the event handler    
    nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, ADC_SAMPLES_BUFFER_LEN);
    Do I need to still do this, or do I only need this one in the event handler as I have 2 buffers, which I may not need thinking about this as we dont read the adc continuously and therefore need time to process a read while another is going on.

    When I enable 2 of the ADC inputs I see it will then scan these sequentially, will the results be put in one buffer or is this why I would need 2 buffers. If its just one then I would need to double its size and I believe the results would be interleaved.

    UPDATE: I tried this code but I get an BUSy ERROR when I call
    nrf_drv_saadc_buffer_convert(m_buffer_pool[0], ADC_SAMPLES_BUFFER_LEN);

    to start the next adc read on different pin


    Robin

  • "Then this to start the next read
    nrf_drv_ppi_channel_enable(m_ppi_channel);    

    Is that correct ?"

    No, this line enables the given channe, just replace <next channel> and <pin number> whith the values you want :
    NRF_SAADC->CH[<next channel>].PSELP = <pin number>;

    "Why do I have to call  nrf_drv_saadc_buffer_convert(m_buffer_pool[0], ADC_SAMPLES_BUFFER_LEN);
    I have a call in the event handler    
    nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, ADC_SAMPLES_BUFFER_LEN);
    Do I need to still do this, or do I only need this one in the event handler as I have 2 buffers, which I may not need thinking about this as we dont read the adc continuously and therefore need time to process a read while another is going on."

    This will probably work, but I have not tried to change the channel settings after I've called the START task, therefore I thought it best to do it after.

    "When I enable 2 of the ADC inputs I see it will then scan these sequentially, will the results be put in one buffer or is this why I would need 2 buffers. If its just one then I would need to double its size and I believe the results would be interleaved."

    One buffer, you need to double the size, and they will be stored sequentially(interleaved), f.ex

    [Chan1][Chan2][Chan1][Chan2]...
    [32-bit word 0 ][32-bit word 1  ]...

  • Thanks Haarkonsh
    Currently in my existing code I am calling 
    nrf_drv_ppi_channel_disable(m_ppi_channel);
    In the ADC read complete event handler as it looked like another ADC read was being started.
    And this is why I am calling    nrf_drv_ppi_channel_enable(m_ppi_channel);    to start the next ADC read.

    So looking at my code i provided have I setup the SAADCC/PPI/TIMER feature correctly so it runs once for the number of channels enabled and samples ?
    If it is ok and I shouldnt have to call nrf_drv_ppi_channel_disable(m_ppi_channel);  in the saadc read event complete handler , then how do I restart it again for the next read after I disable and enable channels for the next read using NRF_SAADC->CH[<next channel>].PSELP = <pin number>;

    Is this call  nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, ADC_SAMPLES_BUFFER_LEN);
    in the event handler starting the next read cycle while I am still processing the first 

    Robin

  • All you need to enable a channel is to write to the NRF_SAADC->CH[x].PSELP register, to disable a channel you clear it.

    You don't need to call nrf_drv_ppi_channel_disable(m_ppi_channel);, you just need to stop the TIMER. You can connect the SAADC's END event to the TIMER's STOP task to do that. Then you don't have to execute any code to do it.

    To start the timer again you call 
    nrfx_timer_resume(), or NRF_TIMERn->TASKS_START = 1; replace 'n' with your timer instance number.

    "Is this call  nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, ADC_SAMPLES_BUFFER_LEN);
    in the event handler starting the next read cycle while I am still processing the first "

    No, it writes the address and size of the next buffer to the RESULT.PTR and .MAXCOUNT registers and triggers the START task. It does not start any sampling, only the SAMPLE task can trigger a sample event. You can safely process your previous buffer until the next time you call nrf_drv_saadc_buffer_convert. 


  • Thanks I think I almost have it now, one last question

    "You can connect the SAADC's END event to the TIMER's STOP task to do that. Then you don't have to execute any code to do it."

    How do I do this ?
    maybe you could show me how this would be done in the nordic saadc exmaple that comes with SDK 16.0.0

    RObin

Related