<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://devzone.nordicsemi.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>[SAADC] Zephyr&amp;#39;s SAADC Driver (DMA Design choices analysis)</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/121131/saadc-zephyr-s-saadc-driver-dma-design-choices-analysis</link><description>Setup: NCS v2.8.0 nRF52840 
 Hi, 
 I would like to ask about the implementation of Zephyr&amp;#39;s SAADC driver (` adc_nrfx_saadc.c `). 
 In the project I&amp;#39;m working on, I need to sample the ADC at 16 kHz. However, I don&amp;#39;t need to process every individual sample</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Wed, 23 Jul 2025 14:52:33 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/121131/saadc-zephyr-s-saadc-driver-dma-design-choices-analysis" /><item><title>RE: [SAADC] Zephyr's SAADC Driver (DMA Design choices analysis)</title><link>https://devzone.nordicsemi.com/thread/543382?ContentTypeID=1</link><pubDate>Wed, 23 Jul 2025 14:52:33 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:9c238379-c68d-4bc5-a1fd-bafff09ad314</guid><dc:creator>Kazi Afroza Sultana</dc:creator><description>&lt;p&gt;Hello,&lt;/p&gt;
&lt;p&gt;&lt;span&gt;This should work for more than one channels as well: &lt;a title="https://academy.nordicsemi.com/courses/nrf-connect-sdk-intermediate/lessons/lesson-6-analog-to-digital-converter-adc/topic/exercise-3-interfacing-with-adc-using-nrfx-drivers-and-timer-ppi/" href="https://academy.nordicsemi.com/courses/nrf-connect-sdk-intermediate/lessons/lesson-6-analog-to-digital-converter-adc/topic/exercise-3-interfacing-with-adc-using-nrfx-drivers-and-timer-ppi/" rel="noopener noreferrer" target="_blank"&gt;https://academy.nordicsemi.com/courses/nrf-connect-sdk-intermediate/lessons/lesson-6-analog-to-digital-converter-adc/topic/exercise-3-interfacing-with-adc-using-nrfx-drivers-and-timer-ppi/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: [SAADC] Zephyr's SAADC Driver (DMA Design choices analysis)</title><link>https://devzone.nordicsemi.com/thread/542077?ContentTypeID=1</link><pubDate>Thu, 10 Jul 2025 09:48:35 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:7a0c59d3-9428-4f8d-8f22-0e528211663d</guid><dc:creator>Pawel(embeddedsolutions.pl)</dc:creator><description>&lt;p&gt;Hi again,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I am back here to share thoughts and decisions I finally made.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I ended up with implementing &lt;em&gt;&lt;strong&gt;NRFX_SAADC_PPI&lt;/strong&gt;&lt;/em&gt; driver compatible with Zephyr&amp;#39;s ADC API.&lt;br /&gt;The driver utilizes the &lt;code&gt;NRFX_SAADC&lt;/code&gt;,&amp;nbsp;&lt;code&gt;NRFX_TIMER&lt;/code&gt; and &lt;code&gt;NRFX_GPPI&lt;/code&gt;&amp;nbsp;APIs.&lt;br /&gt;SAADC works in scan mode with double-buffering. All defined channels are sampled with specified frequency.&lt;br /&gt;Users of the driver pull samples associated with&amp;nbsp;requested&amp;nbsp;channel/channels.&lt;br /&gt;&lt;br /&gt;It is important to&amp;nbsp;know which timer instances are used by other components of the system.&lt;br /&gt;On my side it turned out that I was using the same timer instance as BLE controller, so I had to change it to different one.&lt;br /&gt;Next challenge I encountered was validation of acquisition time, resolution, oversampling during build-time via preprocessor macros.&lt;br /&gt;I failed at this point and the validation is done on runtime at initialization.&lt;br /&gt;&lt;br /&gt;Thank you all for pointing me into right direction&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: [SAADC] Zephyr's SAADC Driver (DMA Design choices analysis)</title><link>https://devzone.nordicsemi.com/thread/539078?ContentTypeID=1</link><pubDate>Thu, 12 Jun 2025 14:37:04 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:dd61484e-c66b-4a9c-adfe-fa08e62e6f1f</guid><dc:creator>STE</dc:creator><description>&lt;p&gt;Dear Sultana,&lt;br /&gt;could you please let me know if by using same approach, then double buffering and PPI, instead to sample one single ADC channel as into example it is possible to sample more channels in sequence? My need is to take samples every 5ms of 4 analog input channels for a total of 400 samples for each channel, so the final buffer will be equal to 1600 samples, in such way every 2 seconds I should catch an event and then retrieve all the 1600 samples (that at the end will give to me 400 samples for each channel).&lt;/p&gt;
&lt;p&gt;Thanks for your suggest, best regards.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: [SAADC] Zephyr's SAADC Driver (DMA Design choices analysis)</title><link>https://devzone.nordicsemi.com/thread/535740?ContentTypeID=1</link><pubDate>Fri, 16 May 2025 09:51:22 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:562a00d5-7b19-404b-b087-106824845bd5</guid><dc:creator>Turbo J</dc:creator><description>&lt;p&gt;I don&amp;#39;t think you can reliably hit that tiny window with the BTLE radio in operation. Keep in mind that the radio (softdevice) code has to run at the highest priority and could thus preempt ADC code.&lt;/p&gt;
&lt;p&gt;Also seems like its the solution which would require &lt;em&gt;more&lt;/em&gt; development work and not less to me.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Edit: You also wanted to use a dedicated chip in case you intend to use Li-Ion or LiPo battery chemistry. These need charging ICs anyway and you can get those that can tell you the voltage (and battery percent) via a digital bus, typically I&amp;sup2;C.&lt;/p&gt;
&lt;p&gt;No, I don&amp;#39;t think you could run your code on a CR2032 - these won&amp;#39;t provide enough power to run an ADC this fast.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: [SAADC] Zephyr's SAADC Driver (DMA Design choices analysis)</title><link>https://devzone.nordicsemi.com/thread/535729?ContentTypeID=1</link><pubDate>Fri, 16 May 2025 09:07:04 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:cfba0630-7b1c-47fb-aadd-51bb062e9584</guid><dc:creator>Pawel(embeddedsolutions.pl)</dc:creator><description>&lt;p&gt;I understand your concerns but there are no ideal solutions, everything comes with a cost.&lt;/p&gt;
&lt;p&gt;By doing that in the way you proposed, we significantly reduce the maximum amount of samples collected for single channel, reducing the capturing window time (RESULT.MAXCNT / channels used)&lt;/p&gt;
&lt;p&gt;Battery measurement is something that could be called even once per few minutes.&lt;/p&gt;
&lt;p&gt;If maximum allowed frequency is going to be 16.666kHz there is ~60us in between sampling.&amp;nbsp;&lt;br /&gt;Do you feel that it is impossible to re-run the ADC&amp;nbsp; with different configuration in that time?&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I got your point clearly and I am partly convinced, I would like to avoid unnecessary work at this point, that is why I investigate alternative methods&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: [SAADC] Zephyr's SAADC Driver (DMA Design choices analysis)</title><link>https://devzone.nordicsemi.com/thread/535700?ContentTypeID=1</link><pubDate>Fri, 16 May 2025 07:13:28 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:d885bd04-2210-4a62-83fa-67bfc0c9cb7c</guid><dc:creator>Turbo J</dc:creator><description>&lt;p&gt;Terrible idea.&lt;/p&gt;
&lt;p&gt;The problem is that you will likely interrupt the &amp;quot;fast&amp;quot; ADC long enough that you create &amp;quot;gaps&amp;quot; where you miss sampling points. That is usually not acceptable for most applications.&lt;/p&gt;
&lt;p&gt;The solution is to simply measure &lt;em&gt;all&lt;/em&gt; ADC channels at the high frequency. Then either throw away the data that you don&amp;#39;t need or you take an average value and use that for the &amp;quot;slower&amp;quot; channels.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This way you have constant time between all sampling points - most applications prefer this method.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: [SAADC] Zephyr's SAADC Driver (DMA Design choices analysis)</title><link>https://devzone.nordicsemi.com/thread/535569?ContentTypeID=1</link><pubDate>Thu, 15 May 2025 12:14:47 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:e1144484-361c-4a61-82de-786f863f8d47</guid><dc:creator>Pawel(embeddedsolutions.pl)</dc:creator><description>&lt;p&gt;Hi,&lt;/p&gt;
&lt;p&gt;Thank you for bringing up this Nordic academy exercise.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;As far as I understood the code there, I believe it is suitable for the scenarios where the SAADC peripheral is used only&amp;nbsp;for single purpose.&lt;/p&gt;
&lt;p&gt;What I encountered is that I need to sample the ADC channels for different purposes e.g Audio + Battery voltage and these two cases are far far away from being similar.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;What I have done so far is bringing up the HAL NRF API and use it together with Zephyr&amp;#39;s ADC ASYNC API.&lt;br /&gt;That make my code Nordic specific but I am totally fine with it.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;static int read_continuous_mode(const struct device *dev)
{
	const struct amic_config *config = dev-&amp;gt;config;
	struct amic_data *data = dev-&amp;gt;data;

	/*
	 * Continuous mode requires bringing up the underlying HAL SAADC NRF API.
	 * The Zephyr&amp;#39;s SAADC driver is not able to handle the continuous mode.
	 */

	/* Disable interrupts to not be preempted before calling the `adc_read_async` */
	nrf_saadc_int_disable(NRF_SAADC, NRF_SAADC_INT_END | NRF_SAADC_INT_CALIBRATEDONE);

	/* Utilize the Zephyr&amp;#39;s ADC async API to set the resolution and oversampling configuration
	 */
	const int ret = adc_read_async(config-&amp;gt;port.dev, &amp;amp;data-&amp;gt;sequence, NULL);
	if (ret) {
		LOG_ERR(&amp;quot;Can&amp;#39;t read adc: %d&amp;quot;, ret);
		return ret;
	}

	/* Immediately STOP the SAADC sampling invoked by the `adc_read_async` */
	nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_STOP);

	/* Disable SAADC sampling to modify the configuration safely */
	nrf_saadc_disable(NRF_SAADC);

	/* Prepare buffer for SAADC DMA Continuous mode*/
	nrf_saadc_buffer_init(NRF_SAADC, (nrf_saadc_value_t *)data-&amp;gt;sequence.buffer,
			      config-&amp;gt;samples_num);

	/* Enable continuous mode with calculated `data-&amp;gt;interval_us` */
	nrf_saadc_continuous_mode_enable(NRF_SAADC,
					 (SAMPLERATE_FREQUENCY_RATIO * data-&amp;gt;interval_us));

	/* Clear the `NRF_SAADC_EVENT_END` event to ensure clear state */
	nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_END);
	/* Enable back the interrupts handled internally by the Zephyr&amp;#39;s ADC driver. On
	 * `NRF_SAADC_EVENT_END` event the `adc_sequence_callback_handler` is called
	 */
	nrf_saadc_int_enable(NRF_SAADC, NRF_SAADC_INT_END | NRF_SAADC_INT_CALIBRATEDONE);

	/* Start sampling in continuous mode */
	nrf_saadc_enable(NRF_SAADC);
	nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_START);
	nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE);

	LOG_DBG(&amp;quot;ADC continuous mode requested&amp;quot;);
	return 0;
}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The next challenge I believe is to synchronize the calls of functions which use the SAADC so they won&amp;#39;t interrupt with each other.&lt;br /&gt;That is not because I brought up the NRF HAL API but because ADC_ASYNC API is in use.&lt;br /&gt;&lt;br /&gt;Do you have any idea how this can be achievable? or maybe it is already handled by the ADC driver?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: [SAADC] Zephyr's SAADC Driver (DMA Design choices analysis)</title><link>https://devzone.nordicsemi.com/thread/533885?ContentTypeID=1</link><pubDate>Mon, 05 May 2025 10:42:04 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:c6ed06b7-03ad-4db6-befb-ca0f9c359aa7</guid><dc:creator>Kazi Afroza Sultana</dc:creator><description>&lt;p&gt;Hello,&lt;/p&gt;
&lt;p&gt;&lt;span&gt;For implementing a high-frequency ADC solution you may consider using TIMER and PPI/DPPI to trigger ADC sampling at precise intervals without CPU involvement. This previous case&amp;nbsp;&lt;a href="https://devzone.nordicsemi.com/f/nordic-q-a/101915/using-nrf52832-adc-for-audio-encoding/438477"&gt;(+) Using nRF52832 ADC for audio encoding - Nordic Q&amp;amp;A - Nordic DevZone - Nordic DevZone&lt;/a&gt;&amp;nbsp;described about the implementation of TIMER and PPI.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;For continuous sampling, you can implement double buffering also.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;pre class="ui-code" data-mode="text"&gt;/* STEP 4.1 - Define the buffer size for the SAADC */
#define SAADC_BUFFER_SIZE 8000

