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);

Parents
  • Do you only take one sample before you change the SAADC settings? 

    Do you disable the ppi channel after you've taken one sample? 

    What do you use to trigger m_ppi_channel? 

    Why do you not want to use more than one channel?

  • Do you only take one sample before you change the SAADC settings? 

             Its set up to take 32 samples controlled by the PPI, so we kick it off and when its done we have 32 samples in the buffer. We would then change the input and gain and run again on the new input.

    Do you disable the ppi channel after you've taken one sample?

               No , see the event handler at the bottom of this reply, you now have all the code involved,
                NOTE I commented out all my logging when I was measuring the time

     What do you use to trigger m_ppi_channel?

              As per my code above I call   nrf_drv_ppi_channel_enable(m_ppi_channel);

    Why do you not want to use more than one channel?

                I didnt think of that or was aware I could, I am relatively new to this. 
                So are you suggesting I can pre-configure 3 channels for the 3 inputs I have and then just switch the channel for each read cycle. If so can you point me to an example ?
    I assume I would be able to keep the remaining code the same using the one timer and ppi channel ?

    In our application we have 3 gpios connected to 3 different analog sources, apart from the input pin being different the gain is different for all 3.  We need this to be as fast as possible. We dont scan all 3 sequentially in the same order, sometimes its 2 sometimes all 3 and in different order. 

    Thanks Robin

  • 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

Reply Children
Related