samples: drivers: Add spis_wakeup sample

Sample showing how to use the additional wakeup signal
in nordic,nrf-spis driver. The application, configured
as a SPIS, is put to sleep while waiting for an SPI
transmission. An external device working as a SPIM and
also handling the wakeup signal wakes the application
up just before the transmission starts.

Signed-off-by: Piotr Krzyzanowski <piotr.krzyzanowski@nordicsemi.no>
This commit is contained in:
Piotr Krzyzanowski 2025-03-31 19:33:05 +02:00 committed by Benjamin Cabé
commit 41a996df94
11 changed files with 400 additions and 0 deletions

View file

@ -0,0 +1,18 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
#
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
if(NOT SYSBUILD)
message(FATAL_ERROR
" This is a multi-image application that should be built using sysbuild.\n"
" Add --sysbuild argument to west build command to prepare all the images.")
endif()
project(spis_wakeup)
target_sources(app PRIVATE src/main.c)

View file

@ -0,0 +1,73 @@
.. zephyr:code-sample:: spis-wakeup
:name: SPIS wake up
:relevant-api: spi_interface
Reduce current consumption by handling the wake line while using an SPIS.
Overview
********
Sample showing how to use the additional wake line in nrf-spis driver. The application, configured
as a SPIS, is put to sleep while waiting for an SPI transmission. An external device (other core in
this sample) working as a SPIM and also handling the wake line wakes the application up just before
the transmission starts. To simulate two separate devices the Nordic Semiconductor nRF54H20 DK has
been used and SPIS / SPIM drivers are controlled by two independent cores:
- nrf54h20dk/nrf54h20/cpuapp works as a SPIS controller device
- nrf54h20dk/nrf54h20/cpurad works as a SPIM controller device
Requirements
************
This sample can be run on multicore SoCs like nRF54H20 and requires additional wiring as below.
Wiring
******
Wires (jumpers) for connecting SPIS and SPIM devices are needed:
- SPIS WAKE <-> WAKE SPIM
- SPIS CS <-----> CS SPIM
- SPIS SCK <---> SCK SPIM
- SPIS MOSI <-> MOSI SPIM
- SPIS MISO <-> MISO SPIM
Building and Running
********************
The code can be found in :zephyr_file:`samples/boards/nordic/spis_wakeup`.
To build and flash the application:
.. zephyr-app-commands::
:zephyr-app: samples/boards/nordic/spis_wakeup
:board: nrf54h20dk/nrf54h20/cpuapp
:goals: build flash
:compact:
Sample Output
=============
nrf54h20/cpuapp:
.. code-block:: console
[00:00:00.272,217] <inf> spi_wakeup: Hello world from nrf54h20dk@0.9.0/nrf54h20/cpuapp
[00:00:00.272,241] <inf> spi_wakeup: SPIS: waiting for SPI transfer; going to sleep...
[00:00:02.274,192] <inf> spi_wakeup: SPIS: woken up by nrf54h20/cpurad
[00:00:02.274,215] <inf> spi_wakeup: SPIS: will be active for 500ms after transfer
nrf54h20/cpurad:
.. code-block:: console
[00:00:00.272,195] <inf> spi_wakeup: Hello world from nrf54h20dk@0.9.0/nrf54h20/cpurad
[00:00:00.272,219] <inf> spi_wakeup: SPIM: going to sleep for 1.5s...
[00:00:01.772,352] <inf> spi_wakeup: SPIM: will be active for 500ms before transfer
[00:00:02.273,456] <inf> spi_wakeup: SPIM: transferring the CONFIG_BOARD_QUALIFIERS: 'nrf54h20/cpurad'
References
**********
:zephyr_file:`dts/bindings/spi/nordic,nrf-spi-common.yaml`

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
/ {
aliases {
led = &led0;
spis = &spi130;
/delete-property/ led1;
/delete-property/ sw0;
/delete-property/ sw1;
/delete-property/ sw2;
/delete-property/ sw3;
};
/delete-node/ buttons;
};
/delete-node/ &led1;
&exmif {
status = "disabled";
};
&gpiote130 {
status = "okay";
owned-channels = <0>;
};
&pinctrl {
spi130_default_alt: spi130_default_alt {
group1 {
psels = <NRF_PSEL(SPIS_SCK, 0, 0)>,
<NRF_PSEL(SPIS_MOSI, 0, 6)>,
<NRF_PSEL(SPIS_MISO, 0, 8)>,
<NRF_PSEL(SPIS_CSN, 0, 10)>;
};
};
spi130_sleep_alt: spi130_sleep_alt {
group1 {
psels = <NRF_PSEL(SPIS_SCK, 0, 0)>,
<NRF_PSEL(SPIS_MOSI, 0, 6)>,
<NRF_PSEL(SPIS_MISO, 0, 8)>,
<NRF_PSEL(SPIS_CSN, 0, 10)>;
low-power-enable;
};
};
};
&spi130 {
compatible = "nordic,nrf-spis";
status = "okay";
def-char = <0x00>;
pinctrl-0 = <&spi130_default_alt>;
pinctrl-1 = <&spi130_sleep_alt>;
pinctrl-names = "default", "sleep";
wake-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
memory-regions = <&cpuapp_dma_region>;
/delete-property/rx-delay-supported;
/delete-property/rx-delay;
};
&uart136 {
zephyr,pm-device-runtime-auto;
};

