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