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.

Parents
  • Hi Bojan,

    You cannot use the Zephyr ADC API if you want to trigger sampling regularly without CPU intervention due to the general nature of the Zephyr APIs. In that case you need to use a TIMER to trigger the SAADC via PPI, and for that you need to use the nrfx drivers directly.

    I do not have an example which does that at hand, but how to use nrfx drivers in general in Zephyr is demonstrated by the nrfx use example. You can combine that with this unofficial SAADC example, which demonstrates using the latest nrfx API to sample multiple channels regularly based on a timer (it is for the nRF5 SDK, but the nrfx API is the same regardless).

    Referring to the example, note that if you do not want to handle the sample set every time you just need to increase the buffer length, as you will get an interrupt ever time the buffer is filled. Also, you may not need to use double buffering as demonstrated, if you have time to process the buffer on time without it.

    Br,

    Einar

  • Hello, Einar.

    I am trying to port your unofficial SAADC example into Zephyr environment.

    As a first goal, I wanted to test only adc_configure() function. According to nrfx use example that shows how to use nrfx drivers in Zephyr, I did the following in my prj.config file:

    CONFIG_ADC=n
    CONFIG_ADC_NRFX_SAADC=y

    I also included nrfx_saadc header file  line in my main.cpp file:

    #include <nrfx_saadc.h>

    When I compile the code, the compiler complains about undefined reference to nrfx_saadc_xxx() functions that are all declared in included nrfx_saadc.h file:

    build/../src/main.cpp:187: undefined reference to `nrfx_saadc_channels_config'
    build/../src/main.cpp:190: undefined reference to `nrfx_saadc_advanced_mode_set'
    build/../src/main.cpp:197: undefined reference to `nrfx_saadc_buffer_set'
    build/../src/main.cpp:200: undefined reference to `nrfx_saadc_buffer_set'
    build/../src/main.cpp:203: undefined reference to `nrfx_saadc_mode_trigger'
    

    Do you have any idea what I am missing here? Do I need to include something more in my prj.config file that will disable the usage of Zephyr ADC APIs and encourage the usage of NRFX SAADC driver?

    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.

Reply
  • 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.

Children
Related