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.

  • 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

  • Thanks for your help, Einar.

    Please help me to better understand the following...

    If we use Zephyr OS and Zephyr timer to take the samples at regular intervals, can we tell Zephyr ADC API to take some number of samples from the particular channel, not only one sample?

    This is the sequence I call when the Zephyr timer expires:

    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);
    }

    I selected the channels I want to read as well as where to store the data. However, it seems that only one sample is stored. Can I somehow define the number of samples I want to take from the channel?

    Cheers,

    Bojan.

  • Hi Bojan,

    Yes, you can trigger sampling using the Zephyr API like that.

    When populating the adc_sequence struct you do not populate a pointer to options (adc_sequence_options). This is where you specify the number of samplings to perform with the extra_samplings field. You can see some examples of this in the test code in zephyr\tests\drivers\adc\adc_api\src\test_adc.c.

    Einar

  • Thanks, Einar, for all your help and clarifications.

    One more thing I would like you to help me with concerning ADC converters...

    How can we call nRF52840 ADC calibration function from Zephyr?

    Regards,

    Bojan.

  • Hi Bojan,

    You can set the calibrate flag in the adc_sequence struct to true to perform calibration before sampling. (You can see form the implementation in adc_nrfx_saadc.c that this triggers NRF_SAADC_TASK_CALIBRATEOFFSET).

    Einar

Related