1 /*
2  * Copyright (c) 2021 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/spi.h>
10 #include <zephyr/drivers/pinctrl.h>
11 
12 #include <nrfx_spim.h>
13 #include <nrfx_uarte.h>
14 #include <drivers/src/prs/nrfx_prs.h>
15 #include <zephyr/irq.h>
16 #include <zephyr/sys/util.h>
17 
18 #define TRANSFER_LENGTH 10
19 
20 /* Devicetree nodes corresponding to the peripherals to be used directly via
21  * nrfx drivers (SPIM2 and UARTE2).
22  */
23 #define SPIM_NODE  DT_NODELABEL(spi2)
24 #define UARTE_NODE DT_NODELABEL(uart2)
25 
26 /* Devicetree node corresponding to the peripheral to be used via Zephyr SPI
27  * driver (SPIM1), in the background transfer.
28  */
29 #define SPI_DEV_NODE DT_NODELABEL(spi1)
30 
31 static nrfx_spim_t spim = NRFX_SPIM_INSTANCE(2);
32 static nrfx_uarte_t uarte = NRFX_UARTE_INSTANCE(2);
33 static bool spim_initialized;
34 static bool uarte_initialized;
35 static volatile size_t received;
36 static K_SEM_DEFINE(transfer_finished, 0, 1);
37 
38 static enum {
39 	PERFORM_TRANSFER,
40 	SWITCH_PERIPHERAL
41 } user_request;
42 static K_SEM_DEFINE(button_pressed, 0, 1);
43 
sw0_handler(const struct device * dev,struct gpio_callback * cb,uint32_t pins)44 static void sw0_handler(const struct device *dev, struct gpio_callback *cb,
45 			uint32_t pins)
46 {
47 	user_request = PERFORM_TRANSFER;
48 	k_sem_give(&button_pressed);
49 }
50 
sw1_handler(const struct device * dev,struct gpio_callback * cb,uint32_t pins)51 static void sw1_handler(const struct device *dev, struct gpio_callback *cb,
52 			uint32_t pins)
53 {
54 	user_request = SWITCH_PERIPHERAL;
55 	k_sem_give(&button_pressed);
56 }
57 
init_buttons(void)58 static bool init_buttons(void)
59 {
60 	static const struct button_spec {
61 		struct gpio_dt_spec gpio;
62 		char const *label;
63 		char const *action;
64 		gpio_callback_handler_t handler;
65 	} btn_spec[] = {
66 		{
67 			GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios),
68 			DT_PROP(DT_ALIAS(sw0), label),
69 			"trigger a transfer",
70 			sw0_handler
71 		},
72 		{
73 			GPIO_DT_SPEC_GET(DT_ALIAS(sw1), gpios),
74 			DT_PROP(DT_ALIAS(sw1), label),
75 			"switch the type of peripheral",
76 			sw1_handler
77 		},
78 	};
79 	static struct gpio_callback btn_cb_data[ARRAY_SIZE(btn_spec)];
80 
81 	for (int i = 0; i < ARRAY_SIZE(btn_spec); ++i) {
82 		const struct button_spec *btn = &btn_spec[i];
83 		int ret;
84 
85 		if (!gpio_is_ready_dt(&btn->gpio)) {
86 			printk("%s is not ready\n", btn->gpio.port->name);
87 			return false;
88 		}
89 
90 		ret = gpio_pin_configure_dt(&btn->gpio, GPIO_INPUT);
91 		if (ret < 0) {
92 			printk("Failed to configure %s pin %d: %d\n",
93 				btn->gpio.port->name, btn->gpio.pin, ret);
94 			return false;
95 		}
96 
97 		ret = gpio_pin_interrupt_configure_dt(&btn->gpio,
98 						      GPIO_INT_EDGE_TO_ACTIVE);
99 		if (ret < 0) {
100 			printk("Failed to configure interrupt on %s pin %d: %d\n",
101 				btn->gpio.port->name, btn->gpio.pin, ret);
102 			return false;
103 		}
104 
105 		gpio_init_callback(&btn_cb_data[i],
106 				   btn->handler, BIT(btn->gpio.pin));
107 		gpio_add_callback(btn->gpio.port, &btn_cb_data[i]);
108 		printk("-> press \"%s\" to %s\n", btn->label, btn->action);
109 	}
110 
111 	return true;
112 }
113 
spim_handler(const nrfx_spim_evt_t * p_event,void * p_context)114 static void spim_handler(const nrfx_spim_evt_t *p_event, void *p_context)
115 {
116 	if (p_event->type == NRFX_SPIM_EVENT_DONE) {
117 		k_sem_give(&transfer_finished);
118 	}
119 }
120 
switch_to_spim(void)121 static bool switch_to_spim(void)
122 {
123 	int ret;
124 	nrfx_err_t err;
125 
126 	PINCTRL_DT_DEFINE(SPIM_NODE);
127 
128 	if (spim_initialized) {
129 		return true;
130 	}
131 
132 	/* If the UARTE is currently initialized, it must be deinitialized
133 	 * before the SPIM can be used.
134 	 */
135 	if (uarte_initialized) {
136 		nrfx_uarte_uninit(&uarte);
137 		/* Workaround: uninit does not clear events, make sure all events are cleared. */
138 		nrfy_uarte_int_init(uarte.p_reg, 0xFFFFFFFF, 0, false);
139 		uarte_initialized = false;
140 	}
141 
142 	nrfx_spim_config_t spim_config = NRFX_SPIM_DEFAULT_CONFIG(
143 		NRF_SPIM_PIN_NOT_CONNECTED,
144 		NRF_SPIM_PIN_NOT_CONNECTED,
145 		NRF_SPIM_PIN_NOT_CONNECTED,
146 		NRF_DT_GPIOS_TO_PSEL(SPIM_NODE, cs_gpios));
147 	spim_config.frequency = MHZ(1);
148 	spim_config.skip_gpio_cfg = true;
149 	spim_config.skip_psel_cfg = true;
150 
151 	ret = pinctrl_apply_state(PINCTRL_DT_DEV_CONFIG_GET(SPIM_NODE),
152 				  PINCTRL_STATE_DEFAULT);
153 	if (ret < 0) {
154 		return ret;
155 	}
156 
157 	/* Set initial state of SCK according to the SPI mode. */
158 	nrfy_gpio_pin_write(nrfy_spim_sck_pin_get(spim.p_reg),
159 			    (spim_config.mode <= NRF_SPIM_MODE_1) ? 0 : 1);
160 
161 	err = nrfx_spim_init(&spim, &spim_config, spim_handler, NULL);
162 	if (err != NRFX_SUCCESS) {
163 		printk("nrfx_spim_init() failed: 0x%08x\n", err);
164 		return false;
165 	}
166 
167 	spim_initialized = true;
168 	printk("Switched to SPIM\n");
169 	return true;
170 }
171 
spim_transfer(const uint8_t * tx_data,size_t tx_data_len,uint8_t * rx_buf,size_t rx_buf_size)172 static bool spim_transfer(const uint8_t *tx_data, size_t tx_data_len,
173 			  uint8_t *rx_buf, size_t rx_buf_size)
174 {
175 	nrfx_err_t err;
176 	nrfx_spim_xfer_desc_t xfer_desc = {
177 		.p_tx_buffer = tx_data,
178 		.tx_length = tx_data_len,
179 		.p_rx_buffer = rx_buf,
180 		.rx_length = rx_buf_size,
181 	};
182 
183 	err = nrfx_spim_xfer(&spim, &xfer_desc, 0);
184 	if (err != NRFX_SUCCESS) {
185 		printk("nrfx_spim_xfer() failed: 0x%08x\n", err);
186 		return false;
187 	}
188 
189 	if (k_sem_take(&transfer_finished, K_MSEC(100)) != 0) {
190 		printk("SPIM transfer timeout\n");
191 		return false;
192 	}
193 
194 	received = rx_buf_size;
195 	return true;
196 }
197 
uarte_handler(const nrfx_uarte_event_t * p_event,void * p_context)198 static void uarte_handler(const nrfx_uarte_event_t *p_event, void *p_context)
199 {
200 	if (p_event->type == NRFX_UARTE_EVT_RX_DONE) {
201 		received = p_event->data.rx.length;
202 		k_sem_give(&transfer_finished);
203 	} else if (p_event->type == NRFX_UARTE_EVT_ERROR) {
204 		received = 0;
205 		k_sem_give(&transfer_finished);
206 	}
207 }
208 
switch_to_uarte(void)209 static bool switch_to_uarte(void)
210 {
211 	int ret;
212 	nrfx_err_t err;
213 
214 	PINCTRL_DT_DEFINE(UARTE_NODE);
215 
216 	if (uarte_initialized) {
217 		return true;
218 	}
219 
220 	/* If the SPIM is currently initialized, it must be deinitialized
221 	 * before the UARTE can be used.
222 	 */
223 	if (spim_initialized) {
224 		nrfx_spim_uninit(&spim);
225 		/* Workaround: uninit does not clear events, make sure all events are cleared. */
226 		nrfy_spim_int_init(spim.p_reg, 0xFFFFFFFF, 0, false);
227 		spim_initialized = false;
228 	}
229 
230 	nrfx_uarte_config_t uarte_config = NRFX_UARTE_DEFAULT_CONFIG(
231 		NRF_UARTE_PSEL_DISCONNECTED,
232 		NRF_UARTE_PSEL_DISCONNECTED);
233 	uarte_config.baudrate = NRF_UARTE_BAUDRATE_1000000;
234 	uarte_config.skip_gpio_cfg = true;
235 	uarte_config.skip_psel_cfg = true;
236 
237 	ret = pinctrl_apply_state(PINCTRL_DT_DEV_CONFIG_GET(UARTE_NODE),
238 				  PINCTRL_STATE_DEFAULT);
239 	if (ret < 0) {
240 		return ret;
241 	}
242 
243 	err = nrfx_uarte_init(&uarte, &uarte_config, uarte_handler);
244 	if (err != NRFX_SUCCESS) {
245 		printk("nrfx_uarte_init() failed: 0x%08x\n", err);
246 		return false;
247 	}
248 
249 	uarte_initialized = true;
250 	printk("Switched to UARTE\n");
251 	return true;
252 }
253 
uarte_transfer(const uint8_t * tx_data,size_t tx_data_len,uint8_t * rx_buf,size_t rx_buf_size)254 static bool uarte_transfer(const uint8_t *tx_data, size_t tx_data_len,
255 			   uint8_t *rx_buf, size_t rx_buf_size)
256 {
257 	nrfx_err_t err;
258 
259 	err = nrfx_uarte_rx(&uarte, rx_buf, rx_buf_size);
260 	if (err != NRFX_SUCCESS) {
261 		printk("nrfx_uarte_rx() failed: 0x%08x\n", err);
262 		return false;
263 	}
264 
265 	err = nrfx_uarte_tx(&uarte, tx_data, tx_data_len, 0);
266 	if (err != NRFX_SUCCESS) {
267 		printk("nrfx_uarte_tx() failed: 0x%08x\n", err);
268 		return false;
269 	}
270 
271 	if (k_sem_take(&transfer_finished, K_MSEC(100)) != 0) {
272 		/* The UARTE transfer finishes when the RX buffer is completely
273 		 * filled. In case the UARTE receives less data (or nothing at
274 		 * all) within the specified time, taking the semaphore will
275 		 * fail. In such case, stop the reception and end the transfer
276 		 * this way. Now taking the semaphore should be successful.
277 		 */
278 		nrfx_uarte_rx_abort(&uarte, 0, 0);
279 		if (k_sem_take(&transfer_finished, K_MSEC(10)) != 0) {
280 			printk("UARTE transfer timeout\n");
281 			return false;
282 		}
283 	}
284 
285 	return true;
286 }
287 
buffer_dump(const uint8_t * buffer,size_t length)288 static void buffer_dump(const uint8_t *buffer, size_t length)
289 {
290 	for (int i = 0; i < length; ++i) {
291 		printk(" %02X", buffer[i]);
292 	}
293 	printk("\n");
294 }
295 
background_transfer(const struct device * spi_dev)296 static bool background_transfer(const struct device *spi_dev)
297 {
298 	static const uint8_t tx_buffer[] = "Nordic Semiconductor";
299 	static uint8_t rx_buffer[sizeof(tx_buffer)];
300 	static const struct spi_config spi_dev_cfg = {
301 		.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) |
302 			     SPI_TRANSFER_MSB,
303 		.frequency = MHZ(1),
304 		.cs = {
305 			.gpio = GPIO_DT_SPEC_GET(SPI_DEV_NODE, cs_gpios),
306 		},
307 	};
308 	static const struct spi_buf tx_buf = {
309 		.buf = (void *)tx_buffer,
310 		.len = sizeof(tx_buffer)
311 	};
312 	static const struct spi_buf_set tx = {
313 		.buffers = &tx_buf,
314 		.count = 1
315 	};
316 	static const struct spi_buf rx_buf = {
317 		.buf = rx_buffer,
318 		.len = sizeof(rx_buffer),
319 	};
320 	static const struct spi_buf_set rx = {
321 		.buffers = &rx_buf,
322 		.count = 1
323 	};
324 	int ret;
325 
326 	printk("-- Background transfer on \"%s\" --\n", spi_dev->name);
327 
328 	ret = spi_transceive(spi_dev, &spi_dev_cfg, &tx, &rx);
329 	if (ret < 0) {
330 		printk("Background transfer failed: %d\n", ret);
331 		return false;
332 	}
333 
334 	printk("Tx:");
335 	buffer_dump(tx_buf.buf, tx_buf.len);
336 	printk("Rx:");
337 	buffer_dump(rx_buf.buf, rx_buf.len);
338 	return true;
339 }
340 
main(void)341 int main(void)
342 {
343 	printk("nrfx PRS example on %s\n", CONFIG_BOARD);
344 
345 	static uint8_t tx_buffer[TRANSFER_LENGTH];
346 	static uint8_t rx_buffer[sizeof(tx_buffer)];
347 	uint8_t fill_value = 0;
348 	const struct device *const spi_dev = DEVICE_DT_GET(SPI_DEV_NODE);
349 
350 	if (!device_is_ready(spi_dev)) {
351 		printk("%s is not ready\n", spi_dev->name);
352 		return 0;
353 	}
354 
355 	/* Install a shared interrupt handler for peripherals used via
356 	 * nrfx drivers. It will dispatch the interrupt handling to the
357 	 * driver for the currently initialized peripheral.
358 	 */
359 	BUILD_ASSERT(
360 		DT_IRQ(SPIM_NODE, priority) == DT_IRQ(UARTE_NODE, priority),
361 		"Interrupt priorities for SPIM_NODE and UARTE_NODE need to be equal.");
362 	IRQ_CONNECT(DT_IRQN(SPIM_NODE), DT_IRQ(SPIM_NODE, priority),
363 		    nrfx_isr, nrfx_prs_box_2_irq_handler, 0);
364 
365 	if (!init_buttons()) {
366 		return 0;
367 	}
368 
369 	/* Initially use the SPIM. */
370 	if (!switch_to_spim()) {
371 		return 0;
372 	}
373 
374 	for (;;) {
375 		/* Wait 5 seconds for the user to press a button. If no button
376 		 * is pressed within this time, perform the background transfer.
377 		 * Otherwise, realize the operation requested by the user.
378 		 */
379 		if (k_sem_take(&button_pressed, K_MSEC(5000)) != 0) {
380 			if (!background_transfer(spi_dev)) {
381 				return 0;
382 			}
383 		} else {
384 			bool res;
385 
386 			switch (user_request) {
387 			case PERFORM_TRANSFER:
388 				printk("-- %s transfer --\n",
389 					spim_initialized ? "SPIM" : "UARTE");
390 				received = 0;
391 				for (int i = 0; i < sizeof(tx_buffer); ++i) {
392 					tx_buffer[i] = fill_value++;
393 				}
394 				res = (spim_initialized
395 				       ? spim_transfer(tx_buffer,
396 						       sizeof(tx_buffer),
397 						       rx_buffer,
398 						       sizeof(rx_buffer))
399 				       : uarte_transfer(tx_buffer,
400 							sizeof(tx_buffer),
401 							rx_buffer,
402 							sizeof(rx_buffer)));
403 				if (!res) {
404 					return 0;
405 				}
406 
407 				printk("Tx:");
408 				buffer_dump(tx_buffer, sizeof(tx_buffer));
409 				printk("Rx:");
410 				buffer_dump(rx_buffer, received);
411 				break;
412 
413 			case SWITCH_PERIPHERAL:
414 				res = (spim_initialized
415 				       ? switch_to_uarte()
416 				       : switch_to_spim());
417 				if (!res) {
418 					return 0;
419 				}
420 				break;
421 			}
422 		}
423 	}
424 	return 0;
425 }
426