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

Continuous periodic nRF52840 ADC sampling with Zephyr

Hello, guys.

We are using Zephyr RTOS (NCS v1.4.2) with nRF52840 development kit.

The idea is to have continuous ADC sampling on two ADC channels. So far, I was able to properly initialize the ADC device, and get one single ADC read with the following piece of code:

const struct adc_sequence sequence = {
	.channels = BIT(ADC_1ST_CHANNEL_ID),
	.buffer = m_sample_buffer,
	.buffer_size = sizeof(m_sample_buffer),
	.resolution = ADC_RESOLUTION,
};

ret = adc_read(adc_dev, &sequence);
if (ret) {
    printk("adc_read() failed with code %d\n", ret);
}

There is an issue even with this piece of code because, when I call adc_read(adc_dev, &sequence) function, only the first value of m_sample_buffer is updated, the rest of the m_sample_buffer values remain 0. What I am missing here?

Other than that, is there any way that we configure ADC device to do periodic sampling in the background without our intervention?

Thanks in advance for your time and efforts.

Sincerely,

Bojan.

  • Just figured out that I needed to include:

    CONFIG_ADC=n
    CONFIG_NRFX_SAADC=y

    Instead of:

    CONFIG_ADC=n
    CONFIG_ADC_NRFX_SAADC=y

    The code now compiles but when I try to debug it I get stuck:

    *** Booting Zephyr OS build v2.4.0-ncs2  ***
    [00:00:00.345,001] <err> os: >>> ZEPHYR FATAL ERROR 1: Unhandled interrupt on CPU 0
    [00:00:00.353,698] <err> os: Current thread: 0x20002708 (unknown)
    [00:00:00.365,783] <err> fatal_error: Resetting system

    The only thing I have in my main() function is adc_configure() with the following content:

    static void adc_configure(void)
    {
        int err_code;
    
        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;
    
        err_code = nrfx_saadc_init(NRFX_SAADC_DEFAULT_CONFIG_IRQ_PRIORITY);
        //APP_ERROR_CHECK(err_code);
    
        static nrfx_saadc_channel_t channel_configs[ADC_CHANNELS_IN_USE];
    
        uint8_t channel_mask = 0;
        for(int i = 0; i < ADC_CHANNELS_IN_USE; i++) {
            nrf_saadc_input_t pin = ANALOG_INPUT_MAP[i];
            // Apply default config to each channel
            nrfx_saadc_channel_t config = NRFX_SAADC_DEFAULT_CHANNEL_SE(pin, i);
    
            // Replace some parameters in default config
            config.channel_config.reference = NRF_SAADC_REFERENCE_VDD4;          
            config.channel_config.gain = NRF_SAADC_GAIN1_4;
    
            // Copy to list of channel configs
            memcpy(&channel_configs[i], &config, sizeof(config));
    
            // Update channel mask
            channel_mask |= 1 << i;
        }
    
        err_code = nrfx_saadc_channels_config(channel_configs, ADC_CHANNELS_IN_USE);
        //APP_ERROR_CHECK(err_code);
    
        err_code = nrfx_saadc_advanced_mode_set(channel_mask,
                                                NRF_SAADC_RESOLUTION_14BIT,
                                                &saadc_adv_config,
                                                event_handler);
        //APP_ERROR_CHECK(err_code);
                                                
        // Configure two buffers to ensure double buffering of samples, to avoid data loss when the sampling frequency is high
        err_code = nrfx_saadc_buffer_set(&samples[next_free_buf_index()][0], SAADC_BUF_SIZE);
        //APP_ERROR_CHECK(err_code);
    
        err_code = nrfx_saadc_buffer_set(&samples[next_free_buf_index()][0], SAADC_BUF_SIZE);
        //APP_ERROR_CHECK(err_code);
    
        err_code = nrfx_saadc_mode_trigger();
        //APP_ERROR_CHECK(err_code);
    }

    Please help.

    Regards,

    Bojan.

  • Hello, Einar.

    I was able to resolve the issue around timer and saadc peripherals (link). They are now both properly initialized and running.

    There is one step left in order to successfully port your code into Zephyr environment - to implement PPI functionality and trigger SAADC sampling on timer1 interrupt. I have this error in ppi_init() function:

    ../src/main.cpp: In function 'void ppi_init()':
    ../src/main.cpp:161:67: error: cannot convert 'nrf_saadc_task_t' to 'const NRF_SAADC_Type*'
      161 |                                        nrf_saadc_task_address_get(NRF_SAADC_TASK_SAMPLE));
          |                                                                   ^~~~~~~~~~~~~~~~~~~~~
          |                                                                   |
          |                                                                   nrf_saadc_task_t
    In file included from D:/Users/bojan/ncs/v1.4.2/modules/hal/nordic/nrfx/drivers/include/nrfx_saadc.h:36,
                     from ../src/main.cpp:58:
    D:/Users/bojan/ncs/v1.4.2/modules/hal/nordic/nrfx/hal/nrf_saadc.h:639:78: note:   initializing argument 1 of 'uint32_t nrf_saadc_task_address_get(const NRF_SAADC_Type*, nrf_saadc_task_t)'
      639 | NRF_STATIC_INLINE uint32_t nrf_saadc_task_address_get(NRF_SAADC_Type const * p_reg,
    

    Any idea what's wrong on my side?

    Regards,

    Bojan.

  • Hi Bojan,

    It seems this is not valid C++ code. Perhaps you can just replace nrf_saadc_task_address_get(NRF_SAADC_TASK_SAMPLE) with NRF_SAADC->TASKS_SAMPLE or just a pointer to 0x40007004?

    Einar

  • Hey, Einar.

    I figured it out. In NCS environment, nrf_saadc_task_address_get() function requires two arguments:

    /**
     * @brief Function for getting the address of the specified SAADC task register.
     *
     * @param[in] p_reg Pointer to the structure of registers of the peripheral.
     * @param[in] task  SAADC task.
     *
     * @return Address of the specified SAADC task.
     */
    NRF_STATIC_INLINE uint32_t nrf_saadc_task_address_get(NRF_SAADC_Type const * p_reg,
                                                          nrf_saadc_task_t       task);

    Consequently, what I did in the code is:

        err_code = nrfx_ppi_channel_assign(m_timer_saadc_ppi_channel, 
                                           nrfx_timer_event_address_get(&m_sample_timer, NRF_TIMER_EVENT_COMPARE0),
                                           nrf_saadc_task_address_get((NRF_SAADC_Type *)0x40007000, NRF_SAADC_TASK_SAMPLE));

    and it is now working well.

    Thanks once again, man!

    Cheers,

    Bojan.

  • Ah, of course. I did not catch that the API was changed. Good to hear it works now. Thank you for updating us!

Related