View file

@ -0,0 +1,13 @@
CONFIG_SPI=y
CONFIG_SPI_SLAVE=y
CONFIG_GPIO=y
CONFIG_PM=y
CONFIG_PM_S2RAM=y
CONFIG_PM_S2RAM_CUSTOM_MARKING=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
CONFIG_ASSERT=y
CONFIG_LOG=y

View file

@ -0,0 +1,22 @@
common:
sysbuild: true
depends_on: spi
sample:
name: SPI wakeup sample
tests:
sample.drivers.spis.wakeup:
tags:
- spi
platform_allow:
- nrf54h20dk/nrf54h20/cpuapp
integration_platforms:
- nrf54h20dk/nrf54h20/cpuapp
harness: console
harness_config:
fixture: spi_loopback
type: multi_line
ordered: true
regex:
- ".*SPIS: waiting for SPI transfer"
- ".*SPIS: woken up by"

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/gpio.h>
#define BUF_SIZE 32
static const struct device *spis_dev = DEVICE_DT_GET(DT_ALIAS(spis));
static const struct spi_config spis_config = {.operation = SPI_OP_MODE_SLAVE | SPI_WORD_SET(8)};
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led), gpios);
LOG_MODULE_REGISTER(spi_wakeup);
int main(void)
{
bool status;
static char rx_buffer[BUF_SIZE];
struct spi_buf rx_spi_bufs = {.buf = rx_buffer, .len = sizeof(rx_buffer)};
struct spi_buf_set rx_spi_buf_set = {.buffers = &rx_spi_bufs, .count = 1};
LOG_INF("Hello world from %s", CONFIG_BOARD_TARGET);
status = gpio_is_ready_dt(&led);
__ASSERT(status, "Error: GPIO Device not ready");
status = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
__ASSERT(status == 0, "Could not configure led GPIO");
status = device_is_ready(spis_dev);
__ASSERT(status, "Error: SPI device is not ready");
while (1) {
memset(rx_buffer, 0x00, sizeof(rx_buffer));
LOG_INF("SPIS: waiting for SPI transfer; going to sleep...");
gpio_pin_set_dt(&led, 0);
spi_read(spis_dev, &spis_config, &rx_spi_buf_set);
gpio_pin_set_dt(&led, 1);
LOG_INF("SPIS: woken up by %s", rx_buffer);
LOG_INF("SPIS: will be active for 500ms after transfer");
k_busy_wait(500000);
}
return 0;
}

View file

