Timer/SAADC/(D)PPI differences between nRF52840 & nRF5340

Hi team !

I'm facing issue when porting my code from nRF52840DK to nRF5340DK

I implemented SAADC acquisition, using Timer/SAADC/(D)PPI stack on both boards, handling the few differences I found (i.e: nrfx_ppi_channel... vs nrfx_dppi_channel... functions, includes, etc).
So I got my code running on both boards, but I still see a huge difference I can't tell where it comes:

As I log some timing values at runtime, I can see how long my firmware takes to do a full loop (ADC acquisition, writing buffer in flash/sending buffer through BLE, depending on the situation).
- With the nRF52840, I have a 100ms loop, as expected. During this time, I take 100 ADC samples from 1 channel, get it in my main() from ADC queue, and handle it.
- With the nRF5340, the same code take 400ms to do a loop, everything else is OK.

The only way I found to fix this delta is to divide by 4 the compare value I set when calling nrfx_timer_compare(), from 1000 (1ms @ 1MHz), to 250 (don't really know what this value means in this situation...).

My question is: why do I have to change this compare value and is it the best way to achieve my goal ?

Thanks in advance for your support.

Guillaume

const nrfx_timer_t m_sample_timer = NRFX_TIMER_INSTANCE(1);
const uint32_t saadc_sampling_rate = 1; // milliseconds (ms)

void timer_init(void)
{
    // SAADC timer config
    nrfx_timer_config_t timer_config_sample = {
            .frequency = NRF_TIMER_FREQ_1MHz,
            .mode = NRF_TIMER_MODE_TIMER,
            .bit_width = NRF_TIMER_BIT_WIDTH_32,
            .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY
        };
    // Hot fix for code compatibility 5340DK vs 52840DK
    #ifdef DPPI_PRESENT
        int compare_value = 250;
    #else
        int compare_value = 1000;
    #endif
    
        err_code = nrfx_timer_init(&m_sample_timer, &timer_config_sample, timer_handler);
        if(err_code != NRFX_SUCCESS) {
            printk("Unable to init sampling timer (error %d)", err_code);
        }
        nrfx_timer_compare(&m_sample_timer,
                                    NRF_TIMER_CC_CHANNEL0,
                                    nrfx_timer_us_to_ticks(&m_sample_timer, saadc_sampling_rate * compare_value),
                                    false);
        nrfx_timer_compare(&m_sample_timer,
                                    NRF_TIMER_CC_CHANNEL1,
                                    nrfx_timer_us_to_ticks(&m_sample_timer, saadc_sampling_rate * compare_value * 2),
                                    false);
        nrfx_timer_compare(&m_sample_timer,
                                    NRF_TIMER_CC_CHANNEL2,
                                    nrfx_timer_us_to_ticks(&m_sample_timer, saadc_sampling_rate * compare_value * 3),
                                    false);
        nrfx_timer_extended_compare(&m_sample_timer,
                                    NRF_TIMER_CC_CHANNEL3,
                                    nrfx_timer_us_to_ticks(&m_sample_timer, saadc_sampling_rate * compare_value * 4),
                                    NRF_TIMER_SHORT_COMPARE3_CLEAR_MASK,
                                    false);
    
    nrfx_timer_resume(&m_sample_timer);
}

Config:

- Windows_NT x64 v10.0.19042
- VSCode v1.65.2
- nRF Connect for VS Code v2022.3.104

  • Hi,

    The only difference I can think of would be that one of the devices have oversampling enabled in some config, while the other does not. If oversampling is set to 4X without enabling BURST, you would need to sample 4 times for each result to be generated. However, with SCAN mode (multiple channels enabled), without BURST enabled, this would generate the wrong results.

    Can you post your config and all code for using SAADC?

    Best regards,
    Jørgen

  • Hi Jørgen,

    Thanks for your answer.

    Here is the function I use to configure ADC, and there is no conditional settings depending on the board, beside calling nrfx_ppi or nrfx_dppi functions.

    #define ADC_CHANNELS_IN_USE     1
    #define SAADC_BUF_SIZE          ADC_CHANNELS_IN_USE * 25 * 4
    #define SAADC_BUF_COUNT         2
    
    bool adc_configure(void)
    {
        nrfx_err_t err_code;
        nrf_saadc_value_t adc_samples[SAADC_BUF_COUNT][SAADC_BUF_SIZE];
    
        nrfx_saadc_adv_config_t saadc_adv_config = NRFX_SAADC_DEFAULT_ADV_CONFIG;
        saadc_adv_config.internal_timer_cc = 0;
        saadc_adv_config.start_on_end = true;
        saadc_adv_config.oversampling = NRF_SAADC_OVERSAMPLE_32X;
        saadc_adv_config.burst = NRF_SAADC_BURST_ENABLED;
    
        err_code = nrfx_saadc_init(NRFX_SAADC_DEFAULT_CONFIG_IRQ_PRIORITY);
        printk(" * nrfx_saadc_init error code: %d\n", err_code == NRFX_SUCCESS);
        if (err_code != NRFX_SUCCESS) {
            return false;
        }
    
        static nrfx_saadc_channel_t channel_configs[ADC_CHANNELS_IN_USE];
    
        uint8_t channel_mask = 0;
        //nrfx_saadc_channel_t config = NRFX_SAADC_DEFAULT_CHANNEL_DIFFERENTIAL(
        //                                    NRF_SAADC_INPUT_AIN2, NRF_SAADC_INPUT_AIN3, 0);
        nrfx_saadc_channel_t config = NRFX_SAADC_DEFAULT_CHANNEL_SE(
                                            NRF_SAADC_INPUT_AIN1, 0);
        config.channel_config.reference = NRF_SAADC_REFERENCE_VDD4;          
        config.channel_config.gain = NRF_SAADC_GAIN1;
    
        // Copy to list of channel configs
        memcpy(&channel_configs[0], &config, sizeof(config));
    
        // Update channel mask
        channel_mask |= 1 << 0;
    
        err_code = nrfx_saadc_channels_config(channel_configs, ADC_CHANNELS_IN_USE);
        printk(" * nrfx_saadc_channels_config error code: %d\n", err_code == NRFX_SUCCESS);
        if (err_code != NRFX_SUCCESS) {
            return false;
        }
        err_code = nrfx_saadc_advanced_mode_set(channel_mask,
                                                NRF_SAADC_RESOLUTION_14BIT,
                                                &saadc_adv_config,
                                                &adc_event_handler);
        printk(" * nrfx_saadc_advanced_mode_set error code: %d\n", err_code == NRFX_SUCCESS);
        if (err_code != NRFX_SUCCESS) {
            return false;
        }
        
        // Configure two buffers to ensure double buffering of adc_samples,
        // to avoid data loss when the sampling frequency is high
        err_code = nrfx_saadc_buffer_set(&adc_samples[next_free_buf_index()][0], SAADC_BUF_SIZE);
        printk(" * nrfx_saadc_buffer_set error code: %d\n", err_code == NRFX_SUCCESS);
        if (err_code != NRFX_SUCCESS) {
            return false;
        }
    
        err_code = nrfx_saadc_buffer_set(&adc_samples[next_free_buf_index()][0], SAADC_BUF_SIZE);
        printk(" * nrfx_saadc_buffer_set error code: %d\n", err_code == NRFX_SUCCESS);
        if (err_code != NRFX_SUCCESS) {
            return false;
        }
    
        err_code = nrfx_saadc_mode_trigger();
        printk(" * nrfx_saadc_mode_trigger error code: %d\n", err_code == NRFX_SUCCESS);
        if (err_code != NRFX_SUCCESS) {
            return false;
        }
    
        IRQ_CONNECT(SAADC_IRQn, 1, nrfx_saadc_irq_handler, NULL, 0);
        irq_enable(SAADC_IRQn);
    
        return true;
    }

    I actually tried to change several settings, looking for one which can have an impact on my timing constraint, but only pushing oversampling from 32x(my default value) to 256x added 1 or 2 ms on my loop duration.

    Regarding timer config, I also tried to change its frequency or its bit width, with no visible difference on loop duration.

    You should also know that I use another timer to toggle pins, but really not sure it can have this kind of influence on the new board.

  • And here is the function to configure ppi.

    void ppi_init(void)
    {
        nrfx_err_t err;
        nrf_timer_event_t evt_compare[4] = {
            NRF_TIMER_EVENT_COMPARE0,
            NRF_TIMER_EVENT_COMPARE1,
            NRF_TIMER_EVENT_COMPARE2,
            NRF_TIMER_EVENT_COMPARE3
        };
    
    #if defined(DPPI_PRESENT)
    
    	uint8_t m_timer_saadc_ppi_channel[4];
    
        for (int i = 0; i < 4; i++) {
        	err = nrfx_dppi_channel_alloc(&m_timer_saadc_ppi_channel[i]);
            if (err != NRFX_SUCCESS) {
                printk("SAADC (D)PPI channel allocation error: %08x\n", err);
                return;
            }
        }
    #else
    	uint8_t m_timer_saadc_ppi_channel[4];
    
        for (int i = 0; i < 4; i++) {
        	err = nrfx_ppi_channel_alloc(&m_timer_saadc_ppi_channel[i]);
            if (err != NRFX_SUCCESS) {
                printk("SAADC (D)PPI channel allocation error: %08x\n", err);
                return;
            }
        }
    #endif
    
        for (int i = 0; i < 4; i++) {
            nrfx_gppi_channel_endpoints_setup(m_timer_saadc_ppi_channel[i],
                nrfx_timer_event_address_get(&m_sample_timer, evt_compare[i]),
                nrf_saadc_task_address_get(NRF_SAADC, NRF_SAADC_TASK_SAMPLE));
        }
        
    	/* Enable (D)PPI channel. */
    #if defined(DPPI_PRESENT)
        for (int i = 0; i < 4; i++) {
        	err = nrfx_dppi_channel_enable(m_timer_saadc_ppi_channel[i]);
            if (err != NRFX_SUCCESS) {
                printk("Failed to enable SAADC (D)PPI channel, error: %08x", err);
                return;
            }
        }
    #else
        for (int i = 0; i < 4; i++) {
        	err = nrfx_ppi_channel_enable(m_timer_saadc_ppi_channel[i]);
            if (err != NRFX_SUCCESS) {
                printk("Failed to enable SAADC (D)PPI channel, error: %08x", err);
                return;
            }
        }
    #endif
    }

    Then, I finally use this in my program's main loop.

    void main(void)
    {
        [...]
        while(1) {
            time_start_sampling = k_uptime_get();
    	    k_msgq_get(getADCQueue(), &adc_buffer, K_FOREVER);
    	    time_end_sampling = k_uptime_get();
        	memcpy(buffer, adc_buffer, sizeof(adc_buffer));
        [...]
        }
    }

  • I can't see any obvious issues that should be causing this in the code.

    Can you read out and post the SAADC, TIMER1 and (D)PPI registers using a debugger after the initialization part of the code is done, on both devices, to see if there is any differences in the actual configuration?

    Do you have a minimal sample, with the code you have posted, that we can use to reproduce/debug this issue?

Related