/* STEP 4.2 - Declare the buffers for the SAADC */
static int16_t saadc_sample_buffer[2][SAADC_BUFFER_SIZE];
static uint32_t saadc_current_buffer = 0;

static void saadc_event_handler(nrfx_saadc_evt_t const * p_event)
{
    nrfx_err_t err;
    switch (p_event-&amp;gt;type)
    {
        case NRFX_SAADC_EVT_READY:
            /* STEP 5.1 - Buffer is ready, timer (and sampling) can be started. */
            nrfx_timer_enable(&amp;amp;timer_instance);
            break;
            
        case NRFX_SAADC_EVT_BUF_REQ:
            /* STEP 5.2 - Set up the next available buffer. Alternate between buffer 0
             * and 1 */
            err = nrfx_saadc_buffer_set(
                saadc_sample_buffer[(saadc_current_buffer++) % 2], SAADC_BUFFER_SIZE);
            if (err != NRFX_SUCCESS) {
                LOG_ERR(&amp;quot;nrfx_saadc_buffer_set error: %08x&amp;quot;, err);
                return;
            }
            break;

        case NRFX_SAADC_EVT_DONE:
            /* STEP 5.3 - Buffer has been filled. Do something with the data */
            int64_t average = 0;
            int16_t max = INT16_MIN;
            int16_t min = INT16_MAX;
            int16_t current_value;
            
            for (int i = 0; i &amp;lt; p_event-&amp;gt;data.done.size; i++) {
                current_value = ((int16_t *)(p_event-&amp;gt;data.done.p_buffer))[i];
                average += current_value;
                if (current_value &amp;gt; max) {
                    max = current_value;
                }
                if (current_value &amp;lt; min) {
                    min = current_value;
                }
            }
            average = average / p_event-&amp;gt;data.done.size;
            LOG_INF(&amp;quot;SAADC buffer at 0x%x filled with %d samples&amp;quot;,
                    (uint32_t)p_event-&amp;gt;data.done.p_buffer, p_event-&amp;gt;data.done.size);
            LOG_INF(&amp;quot;AVG=%d, MIN=%d, MAX=%d&amp;quot;, (int16_t)average, min, max);
            break;
            
        default:
            LOG_INF(&amp;quot;Unhandled SAADC evt %d&amp;quot;, p_event-&amp;gt;type);
            break;
    }
}&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;&lt;span&gt;H&lt;/span&gt;ow to set up double-buffering for continuous SAADC sampling, including the use of two buffers, the event handler, and the use of TIMER and (D)PPI for precise sampling intervals.&lt;/div&gt;
&lt;div&gt;You can find the original source and detailed step-by-step instructions here:&amp;nbsp;&lt;a href="https://academy.nordicsemi.com/courses/nrf-connect-sdk-intermediate/lessons/lesson-6-analog-to-digital-converter-adc/topic/exercise-3-interfacing-with-adc-using-nrfx-drivers-and-timer-ppi/"&gt;https://academy.nordicsemi.com/courses/nrf-connect-sdk-intermediate/lessons/lesson-6-analog-to-digital-converter-adc/topic/exercise-3-interfacing-with-adc-using-nrfx-drivers-and-timer-ppi/&lt;/a&gt;&amp;nbsp;&lt;/div&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: [SAADC] Zephyr's SAADC Driver (DMA Design choices analysis)</title><link>https://devzone.nordicsemi.com/thread/533748?ContentTypeID=1</link><pubDate>Fri, 02 May 2025 12:19:18 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:709f4991-3e22-4f40-9402-d8ae074e08ba</guid><dc:creator>Pawel(embeddedsolutions.pl)</dc:creator><description>&lt;p&gt;Thanks, that explains much.&lt;br /&gt;&lt;br /&gt;Would&amp;nbsp;it be possible to use custom ADC driver together with Zephyr&amp;#39;s one?&amp;nbsp;&lt;br /&gt;Of course with proper synchronization.&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: [SAADC] Zephyr's SAADC Driver (DMA Design choices analysis)</title><link>https://devzone.nordicsemi.com/thread/533668?ContentTypeID=1</link><pubDate>Thu, 01 May 2025 12:00:18 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:0f5b9d04-468f-4b24-a0f1-8165f53cdcd2</guid><dc:creator>Turbo J</dc:creator><description>&lt;p&gt;The ADC drivers are written with low power usage in mind and not to be compatible to audio frequency.&lt;/p&gt;
&lt;p&gt;I would recommend not to fork the driver but to write a new one from scratch, possibly even without the NRFX hardware abstraction layer, as I have not check if this layer allows high sampling frequencies either.&lt;/p&gt;
&lt;p&gt;The idea is to do something similar to the audio/I&amp;sup2;S drivers, particularly how these handle lots of data with the k_mem_slab_ stuff.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>