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

How to use nrfx SAADC driver on Zephyr RTOS

Hello guys.

We use NCS v1.4.2 and Zephyr RTOS to develop the application code for our nRF52840 SoC.

Thanks to your support, we were able to use Zephyr ADC APIs and take the readings from some ADC channels (thread).

However, we would like to benefit from some nRF ADC features like taking periodic samples in the background (timer + PPI + ADC) or calling an interrupt function when ADC values are above/below pre-defined limits (e.g. to use nrfx_saadc_limits_set() function to set the limits). For that features, it is needed to use nrfx drivers directly. 

There is one example that shows how, in general, nrfx drivers are used - link. By following the analogy from that example, I tried to disable native Zephyr ADC driver and enable nrfx saadc driver with the following two lines 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 that contains some saadc related functions (init, config...), the compiler complaints about the 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.

  • I wanted to give it a try with nrfx timer, by including only timer_init() function in main.c:

    static void timer_init(void)
    {
        nrfx_err_t err_code;
    
        nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG;
        timer_config.frequency = NRF_TIMER_FREQ_31250Hz;
        err_code = nrfx_timer_init(&m_sample_timer, &timer_config, timer_handler);
        printk("nrfx_timer_init: %u\n", err_code);
        //APP_ERROR_CHECK(err_code);
        nrfx_timer_extended_compare(&m_sample_timer,
                                    NRF_TIMER_CC_CHANNEL0,
                                    nrfx_timer_ms_to_ticks(&m_sample_timer, saadc_sampling_rate),
                                    NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
                                    false);
    
        nrfx_timer_resume(&m_sample_timer);
    }

    Now the code runs but nrfx_timer_init() function returns 0xBAD0000.

    For the nrfx timers, I included the following in prj.config file:

    CONFIG_NRFX_TIMER=y
    CONFIG_NRFX_TIMER1=y

    and 

    #include <nrfx_timer.h>

    in main.c.

    I also tried to connect Zephyr's and nrfx timer IRQ handlers with:

    IRQ_CONNECT(TIMER1_IRQn, NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
                nrfx_timer_1_irq_handler, NULL, 0);

    but to no avail. What I am missing in the case of nrfx timers?

    My impressions are that some deeper explanation is needed (e.g. in the form of tutorials) on how we can benefit from nrfx drivers in Zephyr environment. One example we currently have (link) is not enough.

    Cheers,

    Bojan.

  • Hello Bojan,

    You are getting a fatal error because you have not connected your SAADC peripheral to an interrupt service handler yet. You can do this with the following command:

    IRQ_CONNECT(DT_IRQN(DT_NODELABEL(adc)),
                DT_IRQ(DT_NODELABEL(adc), priority),
                nrfx_saadc_irq_handler, NULL, 0);

    Regarding the timer:

    Actually, error code 0xBAD0000 is equal to NRFX_SUCCESS (see also nrfx_errors.h in ../modules/hal/nordic/nrfx/drivers). To avoid confusion, you could add something like this after the call of the init-function:

    if (err_code != NRFX_SUCCESS) {
            LOG_ERR("nrfx_timer_init error: %08x", err_code);
            return;
        }

    Then you do not have to care about the error code value anymore.

    Cheers,

    Markus

  • Hello, .

    Thank you very much for your support!

    Great to read that nrfx_timer_init() returns NRFX_SUCCESS. Let me dive deeper into my work then!

    I will feel free to come back again if I run into some trouble.

    Cheers!

Related