@ -0,0 +1,14 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
#
if(SB_CONFIG_SOC_NRF54H20)
# Add remote project
ExternalZephyrProject_Add(
APPLICATION wakeup_trigger
SOURCE_DIR ${APP_DIR}/wakeup_trigger
BOARD ${SB_CONFIG_BOARD}/${SB_CONFIG_SOC}/cpurad
BOARD_REVISION ${BOARD_REVISION}
)
endif()

View file

@ -0,0 +1,12 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
#
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(wakeup_trigger)
target_sources(app PRIVATE src/main.c)

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2025 Nordic Semiconductor
* SPDX-License-Identifier: Apache-2.0
*/
/ {
aliases {
led = &led1;
};
leds {
compatible = "gpio-leds";
led1: led_1 {
gpios = <&gpio9 1 GPIO_ACTIVE_HIGH>;
label = "Green LED 1";
};
};
};
&gpiote130 {
status = "okay";
owned-channels = <1>;
};
&gpio0 {
status = "okay";
};
&gpio9 {
status = "okay";
};
&pinctrl {
spi131_default_alt: spi131_default_alt {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 0, 1)>,
<NRF_PSEL(SPIM_MOSI, 0, 7)>,
<NRF_PSEL(SPIM_MISO, 0, 9)>;
};
};
spi131_sleep_alt: spi131_sleep_alt {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 0, 1)>,
<NRF_PSEL(SPIM_MOSI, 0, 7)>,
<NRF_PSEL(SPIM_MISO, 0, 9)>;
low-power-enable;
};
};
};
&spi131 {
compatible = "nordic,nrf-spim";
status = "okay";
pinctrl-0 = <&spi131_default_alt>;
pinctrl-1 = <&spi131_sleep_alt>;
pinctrl-names = "default", "sleep";
overrun-character = <0x00>;
cs-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
wake-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
zephyr,pm-device-runtime-auto;
memory-regions = <&cpurad_dma_region>;
spim_dt: spi-device@0 {
compatible = "vnd,spi-device";
reg = <0>;
spi-max-frequency = <DT_FREQ_M(8)>;
};
};
&uart135 {
zephyr,pm-device-runtime-auto;
};

View file

@ -0,0 +1,13 @@
CONFIG_SPI=y
CONFIG_SPI_NRFX_WAKE_TIMEOUT_US=500
CONFIG_GPIO=y
CONFIG_PM=y
CONFIG_PM_S2RAM=y
CONFIG_PM_S2RAM_CUSTOM_MARKING=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
CONFIG_ASSERT=y
CONFIG_LOG=y

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/gpio.h>
#define SPI_MODE (SPI_OP_MODE_MASTER | SPI_WORD_SET(8))
static const struct spi_dt_spec spim = SPI_DT_SPEC_GET(DT_NODELABEL(spim_dt), SPI_MODE, 0);
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led), gpios);
LOG_MODULE_REGISTER(spi_wakeup);
int main(void)
{
bool status;
static char tx_buffer[] = CONFIG_BOARD_QUALIFIERS;
struct spi_buf tx_spi_bufs = {.buf = tx_buffer, .len = sizeof(tx_buffer)};
struct spi_buf_set tx_spi_buf_set = {.buffers = &tx_spi_bufs, .count = 1};
LOG_INF("Hello world from %s", CONFIG_BOARD_TARGET);
status = gpio_is_ready_dt(&led);
__ASSERT(status, "Error: GPIO Device not ready");
status = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
__ASSERT(status == 0, "Could not configure led GPIO");
status = spi_is_ready_dt(&spim);
__ASSERT(status, "Error: SPI device is not ready");
while (1) {
LOG_INF("SPIM: going to sleep for 1.5s...");
gpio_pin_set_dt(&led, 0);
k_msleep(1500);
gpio_pin_set_dt(&led, 1);
LOG_INF("SPIM: will be active for 500ms before transfer");
k_busy_wait(500000);
LOG_INF("SPIM: transferring the CONFIG_BOARD_QUALIFIERS: '%s'", tx_buffer);
spi_write_dt(&spim, &tx_spi_buf_set);
}
return 0;
}