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 	uint32_t sck_pin;
126 
127 	PINCTRL_DT_DEFINE(SPIM_NODE);
128 
129 	if (spim_initialized) {
130 		return true;
131 	}
132 
133 	/* If the UARTE is currently initialized, it must be deinitialized
134 	 * before the SPIM can be used.
135 	 */
136 	if (uarte_initialized) {
137 		nrfx_uarte_uninit(&uarte);
138 		/* Workaround: uninit does not clear events, make sure all events are cleared. */
139 		nrfy_uarte_int_init(uarte.p_reg, 0xFFFFFFFF, 0, false);
140 		uarte_initialized = false;
141 	}
142 
143 	nrfx_spim_config_t spim_config = NRFX_SPIM_DEFAULT_CONFIG(
144 		NRF_SPIM_PIN_NOT_CONNECTED,
145 		NRF_SPIM_PIN_NOT_CONNECTED,
146 		NRF_SPIM_PIN_NOT_CONNECTED,
147 		NRF_DT_GPIOS_TO_PSEL(SPIM_NODE, cs_gpios));
148 	spim_config.frequency = MHZ(1);
149 	spim_config.skip_gpio_cfg = true;
150 	spim_config.skip_psel_cfg = true;
151 
152 	ret = pinctrl_apply_state(PINCTRL_DT_DEV_CONFIG_GET(SPIM_NODE),
153 				  PINCTRL_STATE_DEFAULT);
154 	if (ret < 0) {
155 		return ret;
156 	}
157 
158 	/* Set initial state of SCK according to the SPI mode. */
159 	sck_pin = nrfy_spim_sck_pin_get(spim.p_reg);
160 
161 	if (sck_pin != NRF_SPIM_PIN_NOT_CONNECTED) {
162 		nrfy_gpio_pin_write(sck_pin, (spim_config.mode <= NRF_SPIM_MODE_1) ? 0 : 1);
163 	}
164 
165 	err = nrfx_spim_init(&spim, &spim_config, spim_handler, NULL);
166 	if (err != NRFX_SUCCESS) {
167 		printk("nrfx_spim_init() failed: 0x%08x\n", err);
168 		return false;
169 	}
170 
171 	spim_initialized = true;
172 	printk("Switched to SPIM\n");
173 	return true;
174 }
175 
spim_transfer(const uint8_t * tx_data,size_t tx_data_len,uint8_t * rx_buf,size_t rx_buf_size)176 static bool spim_transfer(const uint8_t *tx_data, size_t tx_data_len,
177 			  uint8_t *rx_buf, size_t rx_buf_size)
178 {
179 	nrfx_err_t err;
180 	nrfx_spim_xfer_desc_t xfer_desc = {
181 		.p_tx_buffer = tx_data,
182 		.tx_length = tx_data_len,
183 		.p_rx_buffer = rx_buf,
184 		.rx_length = rx_buf_size,
185 	};
186 
187 	err = nrfx_spim_xfer(&spim, &xfer_desc, 0);
188 	if (err != NRFX_SUCCESS) {
189 		printk("nrfx_spim_xfer() failed: 0x%08x\n", err);
190 		return false;
191 	}
192 
193 	if (k_sem_take(&transfer_finished, K_MSEC(100)) != 0) {
194 		printk("SPIM transfer timeout\n");
195 		return false;
196 	}
197 
198 	received = rx_buf_size;
199 	return true;
200 }
201 
uarte_handler(const nrfx_uarte_event_t * p_event,void * p_context)202 static void uarte_handler(const nrfx_uarte_event_t *p_event, void *p_context)
203 {
204 	if (p_event->type == NRFX_UARTE_EVT_RX_DONE) {
205 		received = p_event->data.rx.length;
206 		k_sem_give(&transfer_finished);
207 	} else if (p_event->type == NRFX_UARTE_EVT_ERROR) {
208 		received = 0;
209 		k_sem_give(&transfer_finished);
210 	}
211 }
212 
switch_to_uarte(void)213 static bool switch_to_uarte(void)
214 {
215 	int ret;
216 	nrfx_err_t err;
217 
218 	PINCTRL_DT_DEFINE(UARTE_NODE);
219 
220 	if (uarte_initialized) {
221 		return true;
222 	}
223 
224 	/* If the SPIM is currently initialized, it must be deinitialized
225 	 * before the UARTE can be used.
226 	 */
227 	if (spim_initialized) {
228 		nrfx_spim_uninit(&spim);
229 		/* Workaround: uninit does not clear events, make sure all events are cleared. */
230 		nrfy_spim_int_init(spim.p_reg, 0xFFFFFFFF, 0, false);
231 		spim_initialized = false;
232 	}
233 
234 	nrfx_uarte_config_t uarte_config = NRFX_UARTE_DEFAULT_CONFIG(
235 		NRF_UARTE_PSEL_DISCONNECTED,
236 		NRF_UARTE_PSEL_DISCONNECTED);
237 	uarte_config.baudrate = NRF_UARTE_BAUDRATE_1000000;
238 	uarte_config.skip_gpio_cfg = true;
239 	uarte_config.skip_psel_cfg = true;
240 
241 	ret = pinctrl_apply_state(PINCTRL_DT_DEV_CONFIG_GET(UARTE_NODE),
242 				  PINCTRL_STATE_DEFAULT);
243 	if (ret < 0) {
244 		return ret;
245 	}
246 
247 	err = nrfx_uarte_init(&uarte, &uarte_config, uarte_handler);
248 	if (err != NRFX_SUCCESS) {
249 		printk("nrfx_uarte_init() failed: 0x%08x\n", err);
250 		return false;
251 	}
252 
253 	uarte_initialized = true;
254 	printk("Switched to UARTE\n");
255 	return true;
256 }
257 
uarte_transfer(const uint8_t * tx_data,size_t tx_data_len,uint8_t * rx_buf,size_t rx_buf_size)258 static bool uarte_transfer(const uint8_t *tx_data, size_t tx_data_len,
259 			   uint8_t *rx_buf, size_t rx_buf_size)
260 {
261 	nrfx_err_t err;
262 
263 	err = nrfx_uarte_rx(&uarte, rx_buf, rx_buf_size);
264 	if (err != NRFX_SUCCESS) {
265 		printk("nrfx_uarte_rx() failed: 0x%08x\n", err);
266 		return false;
267 	}
268 
269 	err = nrfx_uarte_tx(&uarte, tx_data, tx_data_len, 0);
270 	if (err != NRFX_SUCCESS) {
271 		printk("nrfx_uarte_tx() failed: 0x%08x\n", err);
272 		return false;
273 	}
274 
275 	if (k_sem_take(&transfer_finished, K_MSEC(100)) != 0) {
276 		/* The UARTE transfer finishes when the RX buffer is completely
277 		 * filled. In case the UARTE receives less data (or nothing at
278 		 * all) within the specified time, taking the semaphore will
279 		 * fail. In such case, stop the reception and end the transfer
280 		 * this way. Now taking the semaphore should be successful.
281 		 */
282 		nrfx_uarte_rx_abort(&uarte, 0, 0);
283 		if (k_sem_take(&transfer_finished, K_MSEC(10)) != 0) {
284 			printk("UARTE transfer timeout\n");
285 			return false;
286 		}
287 	}
288 
289 	return true;
290 }
291 
buffer_dump(const uint8_t * buffer,size_t length)292 static void buffer_dump(const uint8_t *buffer, size_t length)
293 {
294 	for (int i = 0; i < length; ++i) {
295 		printk(" %02X", buffer[i]);
296 	}
297 	printk("\n");
298 }
299 
background_transfer(const struct device * spi_dev)300 static bool background_transfer(const struct device *spi_dev)
301 {
302 	static const uint8_t tx_buffer[] = "Nordic Semiconductor";
303 	static uint8_t rx_buffer[sizeof(tx_buffer)];
304 	static const struct spi_config spi_dev_cfg = {
305 		.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) |
306 			     SPI_TRANSFER_MSB,
307 		.frequency = MHZ(1),
308 		.cs = {
309 			.gpio = GPIO_DT_SPEC_GET(SPI_DEV_NODE, cs_gpios),
310 		},
311 	};
312 	static const struct spi_buf tx_buf = {
313 		.buf = (void *)tx_buffer,
314 		.len = sizeof(tx_buffer)
315 	};
316 	static const struct spi_buf_set tx = {
317 		.buffers = &tx_buf,
318 		.count = 1
319 	};
320 	static const struct spi_buf rx_buf = {
321 		.buf = rx_buffer,
322 		.len = sizeof(rx_buffer),
323 	};
324 	static const struct spi_buf_set rx = {
325 		.buffers = &rx_buf,
326 		.count = 1
327 	};
328 	int ret;
329 
330 	printk("-- Background transfer on \"%s\" --\n", spi_dev->name);
331 
332 	ret = spi_transceive(spi_dev, &spi_dev_cfg, &tx, &rx);
333 	if (ret < 0) {
334 		printk("Background transfer failed: %d\n", ret);
335 		return false;
336 	}
337 
338 	printk("Tx:");
339 	buffer_dump(tx_buf.buf, tx_buf.len);
340 	printk("Rx:");
341 	buffer_dump(rx_buf.buf, rx_buf.len);
342 	return true;
343 }
344 
main(void)345 int main(void)
346 {
347 	printk("nrfx PRS example on %s\n", CONFIG_BOARD);
348 
349 	static uint8_t tx_buffer[TRANSFER_LENGTH];
350 	static uint8_t rx_buffer[sizeof(tx_buffer)];
351 	uint8_t fill_value = 0;
352 	const struct device *const spi_dev = DEVICE_DT_GET(SPI_DEV_NODE);
353 
354 	if (!device_is_ready(spi_dev)) {
355 		printk("%s is not ready\n", spi_dev->name);
356 		return 0;
357 	}
358 
359 	/* Install a shared interrupt handler for peripherals used via
360 	 * nrfx drivers. It will dispatch the interrupt handling to the
361 	 * driver for the currently initialized peripheral.
362 	 */
363 	BUILD_ASSERT(
364 		DT_IRQ(SPIM_NODE, priority) == DT_IRQ(UARTE_NODE, priority),
365 		"Interrupt priorities for SPIM_NODE and UARTE_NODE need to be equal.");
366 	IRQ_CONNECT(DT_IRQN(SPIM_NODE), DT_IRQ(SPIM_NODE, priority),
367 		    nrfx_isr, nrfx_prs_box_2_irq_handler, 0);
368 
369 	if (!init_buttons()) {
370 		return 0;
371 	}
372 
373 	/* Initially use the SPIM. */
374 	if (!switch_to_spim()) {
375 		return 0;
376 	}
377 
378 	for (;;) {
379 		/* Wait 5 seconds for the user to press a button. If no button
380 		 * is pressed within this time, perform the background transfer.
381 		 * Otherwise, realize the operation requested by the user.
382 		 */
383 		if (k_sem_take(&button_pressed, K_MSEC(5000)) != 0) {
384 			if (!background_transfer(spi_dev)) {
385 				return 0;
386 			}
387 		} else {
388 			bool res;
389 
390 			switch (user_request) {
391 			case PERFORM_TRANSFER:
392 				printk("-- %s transfer --\n",
393 					spim_initialized ? "SPIM" : "UARTE");
394 				received = 0;
395 				for (int i = 0; i < sizeof(tx_buffer); ++i) {
396 					tx_buffer[i] = fill_value++;
397 				}
398 				res = (spim_initialized
399 				       ? spim_transfer(tx_buffer,
400 						       sizeof(tx_buffer),
401 						       rx_buffer,
402 						       sizeof(rx_buffer))
403 				       : uarte_transfer(tx_buffer,
404 							sizeof(tx_buffer),
405 							rx_buffer,
406 							sizeof(rx_buffer)));
407 				if (!res) {
408 					return 0;
409 				}
410 
411 				printk("Tx:");
412 				buffer_dump(tx_buffer, sizeof(tx_buffer));
413 				printk("Rx:");
414 				buffer_dump(rx_buffer, received);
415 				break;
416 
417 			case SWITCH_PERIPHERAL:
418 				res = (spim_initialized
419 				       ? switch_to_uarte()
420 				       : switch_to_spim());
421 				if (!res) {
422 					return 0;
423 				}
424 				break;
425 			}
426 		}
427 	}
428 	return 0;
429 }
430