ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
spi.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file spi.h
6 * @brief The one owner of the shared SPI bus for the peripheral drivers.
7 *
8 * The sibling of i2c.h, for the drivers whose part is on SPI rather than I2C (the interface
9 * bridge, the W5500 Ethernet, the radio modules). They share one bus and bring it up through
10 * these verbs. The pins come from PROTOCORE_SPI_MOSI_PIN / PROTOCORE_SPI_MISO_PIN / PROTOCORE_SPI_SCLK_PIN (default
11 * -1 = the platform's default host pins). Re-begin is idempotent, so per-driver calls are
12 * harmless.
13 *
14 * SPI is symmetric: every clock shifts a bit out and a bit in, so one transfer verb covers the
15 * three shapes a driver needs. Pass a null @p rx to discard what comes back (a write), a null
16 * @p tx to clock zeros out (a read), or both to exchange.
17 *
18 * ::protocore_spi_txn runs at the configured clock, bit order and mode; ::protocore_spi_txn_at names its own,
19 * which is what a bus carrying parts with different timing needs.
20 *
21 * ::protocore_spi_txn_ext adds the framing a flash, a display controller or an ADC front end expects: a
22 * command, an address, dummy clocks, and a data phase one, two or four bits wide. The controller
23 * drives each phase, so the data buffer holds data alone.
24 *
25 * Chip select is the caller's: which pin selects a part is a board fact, and a driver often holds
26 * it across several transfers. ::protocore_spi_cs_idle / ::protocore_spi_cs_select / ::protocore_spi_cs_release drive
27 * it through the GPIO seam.
28 *
29 * The bodies compile wherever the platform states a bus.
30 *
31 * @author Douglas Quigg (dstroy0)
32 * @date 2026
33 */
34
35#ifndef PROTOCORE_SPI_H
36#define PROTOCORE_SPI_H
37
39
40#include "protocore_config.h"
41
42/** @brief Bus clock for the shared peripheral bus; 1 MHz is safe on every part on it. */
43#ifndef PROTOCORE_SPI_HZ
44#define PROTOCORE_SPI_HZ 1000000u
45#endif
46
47/** @brief Clock polarity and phase, as SPI mode 0..3. Mode 0 is what these parts use. */
48#ifndef PROTOCORE_SPI_MODE
49#define PROTOCORE_SPI_MODE 0u
50#endif
51
52/** @brief Controller the plain verbs drive. */
53#ifndef PROTOCORE_SPI_HOST
54#define PROTOCORE_SPI_HOST 0u
55#endif
56
57/** @brief Third and fourth data lines, for a quad-width bus; -1 leaves the bus single or dual. */
58#ifndef PROTOCORE_SPI_QUADWP_PIN
59#define PROTOCORE_SPI_QUADWP_PIN (-1)
60#endif
61#ifndef PROTOCORE_SPI_QUADHD_PIN
62#define PROTOCORE_SPI_QUADHD_PIN (-1)
63#endif
64
66
67#if PROTOCORE_HAS_BUS
68
69/** @brief Bring up @p host on the given pins; -1 on quadwp / quadhd leaves the bus single or dual. */
70PROTOCORE_INLINE proto_bool protocore_spi_begin_on(uint8_t host, int mosi, int miso, int sclk, int quadwp, int quadhd)
71{
72 return protocore_platform_spi_begin(host, mosi, miso, sclk, quadwp, quadhd) != 0;
73}
74
75/** @brief Bring up the shared SPI bus on the PROTOCORE_SPI_*_PIN pins (-1 = default). */
77{
81}
82
83/**
84 * @brief Clock @p len bytes on @p host at @p hz, @p bit_order and @p mode, shifting @p tx out and
85 * @p rx in. A null @p rx discards the inbound bits; a null @p tx clocks zeros out.
86 */
87PROTOCORE_INLINE proto_bool protocore_spi_txn_on(uint8_t host, uint32_t hz, uint8_t bit_order, uint8_t mode,
88 const uint8_t *tx, uint8_t *rx, size_t len)
89{
90 return protocore_platform_spi_txn(host, hz, bit_order, mode, tx, rx, (uint32_t)len) != 0;
91}
92
93/** @brief Clock @p len bytes on the shared bus at @p hz, @p bit_order and @p mode. */
94PROTOCORE_INLINE proto_bool protocore_spi_txn_at(uint32_t hz, uint8_t bit_order, uint8_t mode, const uint8_t *tx,
95 uint8_t *rx, size_t len)
96{
97 return protocore_spi_txn_on((uint8_t)PROTOCORE_SPI_HOST, hz, bit_order, mode, tx, rx, len);
98}
99
100/**
101 * @brief Clock @p len bytes, shifting @p tx out and @p rx in.
102 *
103 * A null @p rx discards the inbound bits; a null @p tx clocks zeros out.
104 */
105PROTOCORE_INLINE proto_bool protocore_spi_txn(const uint8_t *tx, uint8_t *rx, size_t len)
106{
107 return protocore_spi_txn_at(PROTOCORE_SPI_HZ, PROTOCORE_SPI_MSBFIRST, (uint8_t)PROTOCORE_SPI_MODE, tx, rx, len);
108}
109
110/** @brief Clock @p len bytes out, discarding what shifts back. */
111PROTOCORE_INLINE proto_bool protocore_spi_write(const uint8_t *tx, size_t len)
112{
113 return protocore_spi_txn(tx, NULL, len);
114}
115
116/** @brief Clock @p len zero bytes out, keeping what shifts back. */
117PROTOCORE_INLINE proto_bool protocore_spi_read(uint8_t *rx, size_t len)
118{
119 return protocore_spi_txn(NULL, rx, len);
120}
121
122/**
123 * @brief A framed transfer on @p host: a @p cmd_bits command, an @p addr_bits address,
124 * @p dummy_bits idle clocks, then @p len data bytes at @p lanes bits per clock.
125 *
126 * A zero bit count omits that phase. @p lanes is PROTOCORE_SPI_LANES_1, _2 or _4.
127 */
128PROTOCORE_INLINE proto_bool protocore_spi_txn_ext_on(uint8_t host, uint32_t hz, uint8_t bit_order, uint8_t mode,
129 uint16_t cmd, uint8_t cmd_bits, uint32_t addr, uint8_t addr_bits,
130 uint8_t dummy_bits, uint8_t lanes, const uint8_t *tx, uint8_t *rx,
131 size_t len)
132{
133 return protocore_platform_spi_txn_ext(host, hz, bit_order, mode, cmd, cmd_bits, addr, addr_bits, dummy_bits, lanes,
134 tx, rx, (uint32_t)len) != 0;
135}
136
137/** @brief A framed transfer on the shared bus at the configured clock, bit order and mode. */
138PROTOCORE_INLINE proto_bool protocore_spi_txn_ext(uint16_t cmd, uint8_t cmd_bits, uint32_t addr, uint8_t addr_bits,
139 uint8_t dummy_bits, uint8_t lanes, const uint8_t *tx, uint8_t *rx,
140 size_t len)
141{
142 return protocore_spi_txn_ext_on((uint8_t)PROTOCORE_SPI_HOST, PROTOCORE_SPI_HZ, PROTOCORE_SPI_MSBFIRST,
143 (uint8_t)PROTOCORE_SPI_MODE, cmd, cmd_bits, addr, addr_bits, dummy_bits, lanes, tx,
144 rx, len);
145}
146
147/** @brief Drive @p pin as an output at the deselected level, which is how a part is left idle. */
149{
150 protocore_platform_gpio_mode(pin, PROTOCORE_GPIO_OUT);
151 protocore_platform_gpio_write(pin, PROTOCORE_GPIO_HIGH);
152}
153
154/** @brief Pull @p pin low, selecting the part for the transfers that follow. */
156{
157 protocore_platform_gpio_write(pin, PROTOCORE_GPIO_LOW);
158}
159
160/** @brief Let @p pin back high, deselecting the part. */
162{
163 protocore_platform_gpio_write(pin, PROTOCORE_GPIO_HIGH);
164}
165
166#else // no bus seam
167
168PROTOCORE_INLINE proto_bool protocore_spi_begin_on(uint8_t host, int mosi, int miso, int sclk, int quadwp, int quadhd)
169{
170 (void)host;
171 (void)mosi;
172 (void)miso;
173 (void)sclk;
174 (void)quadwp;
175 (void)quadhd;
176 return PROTO_TRUE;
177}
178
183
184PROTOCORE_INLINE proto_bool protocore_spi_txn_on(uint8_t host, uint32_t hz, uint8_t bit_order, uint8_t mode,
185 const uint8_t *tx, uint8_t *rx, size_t len)
186{
187 (void)host;
188 (void)hz;
189 (void)bit_order;
190 (void)mode;
191 (void)tx;
192 (void)rx;
193 (void)len;
194 return PROTO_FALSE;
195}
196
197PROTOCORE_INLINE proto_bool protocore_spi_txn_at(uint32_t hz, uint8_t bit_order, uint8_t mode, const uint8_t *tx,
198 uint8_t *rx, size_t len)
199{
200 return protocore_spi_txn_on((uint8_t)PROTOCORE_SPI_HOST, hz, bit_order, mode, tx, rx, len);
201}
202
203PROTOCORE_INLINE proto_bool protocore_spi_txn(const uint8_t *tx, uint8_t *rx, size_t len)
204{
205 return protocore_spi_txn_at(PROTOCORE_SPI_HZ, PROTOCORE_SPI_MSBFIRST, (uint8_t)PROTOCORE_SPI_MODE, tx, rx, len);
206}
207
208PROTOCORE_INLINE proto_bool protocore_spi_write(const uint8_t *tx, size_t len)
209{
210 return protocore_spi_txn(tx, NULL, len);
211}
212
214{
215 return protocore_spi_txn(NULL, rx, len);
216}
217
218PROTOCORE_INLINE proto_bool protocore_spi_txn_ext_on(uint8_t host, uint32_t hz, uint8_t bit_order, uint8_t mode,
219 uint16_t cmd, uint8_t cmd_bits, uint32_t addr, uint8_t addr_bits,
220 uint8_t dummy_bits, uint8_t lanes, const uint8_t *tx, uint8_t *rx,
221 size_t len)
222{
223 (void)host;
224 (void)hz;
225 (void)bit_order;
226 (void)mode;
227 (void)cmd;
228 (void)cmd_bits;
229 (void)addr;
230 (void)addr_bits;
231 (void)dummy_bits;
232 (void)lanes;
233 (void)tx;
234 (void)rx;
235 (void)len;
236 return PROTO_FALSE;
237}
238
239PROTOCORE_INLINE proto_bool protocore_spi_txn_ext(uint16_t cmd, uint8_t cmd_bits, uint32_t addr, uint8_t addr_bits,
240 uint8_t dummy_bits, uint8_t lanes, const uint8_t *tx, uint8_t *rx,
241 size_t len)
242{
243 (void)cmd;
244 (void)cmd_bits;
245 (void)addr;
246 (void)addr_bits;
247 (void)dummy_bits;
248 (void)lanes;
249 (void)tx;
250 (void)rx;
251 (void)len;
252 return PROTO_FALSE;
253}
254
256{
257 (void)pin;
258}
259
261{
262 (void)pin;
263}
264
266{
267 (void)pin;
268}
269
270#endif // PROTOCORE_HAS_BUS
271
273
274#endif // PROTOCORE_SPI_H
#define PROTOCORE_SPI_MISO_PIN
#define PROTOCORE_SPI_SCLK_PIN
#define PROTOCORE_SPI_MOSI_PIN
Shared SPI bus pins for the peripheral drivers, the same way the I2C pins above are shared....
#define PROTOCORE_INLINE
Linkage for a leaf primitive whose body is cheaper than the call that reaches it.
The platform contract: what the library asks of a target, in the library's own words.
PROTOCORE_INLINE proto_bool protocore_spi_txn_ext_on(uint8_t host, uint32_t hz, uint8_t bit_order, uint8_t mode, uint16_t cmd, uint8_t cmd_bits, uint32_t addr, uint8_t addr_bits, uint8_t dummy_bits, uint8_t lanes, const uint8_t *tx, uint8_t *rx, size_t len)
Definition spi.h:218
PROTOCORE_INLINE proto_bool protocore_spi_txn(const uint8_t *tx, uint8_t *rx, size_t len)
Definition spi.h:203
#define PROTOCORE_SPI_MODE
Clock polarity and phase, as SPI mode 0..3. Mode 0 is what these parts use.
Definition spi.h:49
PROTOCORE_INLINE proto_bool protocore_spi_txn_at(uint32_t hz, uint8_t bit_order, uint8_t mode, const uint8_t *tx, uint8_t *rx, size_t len)
Definition spi.h:197
#define PROTOCORE_SPI_QUADWP_PIN
Third and fourth data lines, for a quad-width bus; -1 leaves the bus single or dual.
Definition spi.h:59
PROTOCORE_INLINE proto_bool protocore_spi_read(uint8_t *rx, size_t len)
Definition spi.h:213
#define PROTOCORE_SPI_QUADHD_PIN
Definition spi.h:62
PROTOCORE_INLINE proto_bool protocore_spi_write(const uint8_t *tx, size_t len)
Definition spi.h:208
PROTOCORE_INLINE void protocore_spi_cs_select(uint8_t pin)
Definition spi.h:260
#define PROTOCORE_SPI_HZ
Bus clock for the shared peripheral bus; 1 MHz is safe on every part on it.
Definition spi.h:44
PROTOCORE_INLINE void protocore_spi_cs_idle(uint8_t pin)
Definition spi.h:255
PROTOCORE_INLINE proto_bool protocore_spi_txn_ext(uint16_t cmd, uint8_t cmd_bits, uint32_t addr, uint8_t addr_bits, uint8_t dummy_bits, uint8_t lanes, const uint8_t *tx, uint8_t *rx, size_t len)
Definition spi.h:239
PROTOCORE_INLINE proto_bool protocore_spi_txn_on(uint8_t host, uint32_t hz, uint8_t bit_order, uint8_t mode, const uint8_t *tx, uint8_t *rx, size_t len)
Definition spi.h:184
PROTOCORE_BEGIN_DECLS PROTOCORE_INLINE proto_bool protocore_spi_begin_on(uint8_t host, int mosi, int miso, int sclk, int quadwp, int quadhd)
Definition spi.h:168
#define PROTOCORE_SPI_HOST
Controller the plain verbs drive.
Definition spi.h:54
PROTOCORE_INLINE void protocore_spi_cs_release(uint8_t pin)
Definition spi.h:265
PROTOCORE_INLINE proto_bool protocore_spi_begin(void)
Definition spi.h:179
#define PROTO_FALSE
the false value
Definition types.h:68
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTO_TRUE
the true value, spelled so a caller never writes a bare 1
Definition types.h:67
#define PROTOCORE_END_DECLS
Definition types.h:97