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

nRF52833 SAADC with Channel Scanning Settling Time

Hello Nordic Team,

i'm trying to configure the nRF52833 DK to scan through two channels at a high rate (10ksps) with the SAADC module.  i have configured the time to operate at 100us and I am seeing what looks like settling time with the internal mux.  for reference i used the following configuration in the ADC:

void saadc_init(void)
{
    ret_code_t err_code;
    //nrf_saadc_channel_config_t channel_config =
    //    NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
    nrf_drv_saadc_config_t saadc_config;
    nrf_saadc_channel_config_t channel_config;
    
    saadc_config.low_power_mode = true;
    saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;
    saadc_config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;
    saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW_MID;
    
    err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback);
    APP_ERROR_CHECK(err_code);

    channel_config.acq_time = NRF_SAADC_ACQTIME_3US;
    channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
    channel_config.gain = NRF_SAADC_GAIN1_2;
    channel_config.mode = NRF_SAADC_MODE_SINGLE_ENDED; 
    channel_config.pin_p = NRF_SAADC_INPUT_AIN1;
    channel_config.pin_n = NRF_SAADC_INPUT_DISABLED;  
    channel_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pullup resistor on the input pin
    channel_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pulldown resistor on the input pin
    
    err_code = nrf_drv_saadc_channel_init(0, &channel_config);
    APP_ERROR_CHECK(err_code);
    
    channel_config.acq_time = NRF_SAADC_ACQTIME_3US;
    channel_config.reference = NRF_SAADC_REFERENCE_INTERNAL;
    channel_config.gain = NRF_SAADC_GAIN1_2 ;
    channel_config.mode = NRF_SAADC_MODE_SINGLE_ENDED; 
    channel_config.pin_p = NRF_SAADC_INPUT_AIN2;
    channel_config.pin_n = NRF_SAADC_INPUT_DISABLED;  
    channel_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pullup resistor on the input pin
    channel_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED;                              //Disable pulldown resistor on the input pin
    
    err_code = nrf_drv_saadc_channel_init(1, &channel_config);
    APP_ERROR_CHECK(err_code);


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

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

}

the timer is setup as follows:

void saadc_sampling_event_init(void)
{
    ret_code_t err_code;

    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
    APP_ERROR_CHECK(err_code);

    /* setup m_timer for compare event every 100us*/
    uint32_t ticks = nrf_drv_timer_us_to_ticks(&m_timer, 100);  
    nrf_drv_timer_extended_compare(&m_timer,
                                   NRF_TIMER_CC_CHANNEL0,
                                   ticks,
                                   NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
                                   false);
    nrf_drv_timer_enable(&m_timer);

    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer,
                                                                                NRF_TIMER_CC_CHANNEL0);
    uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();

    /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
    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,
                                          timer_compare_event_addr,
                                          saadc_sample_task_addr);
    APP_ERROR_CHECK(err_code);
}

i used a 50Hz test tone connected to both of the AIN pins configured.  i get what looks like unsettled data (i plotted only a single channel in this case, using every other sample as ch0, and a similar plot is achieved using ch1).  

if i slow the sample rate down to 1kHz, the results improves as per the below:

I would have thought that the input mux could scan between two samples and settle faster than 10ksps accurately.  I likely have configured something incorrectly.  any guidance or advice is greatly appreciated!

Kind regards,

  • It all depends on the external impedance and input capacitance; NRF_SAADC_ACQTIME_3US only allows 3uSec for switching channels and will introduce crosstalk between the channels if the impedance is such that the charge time is more than 5 RC time constants (for 12-bit). Options are to incease this 3uSec sample time (SAADC setting), add external capacitance to the analogue pins if the external signals are always stable, or reduce the external resistive impedance.

    There is always going to be some crosstalk; this can be made predictable and constant by selecting a ground channel between each signal channel - ie now scanning 4 channels - or address CR as above until the crosstalk is less than a single SAADC count.

    Edit: I may have misunderstood the question: if the two signals on the 2 input pins are identical then the above comments don't apply - but still worth trying increasing sample time, say 40uSecs

  • Hello, and thanks for the responses.

    for the 40us acquisition time, I have tried this and the result stays the same.  I slowly decreases the timer period until this behaviour started to show up.  using the same code I decreased the time in 100us steps from 1ms to 500us to increase the sample rate.  my target sample rate is 10kHz for sampling two analog inputs.  

    when I decrease the timer period to below 700us (~1.5ksps output) this starts to appear.  I suspect that it has to do that the timer is interrupting the ADC.  in this case i dont think the ADC is using the EasyDMA; i couldn't tell from the example if it's actually setting that up or not.  i would think the EasyDMA would help eliminate the issue and ping-pong the data.  

    I read a few posts, it doesn't seem that there are any examples using the EasyDMA and the SAADC, is that right?  

  • Two things to consider, do you have a 'scope available? It's just possible that the BLE current pulse is affecting the analogue value as seen by the ADC; easy to check the voltage on a pin with a 'scope. Speeding up the sample rate would make that effect worse as more "incorrect" samples are taken during the voltage droop. If the reference were dropping, the pulse would be positive, so for a negative pulse as shown above the ADC pin voltage must be dropping. You could prove this issue by changing to a VDD-derived reference instead of the fixed voltage reference SAADC_CH_CONFIG_REFSEL_VDD1_4 since then the reference would track the same voltage drop and the problem should go away.

    The second is maybe post the handler code for both SAADC and timer, in case there is something obvious there. Regarding the priorities, you could also give the SAADC higher priority than the Timer to test your theory of exclusion.

    Otherwise maybe Nordic has a suggestion; I use low-level drivers and not the library code, not quite sure how the library handles the scanning.

  • Hi, 

    i'm not currently using the BLE and i am using the internal reference (0.6V). there are no significant load pulses to be seen in this case.  the only thing running on my program is the debug UART to print the SAADC samples.  

    i did change the interrupt priority and increased the size of the buffer.  what i find in this case is that if increase the buffer to say 1024 samples, i can get a few cycles of my input signal that look correct and then when the next buffer is submitted, it looks to get phase misaligned.  To me this implies that the ISR is either too long and the double buffered is not ping-ponging correctly.  

    I'm not committed to using the library functions, if the only way to get this to work fast enough to demonstrate a 10ksps ADC throughput with two channels enabled that's fine, but an example would be really helpful and how to use the EasyDMA correctly.  

    thanks,

  • Hi,

    Could you upload your project? I would like to take a closer look at it.

    regards

    Jared 

Related