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