blob: c827833d3b06b966befd87b2eec56929653386a7 [file] [log] [blame]
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/pinctrl.h>
#include <nrfx_spim.h>
#include <nrfx_uarte.h>
#include <drivers/src/prs/nrfx_prs.h>
#include <zephyr/irq.h>
#define TRANSFER_LENGTH 10
/* Devicetree nodes corresponding to the peripherals to be used directly via
* nrfx drivers (SPIM2 and UARTE2).
*/
#define SPIM_NODE DT_NODELABEL(spi2)
#define UARTE_NODE DT_NODELABEL(uart2)
/* Devicetree node corresponding to the peripheral to be used via Zephyr SPI
* driver (SPIM1), in the background transfer.
*/
#define SPI_DEV_NODE DT_NODELABEL(spi1)
static nrfx_spim_t spim = NRFX_SPIM_INSTANCE(2);
static nrfx_uarte_t uarte = NRFX_UARTE_INSTANCE(2);
static bool spim_initialized;
static bool uarte_initialized;
static volatile size_t received;
static K_SEM_DEFINE(transfer_finished, 0, 1);
static enum {
PERFORM_TRANSFER,
SWITCH_PERIPHERAL
} user_request;
static K_SEM_DEFINE(button_pressed, 0, 1);
static void sw0_handler(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
user_request = PERFORM_TRANSFER;
k_sem_give(&button_pressed);
}
static void sw1_handler(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
user_request = SWITCH_PERIPHERAL;
k_sem_give(&button_pressed);
}
static bool init_buttons(void)
{
static const struct button_spec {
struct gpio_dt_spec gpio;
char const *label;
char const *action;
gpio_callback_handler_t handler;
} btn_spec[] = {
{
GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios),
DT_PROP(DT_ALIAS(sw0), label),
"trigger a transfer",
sw0_handler
},
{
GPIO_DT_SPEC_GET(DT_ALIAS(sw1), gpios),
DT_PROP(DT_ALIAS(sw1), label),
"switch the type of peripheral",
sw1_handler
},
};
static struct gpio_callback btn_cb_data[ARRAY_SIZE(btn_spec)];
for (int i = 0; i < ARRAY_SIZE(btn_spec); ++i) {
const struct button_spec *btn = &btn_spec[i];
int ret;
if (!device_is_ready(btn->gpio.port)) {
printk("%s is not ready\n", btn->gpio.port->name);
return false;
}
ret = gpio_pin_configure_dt(&btn->gpio, GPIO_INPUT);
if (ret < 0) {
printk("Failed to configure %s pin %d: %d\n",
btn->gpio.port->name, btn->gpio.pin, ret);
return false;
}
ret = gpio_pin_interrupt_configure_dt(&btn->gpio,
GPIO_INT_EDGE_TO_ACTIVE);
if (ret < 0) {
printk("Failed to configure interrupt on %s pin %d: %d\n",
btn->gpio.port->name, btn->gpio.pin, ret);
return false;
}
gpio_init_callback(&btn_cb_data[i],
btn->handler, BIT(btn->gpio.pin));
gpio_add_callback(btn->gpio.port, &btn_cb_data[i]);
printk("-> press \"%s\" to %s\n", btn->label, btn->action);
}
return true;
}
static void spim_handler(const nrfx_spim_evt_t *p_event, void *p_context)
{
if (p_event->type == NRFX_SPIM_EVENT_DONE) {
k_sem_give(&transfer_finished);
}
}
static bool switch_to_spim(void)
{
int ret;
nrfx_err_t err;
PINCTRL_DT_DEFINE(SPIM_NODE);
if (spim_initialized) {
return true;
}
/* If the UARTE is currently initialized, it must be deinitialized
* before the SPIM can be used.
*/
if (uarte_initialized) {
nrfx_uarte_uninit(&uarte);
uarte_initialized = false;
}
nrfx_spim_config_t spim_config = NRFX_SPIM_DEFAULT_CONFIG(
NRFX_SPIM_PIN_NOT_USED,
NRFX_SPIM_PIN_NOT_USED,
NRFX_SPIM_PIN_NOT_USED,
NRF_DT_GPIOS_TO_PSEL(SPIM_NODE, cs_gpios));
spim_config.frequency = NRF_SPIM_FREQ_1M;
spim_config.skip_gpio_cfg = true;
spim_config.skip_psel_cfg = true;
ret = pinctrl_apply_state(PINCTRL_DT_DEV_CONFIG_GET(SPIM_NODE),
PINCTRL_STATE_DEFAULT);
if (ret < 0) {
return ret;
}
err = nrfx_spim_init(&spim, &spim_config, spim_handler, NULL);
if (err != NRFX_SUCCESS) {
printk("nrfx_spim_init() failed: 0x%08x\n", err);
return false;
}
spim_initialized = true;
printk("Switched to SPIM\n");
return true;
}
static bool spim_transfer(const uint8_t *tx_data, size_t tx_data_len,
uint8_t *rx_buf, size_t rx_buf_size)
{
nrfx_err_t err;
nrfx_spim_xfer_desc_t xfer_desc = {
.p_tx_buffer = tx_data,
.tx_length = tx_data_len,
.p_rx_buffer = rx_buf,
.rx_length = rx_buf_size,
};
err = nrfx_spim_xfer(&spim, &xfer_desc, 0);
if (err != NRFX_SUCCESS) {
printk("nrfx_spim_xfer() failed: 0x%08x\n", err);
return false;
}
if (k_sem_take(&transfer_finished, K_MSEC(100)) != 0) {
printk("SPIM transfer timeout\n");
return false;
}
received = rx_buf_size;
return true;
}
static void uarte_handler(const nrfx_uarte_event_t *p_event, void *p_context)
{
if (p_event->type == NRFX_UARTE_EVT_RX_DONE) {
received = p_event->data.rxtx.bytes;
k_sem_give(&transfer_finished);
} else if (p_event->type == NRFX_UARTE_EVT_ERROR) {
received = 0;
k_sem_give(&transfer_finished);
}
}
static bool switch_to_uarte(void)
{
int ret;
nrfx_err_t err;
PINCTRL_DT_DEFINE(UARTE_NODE);
if (uarte_initialized) {
return true;
}
/* If the SPIM is currently initialized, it must be deinitialized
* before the UARTE can be used.
*/
if (spim_initialized) {
nrfx_spim_uninit(&spim);
spim_initialized = false;
}
nrfx_uarte_config_t uarte_config = NRFX_UARTE_DEFAULT_CONFIG(
NRF_UARTE_PSEL_DISCONNECTED,
NRF_UARTE_PSEL_DISCONNECTED);
uarte_config.baudrate = NRF_UARTE_BAUDRATE_1000000;
uarte_config.skip_gpio_cfg = true;
uarte_config.skip_psel_cfg = true;
ret = pinctrl_apply_state(PINCTRL_DT_DEV_CONFIG_GET(UARTE_NODE),
PINCTRL_STATE_DEFAULT);
if (ret < 0) {
return ret;
}
err = nrfx_uarte_init(&uarte, &uarte_config, uarte_handler);
if (err != NRFX_SUCCESS) {
printk("nrfx_uarte_init() failed: 0x%08x\n", err);
return false;
}
uarte_initialized = true;
printk("Switched to UARTE\n");
return true;
}
static bool uarte_transfer(const uint8_t *tx_data, size_t tx_data_len,
uint8_t *rx_buf, size_t rx_buf_size)
{
nrfx_err_t err;
err = nrfx_uarte_rx(&uarte, rx_buf, rx_buf_size);
if (err != NRFX_SUCCESS) {
printk("nrfx_uarte_rx() failed: 0x%08x\n", err);
return false;
}
err = nrfx_uarte_tx(&uarte, tx_data, tx_data_len);
if (err != NRFX_SUCCESS) {
printk("nrfx_uarte_tx() failed: 0x%08x\n", err);
return false;
}
if (k_sem_take(&transfer_finished, K_MSEC(100)) != 0) {
/* The UARTE transfer finishes when the RX buffer is completely
* filled. In case the UARTE receives less data (or nothing at
* all) within the specified time, taking the semaphore will
* fail. In such case, stop the reception and end the transfer
* this way. Now taking the semaphore should be successful.
*/
nrfx_uarte_rx_abort(&uarte);
if (k_sem_take(&transfer_finished, K_MSEC(10)) != 0) {
printk("UARTE transfer timeout\n");
return false;
}
}
return true;
}
static void buffer_dump(const uint8_t *buffer, size_t length)
{
for (int i = 0; i < length; ++i) {
printk(" %02X", buffer[i]);
}
printk("\n");
}
static bool background_transfer(const struct device *spi_dev)
{
static const uint8_t tx_buffer[] = "Nordic Semiconductor";
static uint8_t rx_buffer[sizeof(tx_buffer)];
static const struct spi_cs_control spi_dev_cs_ctrl = {
.gpio = GPIO_DT_SPEC_GET(SPI_DEV_NODE, cs_gpios),
};
static const struct spi_config spi_dev_cfg = {
.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) |
SPI_TRANSFER_MSB,
.frequency = 1000000,
.cs = &spi_dev_cs_ctrl
};
static const struct spi_buf tx_buf = {
.buf = (void *)tx_buffer,
.len = sizeof(tx_buffer)
};
static const struct spi_buf_set tx = {
.buffers = &tx_buf,
.count = 1
};
static const struct spi_buf rx_buf = {
.buf = rx_buffer,
.len = sizeof(rx_buffer),
};
static const struct spi_buf_set rx = {
.buffers = &rx_buf,
.count = 1
};
int ret;
printk("-- Background transfer on \"%s\" --\n", spi_dev->name);
ret = spi_transceive(spi_dev, &spi_dev_cfg, &tx, &rx);
if (ret < 0) {
printk("Background transfer failed: %d\n", ret);
return false;
}
printk("Tx:");
buffer_dump(tx_buf.buf, tx_buf.len);
printk("Rx:");
buffer_dump(rx_buf.buf, rx_buf.len);
return true;
}
void main(void)
{
printk("nrfx PRS example on %s\n", CONFIG_BOARD);
static uint8_t tx_buffer[TRANSFER_LENGTH];
static uint8_t rx_buffer[sizeof(tx_buffer)];
uint8_t fill_value = 0;
const struct device *const spi_dev = DEVICE_DT_GET(SPI_DEV_NODE);
if (!device_is_ready(spi_dev)) {
printk("%s is not ready\n", spi_dev->name);
return;
}
/* Install a shared interrupt handler for peripherals used via
* nrfx drivers. It will dispatch the interrupt handling to the
* driver for the currently initialized peripheral.
*/
BUILD_ASSERT(
DT_IRQ(SPIM_NODE, priority) == DT_IRQ(UARTE_NODE, priority),
"Interrupt priorities for SPIM_NODE and UARTE_NODE need to be equal.");
IRQ_CONNECT(DT_IRQN(SPIM_NODE), DT_IRQ(SPIM_NODE, priority),
nrfx_isr, nrfx_prs_box_2_irq_handler, 0);
if (!init_buttons()) {
return;
}
/* Initially use the SPIM. */
if (!switch_to_spim()) {
return;
}
for (;;) {
/* Wait 5 seconds for the user to press a button. If no button
* is pressed within this time, perform the background transfer.
* Otherwise, realize the operation requested by the user.
*/
if (k_sem_take(&button_pressed, K_MSEC(5000)) != 0) {
if (!background_transfer(spi_dev)) {
return;
}
} else {
bool res;
switch (user_request) {
case PERFORM_TRANSFER:
printk("-- %s transfer --\n",
spim_initialized ? "SPIM" : "UARTE");
received = 0;
for (int i = 0; i < sizeof(tx_buffer); ++i) {
tx_buffer[i] = fill_value++;
}
res = (spim_initialized
? spim_transfer(tx_buffer,
sizeof(tx_buffer),
rx_buffer,
sizeof(rx_buffer))
: uarte_transfer(tx_buffer,
sizeof(tx_buffer),
rx_buffer,
sizeof(rx_buffer)));
if (!res) {
return;
}
printk("Tx:");
buffer_dump(tx_buffer, sizeof(tx_buffer));
printk("Rx:");
buffer_dump(rx_buffer, received);
break;
case SWITCH_PERIPHERAL:
res = (spim_initialized
? switch_to_uarte()
: switch_to_spim());
if (!res) {
return;
}
break;
}
}
}
}