1 /*
2 * Copyright (c) 2022 - 2024, Nordic Semiconductor ASA
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <nrfx_example.h>
35 #include <saadc_examples_common.h>
36 #include <nrfx_saadc.h>
37 #include <helpers/nrfx_gppi.h>
38 #include <nrfx_timer.h>
39 #include <nrfx_gpiote.h>
40
41 #define NRFX_LOG_MODULE EXAMPLE
42 #define NRFX_EXAMPLE_CONFIG_LOG_ENABLED 1
43 #define NRFX_EXAMPLE_CONFIG_LOG_LEVEL 3
44 #include <nrfx_log.h>
45
46 /**
47 * @defgroup nrfx_saadc_maximum_performance_example Maximum performance SAADC example
48 * @{
49 * @ingroup nrfx_saadc_examples
50 *
51 * @brief Example showing advanced functionality of nrfx_saadc driver operating at its peak performance.
52 *
53 * @details Application initializes nrfx_saadc driver and starts operating in the non-blocking mode. Sampling is
54 * performed at the highest supported frequency.
55 * In the example, @ref m_single_channel is configured and the SAADC driver is set to the advanced mode.
56 * To achieve maximum performance:
57 * - Performing sampling at @ref MAX_SAADC_SAMPLE_FREQUENCY requires an external timer. It is done
58 * by setting up endpoints of the channel @ref m_gppi_channels [ @p gppi_channels_purpose_t::SAADC_SAMPLING ]
59 * to trigger SAADC sample task ( @p nrf_saadc_task_t::NRF_SAADC_TASK_SAMPLE ) on the timer compare event.
60 * - Hardware start-on-end must be provided. It is done by setting up endpoints of the channel
61 * @ref m_gppi_channels [ @p gppi_channels_purpose_t::SAADC_START_ON_END ] to trigger SAADC task start
62 * ( @p nrf_saadc_task_t::NRF_SAADC_TASK_START ) on the SAADC event end ( @p nrf_saadc_event_t::NRF_SAADC_EVENT_END ).
63 *
64 * Calibration in a non-blocking manner is triggered by @p nrfx_saadc_offset_calibrate. Then at @p NRFX_SAADC_EVT_CALIBRATEDONE
65 * event in @ref saadc_handler() sampling is initiated by calling @p nrfx_saadc_mode_trigger() function.
66 * Consecutive sample tasks are triggered by the external timer at the sample rate specified in @ref SAADC_SAMPLE_FREQUENCY symbol.
67 *
68 * In the example there is GPPI channel configured to test the functionality of SAADC. The endpoints are setup up in a way
69 * that @p NRF_SAADC_EVENT_RESULTDONE event is connected with the GPIOTE task that toggles the @ref OUT_GPIO_PIN pin.
70 */
71
72 /** @brief Symbol specifying timer instance to be used. */
73 #define TIMER_INST_IDX 0
74
75 /** @brief Symbol specifying GPIOTE instance to be used. */
76 #define GPIOTE_INST_IDX 0
77
78 /** @brief Symbol specifying analog input to be observed by SAADC channel 0. */
79 #define CH0_AIN ANALOG_INPUT_TO_SAADC_AIN(ANALOG_INPUT_A0)
80
81 /** @brief Symbol specifying GPIO pin used to test the functionality of SAADC. */
82 #define OUT_GPIO_PIN LOOPBACK_PIN_1B
83
84 /** @brief Acquisition time [us] for source resistance <= 10 kOhm (see SAADC electrical specification). */
85 #define ACQ_TIME_10K 3UL
86
87 /** @brief Conversion time [us] (see SAADC electrical specification). */
88 #define CONV_TIME 2UL
89
90 /** @brief Symbol specifying maximal possible SAADC sample rate (see SAADC electrical specification). */
91 #define MAX_SAADC_SAMPLE_FREQUENCY 200000UL
92
93 /** @brief Symbol specifying SAADC sample frequency for the continuous sampling. */
94 #define SAADC_SAMPLE_FREQUENCY MAX_SAADC_SAMPLE_FREQUENCY
95
96 /** @brief Symbol specifying time in microseconds to wait for timer handler execution. */
97 #define TIME_TO_WAIT_US (uint32_t)(1000000UL / SAADC_SAMPLE_FREQUENCY)
98
99 /**
100 * @brief Symbol specifying the number of sample buffers ( @ref m_sample_buffers ).
101 * Two buffers are required for performing double-buffered conversions.
102 */
103 #define BUFFER_COUNT 2UL
104
105 /** @brief Symbol specifying the size of singular sample buffer ( @ref m_sample_buffers ). */
106 #define BUFFER_SIZE 2UL
107
108 /** @brief Symbol specifying the number of SAADC samplings to trigger. */
109 #define SAMPLING_ITERATIONS 3UL
110
111 /** @brief Symbol specifying the resolution of the SAADC. */
112 #define RESOLUTION NRF_SAADC_RESOLUTION_10BIT
113
114 /** @brief SAADC channel configuration structure for single channel use. */
115 static const nrfx_saadc_channel_t m_single_channel = SAADC_CHANNEL_SE_ACQ_3US(CH0_AIN, 0);
116
117 /** @brief Samples buffer to store values from a SAADC channel. */
118 #if (NRF_SAADC_8BIT_SAMPLE_WIDTH == 8) && (RESOLUTION == NRF_SAADC_RESOLUTION_8BIT)
119 static uint8_t m_sample_buffers[BUFFER_COUNT][BUFFER_SIZE];
120 #else
121 static uint16_t m_sample_buffers[BUFFER_COUNT][BUFFER_SIZE];
122 #endif
123
124 /** @brief Array of the GPPI channels. */
125 static uint8_t m_gppi_channels[2];
126
127 /** @brief Enum with intended uses of GPPI channels defined as @ref m_gppi_channels. */
128 typedef enum
129 {
130 SAADC_SAMPLING, ///< Triggers SAADC sampling task on external timer event.
131 SAADC_START_ON_END, ///< Triggers SAADC start task on SAADC end event.
132 } gppi_channels_purpose_t;
133
134 /** Maximum sampling rate of SAADC is 200 [kHz]. */
135 NRFX_STATIC_ASSERT(SAADC_SAMPLE_FREQUENCY <= (MAX_SAADC_SAMPLE_FREQUENCY));
136
137 /** For continuous sampling the sample rate SAADC_SAMPLE_FREQUENCY should fulfill the following criteria (see SAADC Continuous sampling). */
138 NRFX_STATIC_ASSERT(SAADC_SAMPLE_FREQUENCY <= (1000000UL / (ACQ_TIME_10K + CONV_TIME)));
139
140 /**
141 * @brief Function for handling SAADC driver events.
142 *
143 * @param[in] p_event Pointer to an SAADC driver event.
144 */
saadc_handler(nrfx_saadc_evt_t const * p_event)145 static void saadc_handler(nrfx_saadc_evt_t const * p_event)
146 {
147 nrfx_err_t status;
148 (void)status;
149
150 static uint16_t buffer_index = 1;
151 static uint16_t buf_req_evt_counter;
152 uint16_t samples_number;
153
154 switch(p_event->type)
155 {
156 case NRFX_SAADC_EVT_CALIBRATEDONE:
157 NRFX_LOG_INFO("SAADC event: CALIBRATEDONE");
158
159 status = nrfx_saadc_mode_trigger();
160 NRFX_ASSERT(status == NRFX_SUCCESS);
161 break;
162
163 case NRFX_SAADC_EVT_READY:
164 NRFX_LOG_INFO("SAADC event: READY");
165
166 nrfx_gppi_channels_enable(NRFX_BIT(m_gppi_channels[SAADC_SAMPLING]));
167 break;
168
169 case NRFX_SAADC_EVT_BUF_REQ:
170 NRFX_LOG_INFO("SAADC event: BUF_REQ");
171
172 if (++buf_req_evt_counter < SAMPLING_ITERATIONS)
173 {
174 /* Next available buffer must be set on the NRFX_SAADC_EVT_BUF_REQ event to achieve the continuous conversion. */
175 status = nrfx_saadc_buffer_set(m_sample_buffers[buffer_index++], BUFFER_SIZE);
176 NRFX_ASSERT(status == NRFX_SUCCESS);
177 buffer_index = buffer_index % BUFFER_COUNT;
178 }
179 else
180 {
181 nrfx_gppi_channels_disable(NRFX_BIT(m_gppi_channels[SAADC_START_ON_END]));
182 }
183 break;
184
185 case NRFX_SAADC_EVT_DONE:
186 NRFX_LOG_INFO("SAADC event: DONE");
187 NRFX_LOG_INFO("Sample buffer address == %p", p_event->data.done.p_buffer);
188
189 samples_number = p_event->data.done.size;
190 for (uint16_t i = 0; i < samples_number; i++)
191 {
192 NRFX_LOG_INFO("[Sample %u] value == %d",
193 i, NRFX_SAADC_SAMPLE_GET(RESOLUTION, p_event->data.done.p_buffer, i));
194 }
195 break;
196
197 case NRFX_SAADC_EVT_FINISHED:
198 NRFX_LOG_INFO("FINISHED");
199
200 nrfx_gppi_channels_disable(NRFX_BIT(m_gppi_channels[SAADC_SAMPLING]));
201 break;
202
203 default:
204 break;
205 }
206 }
207
208 /**
209 * @brief Function for handling TIMER driver events.
210 *
211 * @param[in] event_type Timer event.
212 * @param[in] p_context General purpose parameter set during initialization of the timer.
213 * This parameter can be used to pass additional information to the handler
214 * function for example the timer ID.
215 */
timer_handler(nrf_timer_event_t event_type,void * p_context)216 static void timer_handler(nrf_timer_event_t event_type, void * p_context)
217 {
218 (void)event_type;
219 (void)p_context;
220 }
221
222 /**
223 * @brief Function for application main entry.
224 *
225 * @return Nothing.
226 */
main(void)227 int main(void)
228 {
229 nrfx_err_t status;
230 (void)status;
231
232 #if defined(__ZEPHYR__)
233 IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_SAADC), IRQ_PRIO_LOWEST, nrfx_saadc_irq_handler, 0, 0);
234 IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_GPIOTE_INST_GET(GPIOTE_INST_IDX)), IRQ_PRIO_LOWEST,
235 NRFX_GPIOTE_INST_HANDLER_GET(GPIOTE_INST_IDX), 0, 0);
236 #endif
237
238 NRFX_EXAMPLE_LOG_INIT();
239 NRFX_LOG_INFO("Starting nrfx_saadc maximum performance example.");
240 NRFX_EXAMPLE_LOG_PROCESS();
241
242 status = nrfx_saadc_init(NRFX_SAADC_DEFAULT_CONFIG_IRQ_PRIORITY);
243 NRFX_ASSERT(status == NRFX_SUCCESS);
244
245 nrfx_timer_t timer_inst = NRFX_TIMER_INSTANCE(TIMER_INST_IDX);
246 uint32_t base_frequency = NRF_TIMER_BASE_FREQUENCY_GET(timer_inst.p_reg);
247 nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG(base_frequency);
248 timer_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
249 timer_config.p_context = &timer_inst;
250
251 status = nrfx_timer_init(&timer_inst, &timer_config, timer_handler);
252 NRFX_ASSERT(status == NRFX_SUCCESS);
253
254 nrfx_timer_clear(&timer_inst);
255
256 /* Creating variable desired_ticks to store the output of nrfx_timer_us_to_ticks function. */
257 uint32_t desired_ticks = nrfx_timer_us_to_ticks(&timer_inst, TIME_TO_WAIT_US);
258
259 /*
260 * Setting the timer channel NRF_TIMER_CC_CHANNEL0 in the extended compare mode to clear
261 * the timer and to not trigger an interrupt if the internal counter register is equal to
262 * desired_ticks.
263 */
264 nrfx_timer_extended_compare(&timer_inst,
265 NRF_TIMER_CC_CHANNEL0,
266 desired_ticks,
267 NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
268 false);
269
270 status = nrfx_saadc_channel_config(&m_single_channel);
271 NRFX_ASSERT(status == NRFX_SUCCESS);
272
273 /*
274 * Setting the advanced configuration with triggering sampling by the internal timer disabled
275 * (internal_timer_cc = 0) and without software start task on end event (start_on_end = false).
276 */
277 nrfx_saadc_adv_config_t adv_config = NRFX_SAADC_DEFAULT_ADV_CONFIG;
278 adv_config.internal_timer_cc = 0;
279 adv_config.start_on_end = false;
280
281 uint32_t channel_mask = nrfx_saadc_channels_configured_get();
282 status = nrfx_saadc_advanced_mode_set(channel_mask,
283 RESOLUTION,
284 &adv_config,
285 saadc_handler);
286 NRFX_ASSERT(status == NRFX_SUCCESS);
287
288 status = nrfx_saadc_buffer_set(m_sample_buffers[0], BUFFER_SIZE);
289 NRFX_ASSERT(status == NRFX_SUCCESS);
290
291 /*
292 * Allocate a dedicated channel and configure endpoints of that channel so that the timer compare event
293 * is connected with the SAADC sample task. This means that each time the timer interrupt occurs,
294 * the SAADC sampling will be triggered.
295 */
296 status = nrfx_gppi_channel_alloc(&m_gppi_channels[SAADC_SAMPLING]);
297 NRFX_ASSERT(status == NRFX_SUCCESS);
298
299 nrfx_gppi_channel_endpoints_setup(m_gppi_channels[SAADC_SAMPLING],
300 nrfx_timer_compare_event_address_get(&timer_inst, NRF_TIMER_CC_CHANNEL0),
301 nrf_saadc_task_address_get(NRF_SAADC, NRF_SAADC_TASK_SAMPLE));
302
303 /*
304 * Allocate a dedicated channel and configure endpoints of that so that the SAADC event end is connected
305 * with the SAADC task start. This means that each time the SAADC fills up the result buffer,
306 * the SAADC will be restarted and the result buffer will be prepared in RAM.
307 */
308 status = nrfx_gppi_channel_alloc(&m_gppi_channels[SAADC_START_ON_END]);
309 NRFX_ASSERT(status == NRFX_SUCCESS);
310
311 nrfx_gppi_channel_endpoints_setup(m_gppi_channels[SAADC_START_ON_END],
312 nrf_saadc_event_address_get(NRF_SAADC, NRF_SAADC_EVENT_END),
313 nrf_saadc_task_address_get(NRF_SAADC, NRF_SAADC_TASK_START));
314
315 nrfx_gpiote_t const gpiote_inst = NRFX_GPIOTE_INSTANCE(GPIOTE_INST_IDX);
316 status = nrfx_gpiote_init(&gpiote_inst, NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
317 NRFX_ASSERT(status == NRFX_SUCCESS);
318 NRFX_LOG_INFO("GPIOTE status: %s",
319 nrfx_gpiote_init_check(&gpiote_inst) ? "initialized" : "not initialized");
320
321 pin_on_event_toggle_setup(&gpiote_inst, OUT_GPIO_PIN,
322 nrf_saadc_event_address_get(NRF_SAADC, NRF_SAADC_EVENT_RESULTDONE));
323
324 nrfx_timer_enable(&timer_inst);
325
326 nrfx_gppi_channels_enable(NRFX_BIT(m_gppi_channels[SAADC_START_ON_END]));
327
328 status = nrfx_saadc_offset_calibrate(saadc_handler);
329 NRFX_ASSERT(status == NRFX_SUCCESS);
330
331 while (1)
332 {
333 NRFX_EXAMPLE_LOG_PROCESS();
334 }
335 }
336
337 /** @} */
338