Skip to content

Commit 4b2375f

Browse files
samples: application_development: introduce deinit
Introduce device deinit sample which demonstrates how to share hardware resources between application and device drivers using device_deinit(), specifically between a GPIO port and SPI peripheral. Signed-off-by: Bjarki Arge Andreasen <bjarki.andreasen@nordicsemi.no>
1 parent afa1db6 commit 4b2375f

File tree

7 files changed

+326
-0
lines changed

7 files changed

+326
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(deinit)
6+
7+
target_sources(app PRIVATE src/main.c)
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
.. zephyr:code-sample:: deinit
2+
:name: Device deinit
3+
:relevant-api: gpio_interface spi_interface device_model
4+
5+
Overview
6+
********
7+
8+
This sample demonstrates how to initialize and deinitialize device
9+
drivers at runtime, which allows switching between different devices
10+
and device drivers which share pins or share hardware.
11+
12+
Detailed description
13+
********************
14+
15+
This sample demonstrates how to share a SPI CS (chip-select) pin
16+
between a SPI peripheral and a GPIO port. The SPI CS pin chosen is
17+
connected to an LED on the board used for the sample. This sample
18+
blinks the LED, by either manually toggling the pin using the GPIO
19+
port, or performing a mock SPI transaction with ``SPI_HOLD_ON_CS``
20+
set which keeps the SPI CS pin asserted from the start of the SPI
21+
transaction until we call :c:func:`spi_release`.
22+
23+
We use ``zephyr,deferred-init`` to prevent initializing the SPI
24+
device driver during boot. The sample does the following after
25+
booting:
26+
27+
1. Configure the CS pin as an output in active state with
28+
:c:func:`gpio_pin_configure_dt`
29+
2. Wait
30+
3. Configure the CS pin as an input with
31+
:c:func:`gpio_pin_configure_dt`
32+
33+
This is the first blink
34+
35+
4. Initialize the SPI device driver with :c:func:`device_init`
36+
5. Perform mock SPI transaction with :c:func:`spi_transceive`
37+
6. Wait
38+
7. Release SPI with :c:func:`spi_release`
39+
40+
This is the second blink
41+
42+
8. Deinitialize SPI device driver with :c:func:`device_deinit`
43+
9. Configure the CS pin as an output in active state with
44+
:c:func:`gpio_pin_configure_dt`
45+
10. Wait
46+
11. Configure the CS pin as an input
47+
:c:func:`gpio_pin_configure_dt`
48+
49+
This is the third blink
50+
51+
12. Initialize the SPI device driver with :c:func:`device_init`
52+
13. Perform mock SPI transaction with :c:func:`spi_transceive`
53+
14. Wait
54+
15. Release SPI with :c:func:`spi_release`
55+
56+
This is the last blink.
57+
58+
16. print ``sample complete`` to console
59+
60+
If any errors occur during the sample, the error is printed to
61+
the console and the sample is stopped.
62+
63+
Porting guide
64+
*************
65+
66+
Use the following devicetree overlay example to create an
67+
overlay for your board:
68+
69+
.. code-block:: devicetree
70+
71+
&port0 {
72+
status = "okay";
73+
};
74+
75+
&spi0 {
76+
status = "okay";
77+
zephyr,deferred-init;
78+
cs-gpios = <&port0 0 GPIO_ACTIVE_LOW>;
79+
};
80+
81+
/ {
82+
zephyr,user {
83+
spi = <&spi0>;
84+
85+
/*
86+
* Must match &spi0 cs-gpios and should
87+
* preferably be a pin connected to an LED
88+
* on the board, which illuminates when
89+
* active.
90+
*/
91+
cs-gpios = <&port0 0 GPIO_ACTIVE_LOW>;
92+
};
93+
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
&pinctrl {
8+
lpuart0_default: lpuart0_default {
9+
group0 {
10+
pinmux = <LPSPI0_SCK_PTB2>,
11+
<LPSPI0_SIN_PTB3>,
12+
<LPSPI0_SOUT_PTB1>;
13+
bias-pull-up;
14+
drive-strength = "low";
15+
slew-rate = "slow";
16+
};
17+
};
18+
};
19+
20+
&lpspi0 {
21+
pinctrl-0 = <&lpuart0_default>;
22+
pinctrl-names = "default";
23+
status = "okay";
24+
zephyr,deferred-init;
25+
cs-gpios = <&gpiod 0 GPIO_ACTIVE_LOW>;
26+
};
27+
28+
&gpiod {
29+
status = "okay";
30+
};
31+
32+
/ {
33+
zephyr,user {
34+
spi = <&lpspi0>;
35+
cs-gpios = <&gpiod 0 GPIO_ACTIVE_LOW>;
36+
};
37+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
&spi4 {
8+
status = "okay";
9+
zephyr,deferred-init;
10+
cs-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>;
11+
};
12+
13+
/ {
14+
zephyr,user {
15+
spi = <&spi4>;
16+
cs-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>;
17+
};
18+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
CONFIG_SPI=y
2+
CONFIG_GPIO=y
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
sample:
2+
name: Device deinit
3+
tests:
4+
sample.application_development.deinit:
5+
platform_allow:
6+
- nrf5340dk/nrf5340/cpuapp
7+
harness: console
8+
harness_config:
9+
type: one_line
10+
regex:
11+
- "sample complete"
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/device.h>
9+
#include <zephyr/drivers/spi.h>
10+
#include <zephyr/drivers/gpio.h>
11+
12+
#define SPI_DEV_NODE DT_PROP(DT_PATH(zephyr_user), spi)
13+
14+
static const struct device *const spi_dev = DEVICE_DT_GET(SPI_DEV_NODE);
15+
static const struct gpio_dt_spec cs_pin = GPIO_DT_SPEC_GET(DT_PATH(zephyr_user), cs_gpios);
16+
17+
static uint8_t tx_buf[8];
18+
19+
static const struct spi_buf tx_buf_pool[1] = {
20+
{
21+
.buf = tx_buf,
22+
.len = sizeof(tx_buf),
23+
},
24+
};
25+
26+
static const struct spi_buf_set spi_tx_buf = {
27+
.buffers = tx_buf_pool,
28+
.count = ARRAY_SIZE(tx_buf_pool),
29+
};
30+
31+
static const struct spi_config config = {
32+
.frequency = DT_PROP_OR(SPI_DEV_NODE, max_frequency, 1000000),
33+
.operation = SPI_OP_MODE_MASTER | SPI_HOLD_ON_CS | SPI_WORD_SET(8),
34+
.slave = 0,
35+
.cs = {
36+
.gpio = GPIO_DT_SPEC_GET_OR(SPI_DEV_NODE, cs_gpios, {0}),
37+
.delay = 0,
38+
},
39+
};
40+
41+
static int control_cs_pin_with_gpio(void)
42+
{
43+
int ret;
44+
45+
ret = gpio_pin_configure_dt(&cs_pin, GPIO_OUTPUT_ACTIVE);
46+
if (ret) {
47+
printk("failed to configure CS pin\n");
48+
return 0;
49+
}
50+
51+
k_msleep(1000);
52+
53+
ret = gpio_pin_configure_dt(&cs_pin, GPIO_INPUT);
54+
if (ret) {
55+
printk("failed to configure CS pin\n");
56+
return 0;
57+
}
58+
59+
k_msleep(1000);
60+
return 0;
61+
}
62+
63+
static int control_cs_pin_with_spi(void)
64+
{
65+
int ret;
66+
67+
/*
68+
* Perform SPI transaction and keep CS pin asserted after
69+
* after transaction (see config.operation).
70+
*/
71+
ret = spi_transceive(spi_dev, &config, &spi_tx_buf, NULL);
72+
if (ret) {
73+
printk("failed to perform SPI transaction (ret %d)\n", ret);
74+
return 0;
75+
}
76+
77+
k_msleep(1000);
78+
79+
/* Release SPI device and thus CS pin shall be deasserted */
80+
ret = spi_release(spi_dev, &config);
81+
if (ret) {
82+
printk("failed to release SPI device (ret %d)\n", ret);
83+
return 0;
84+
}
85+
86+
k_msleep(1000);
87+
return 0;
88+
}
89+
90+
int main(void)
91+
{
92+
int ret;
93+
94+
/*
95+
* As we have specified deferred init for the SPI device in the devicetree, the driver
96+
* will not be initialized at this stage. We should have full control of the CS GPIO,
97+
* let's test that.
98+
*/
99+
ret = control_cs_pin_with_gpio();
100+
if (ret) {
101+
printk("failed to control CS pin with GPIO device driver (ret %d)\n", ret);
102+
return 0;
103+
}
104+
105+
/*
106+
* Now lets initialize the SPI device driver.
107+
*/
108+
ret = device_init(spi_dev);
109+
if (ret) {
110+
printk("failed to init SPI device driver (ret %d)\n", ret);
111+
return 0;
112+
}
113+
114+
/*
115+
* To control the CS pin in "human speed", lets perform a mock
116+
* transaction, holding the CS pin until we release the SPI device.
117+
*/
118+
ret = control_cs_pin_with_spi();
119+
if (ret) {
120+
printk("failed to control CS pin with SPI device driver (ret %d)\n", ret);
121+
return 0;
122+
}
123+
124+
/*
125+
* Lets deinit the SPI device driver an manually control the
126+
* CS pin again.
127+
*/
128+
ret = device_deinit(spi_dev);
129+
if (ret) {
130+
printk("failed to deinit SPI device driver (ret %d)\n", ret);
131+
return 0;
132+
}
133+
134+
ret = control_cs_pin_with_gpio();
135+
if (ret) {
136+
printk("failed to control CS pin with GPIO device driver (ret %d)\n", ret);
137+
return 0;
138+
}
139+
140+
/*
141+
* Lastly, lets check if we can bring back the SPI device driver.
142+
*/
143+
ret = device_init(spi_dev);
144+
if (ret) {
145+
printk("failed to init SPI device driver (ret %d)\n", ret);
146+
return 0;
147+
}
148+
149+
/* One final blink */
150+
ret = control_cs_pin_with_spi();
151+
if (ret) {
152+
printk("failed to control CS pin with SPI device driver (ret %d)\n", ret);
153+
return 0;
154+
}
155+
156+
printk("sample complete\n");
157+
return 0;
158+
}

0 commit comments

Comments
 (0)