ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
i2c.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 i2c.h
6 * @brief The one owner of the shared I2C bus for the peripheral drivers.
7 *
8 * The sensor / peripheral drivers (RTC, SHT3x, MPR121, ADS1115, INA219, PCA9685, VL53L0X,
9 * LDC1614, FDC2214) share one bus and bring it up through these verbs. The pins come from
10 * PROTOCORE_I2C_SDA_PIN / PROTOCORE_I2C_SCL_PIN (default -1 = the platform default GPIO 21 / 22). Re-begin
11 * is idempotent, so per-driver calls are harmless.
12 *
13 * This covers the master half of the protocol. The three transfer shapes a driver reaches for are
14 * a write, a read, and a register read, which is a write and a read joined by a repeated start.
15 * The rest of what the bus can express is here too: a bus scan, an address probe, the general
16 * call, 10-bit addressing, a per-transfer clock, and recovery of a bus a device is holding low.
17 *
18 * Every verb has a plain form on the default bus (PROTOCORE_I2C_BUS) and an `_on` form naming a
19 * controller, so a board with two I2C controllers drives both through one owner.
20 *
21 * An address is 7-bit unless it is wrapped in ::PROTOCORE_I2C_ADDR10, which selects the 10-bit form.
22 *
23 * The bodies compile wherever the platform states a bus.
24 *
25 * @author Douglas Quigg (dstroy0)
26 * @date 2026
27 */
28
29#ifndef PROTOCORE_I2C_H
30#define PROTOCORE_I2C_H
31
33
34#include "protocore_config.h"
35
36/** @brief Bus clock for the shared peripheral bus; 100 kHz standard mode. */
37#ifndef PROTOCORE_I2C_HZ
38#define PROTOCORE_I2C_HZ 100000u
39#endif
40
41/** @brief Per-transfer timeout in milliseconds. */
42#ifndef PROTOCORE_I2C_TIMEOUT_MS
43#define PROTOCORE_I2C_TIMEOUT_MS 50u
44#endif
45
46/** @brief Controller the plain verbs drive. */
47#ifndef PROTOCORE_I2C_BUS
48#define PROTOCORE_I2C_BUS 0u
49#endif
50
51/** @brief Standard-mode, fast-mode and fast-mode-plus bus clocks. */
52#define PROTOCORE_I2C_HZ_STANDARD 100000u
53#define PROTOCORE_I2C_HZ_FAST 400000u
54#define PROTOCORE_I2C_HZ_FAST_PLUS 1000000u
55
56/** @brief Wrap a 10-bit address so a transfer verb puts the two-byte form on the wire. */
57#define PROTOCORE_I2C_ADDR10(a) ((uint16_t)(PROTOCORE_I2C_ADDR_10BIT | ((a) & PROTOCORE_I2C_ADDR_MASK)))
58
59/** @brief Lowest and highest 7-bit addresses a scan reports; 0x00 - 0x07 and 0x78 - 0x7F are reserved. */
60#define PROTOCORE_I2C_SCAN_FIRST 0x08u
61#define PROTOCORE_I2C_SCAN_LAST 0x77u
62
64
65#if PROTOCORE_HAS_BUS
66
67/** @brief Bring up @p bus on @p sda / @p scl at @p hz (-1 on a pin = the platform default). */
68PROTOCORE_INLINE proto_bool protocore_i2c_begin_on(uint8_t bus, int sda, int scl, uint32_t hz)
69{
70 return protocore_platform_i2c_begin(bus, sda, scl, hz) != 0;
71}
72
73/** @brief Bring up the shared I2C bus on PROTOCORE_I2C_SDA_PIN / PROTOCORE_I2C_SCL_PIN (-1 = default). */
75{
78}
79
80/** @brief Write @p len bytes to @p addr on @p bus, closing with a stop. */
81PROTOCORE_INLINE proto_bool protocore_i2c_write_on(uint8_t bus, uint16_t addr, const uint8_t *buf, size_t len)
82{
83 return protocore_platform_i2c_write(bus, addr, buf, (uint32_t)len, PROTOCORE_I2C_TIMEOUT_MS) != 0;
84}
85
86/** @brief Write @p len bytes to @p addr, closing with a stop. */
87PROTOCORE_INLINE proto_bool protocore_i2c_write(uint16_t addr, const uint8_t *buf, size_t len)
88{
89 return protocore_i2c_write_on((uint8_t)PROTOCORE_I2C_BUS, addr, buf, len);
90}
91
92/** @brief Read @p len bytes from @p addr on @p bus. */
93PROTOCORE_INLINE proto_bool protocore_i2c_read_on(uint8_t bus, uint16_t addr, uint8_t *buf, size_t len)
94{
95 return protocore_platform_i2c_read(bus, addr, buf, (uint32_t)len, PROTOCORE_I2C_TIMEOUT_MS) != 0;
96}
97
98/** @brief Read @p len bytes from @p addr. */
99PROTOCORE_INLINE proto_bool protocore_i2c_read(uint16_t addr, uint8_t *buf, size_t len)
100{
101 return protocore_i2c_read_on((uint8_t)PROTOCORE_I2C_BUS, addr, buf, len);
102}
103
104/** @brief Write @p wlen bytes to @p addr on @p bus, then read @p rlen back through a repeated start. */
105PROTOCORE_INLINE proto_bool protocore_i2c_write_read_on(uint8_t bus, uint16_t addr, const uint8_t *w, size_t wlen,
106 uint8_t *r, size_t rlen)
107{
108 return protocore_platform_i2c_write_read(bus, addr, w, (uint32_t)wlen, r, (uint32_t)rlen,
110}
111
112/** @brief Write @p wlen bytes, then read @p rlen back in the same transaction (repeated start). */
113PROTOCORE_INLINE proto_bool protocore_i2c_write_read(uint16_t addr, const uint8_t *w, size_t wlen, uint8_t *r,
114 size_t rlen)
115{
116 return protocore_i2c_write_read_on((uint8_t)PROTOCORE_I2C_BUS, addr, w, wlen, r, rlen);
117}
118
119/** @brief Run @p bus at @p hz from here on. */
121{
122 return protocore_platform_i2c_set_clock(bus, hz) != 0;
123}
124
125/** @brief Run the shared bus at @p hz from here on; a part slower than the bus needs this. */
127{
129}
130
131/** @brief Address @p addr on @p bus and stop. @return true if a device drove ACK. */
132PROTOCORE_INLINE proto_bool protocore_i2c_probe_on(uint8_t bus, uint16_t addr)
133{
134 return protocore_platform_i2c_probe(bus, addr, PROTOCORE_I2C_TIMEOUT_MS) != 0;
135}
136
137/** @brief Address @p addr and stop. @return true if a device drove ACK. */
139{
140 return protocore_i2c_probe_on((uint8_t)PROTOCORE_I2C_BUS, addr);
141}
142
143/**
144 * @brief Probe every non-reserved 7-bit address on @p bus, writing those that answered into
145 * @p out (caller-owned, @p cap entries).
146 * @return how many answered, which is how many entries of @p out were written.
147 */
148PROTOCORE_INLINE size_t protocore_i2c_scan_on(uint8_t bus, uint8_t *out, size_t cap)
149{
150 size_t n = 0;
151 for (uint16_t a = PROTOCORE_I2C_SCAN_FIRST; a <= PROTOCORE_I2C_SCAN_LAST && n < cap; a++)
152 {
153 if (protocore_i2c_probe_on(bus, a))
154 {
155 out[n] = (uint8_t)a;
156 n++;
157 }
158 }
159 return n;
160}
161
162/** @brief Probe every non-reserved 7-bit address on the shared bus into @p out. */
163PROTOCORE_INLINE size_t protocore_i2c_scan(uint8_t *out, size_t cap)
164{
165 return protocore_i2c_scan_on((uint8_t)PROTOCORE_I2C_BUS, out, cap);
166}
167
168/** @brief Write @p len bytes to address 0x00 on @p bus, which every device answers. */
169PROTOCORE_INLINE proto_bool protocore_i2c_general_call_on(uint8_t bus, const uint8_t *buf, size_t len)
170{
171 return protocore_i2c_write_on(bus, PROTOCORE_I2C_GENERAL_CALL, buf, len);
172}
173
174/** @brief Write @p len bytes to the general call address, which every device on the bus answers. */
175PROTOCORE_INLINE proto_bool protocore_i2c_general_call(const uint8_t *buf, size_t len)
176{
177 return protocore_i2c_general_call_on((uint8_t)PROTOCORE_I2C_BUS, buf, len);
178}
179
180/** @brief Clock @p bus until the device holding SDA low releases it, then drive a stop. */
181PROTOCORE_INLINE proto_bool protocore_i2c_recover_on(uint8_t bus, int sda, int scl)
182{
183 return protocore_platform_i2c_recover(bus, sda, scl) != 0;
184}
185
186/** @brief Free the shared bus from a device holding SDA low. @return true if SDA came back high. */
188{
190}
191
192#else // no bus seam on this build
193
194PROTOCORE_INLINE proto_bool protocore_i2c_begin_on(uint8_t bus, int sda, int scl, uint32_t hz)
195{
196 (void)bus;
197 (void)sda;
198 (void)scl;
199 (void)hz;
200 return PROTO_TRUE;
201}
202
207
208PROTOCORE_INLINE proto_bool protocore_i2c_write_on(uint8_t bus, uint16_t addr, const uint8_t *buf, size_t len)
209{
210 (void)bus;
211 (void)addr;
212 (void)buf;
213 (void)len;
214 return PROTO_FALSE;
215}
216
217PROTOCORE_INLINE proto_bool protocore_i2c_write(uint16_t addr, const uint8_t *buf, size_t len)
218{
219 return protocore_i2c_write_on((uint8_t)PROTOCORE_I2C_BUS, addr, buf, len);
220}
221
222PROTOCORE_INLINE proto_bool protocore_i2c_read_on(uint8_t bus, uint16_t addr, uint8_t *buf, size_t len)
223{
224 (void)bus;
225 (void)addr;
226 (void)buf;
227 (void)len;
228 return PROTO_FALSE;
229}
230
231PROTOCORE_INLINE proto_bool protocore_i2c_read(uint16_t addr, uint8_t *buf, size_t len)
232{
233 return protocore_i2c_read_on((uint8_t)PROTOCORE_I2C_BUS, addr, buf, len);
234}
235
236PROTOCORE_INLINE proto_bool protocore_i2c_write_read_on(uint8_t bus, uint16_t addr, const uint8_t *w, size_t wlen,
237 uint8_t *r, size_t rlen)
238{
239 (void)bus;
240 (void)addr;
241 (void)w;
242 (void)wlen;
243 (void)r;
244 (void)rlen;
245 return PROTO_FALSE;
246}
247
248PROTOCORE_INLINE proto_bool protocore_i2c_write_read(uint16_t addr, const uint8_t *w, size_t wlen, uint8_t *r,
249 size_t rlen)
250{
251 return protocore_i2c_write_read_on((uint8_t)PROTOCORE_I2C_BUS, addr, w, wlen, r, rlen);
252}
253
255{
256 (void)bus;
257 (void)hz;
258 return PROTO_TRUE;
259}
260
262{
263 (void)hz;
264 return PROTO_TRUE;
265}
266
268{
269 (void)bus;
270 (void)addr;
271 return PROTO_FALSE;
272}
273
278
279PROTOCORE_INLINE size_t protocore_i2c_scan_on(uint8_t bus, uint8_t *out, size_t cap)
280{
281 size_t n = 0;
282 for (uint16_t a = PROTOCORE_I2C_SCAN_FIRST; a <= PROTOCORE_I2C_SCAN_LAST && n < cap; a++)
283 {
284 if (protocore_i2c_probe_on(bus, a))
285 {
286 out[n] = (uint8_t)a;
287 n++;
288 }
289 }
290 return n;
291}
292
293PROTOCORE_INLINE size_t protocore_i2c_scan(uint8_t *out, size_t cap)
294{
295 return protocore_i2c_scan_on((uint8_t)PROTOCORE_I2C_BUS, out, cap);
296}
297
298PROTOCORE_INLINE proto_bool protocore_i2c_general_call_on(uint8_t bus, const uint8_t *buf, size_t len)
299{
300 return protocore_i2c_write_on(bus, PROTOCORE_I2C_GENERAL_CALL, buf, len);
301}
302
304{
305 return protocore_i2c_general_call_on((uint8_t)PROTOCORE_I2C_BUS, buf, len);
306}
307
309{
310 (void)bus;
311 (void)sda;
312 (void)scl;
313 return PROTO_FALSE;
314}
315
320
321#endif // PROTOCORE_HAS_BUS
322
324
325#endif // PROTOCORE_I2C_H
#define PROTOCORE_I2C_SDA_PIN
Shared I2C bus pins for the sensor / peripheral drivers (RTC, SHT3x, MPR121, ADS1115,...
#define PROTOCORE_I2C_SCL_PIN
#define PROTOCORE_INLINE
Linkage for a leaf primitive whose body is cheaper than the call that reaches it.
PROTOCORE_INLINE proto_bool protocore_i2c_probe_on(uint8_t bus, uint16_t addr)
Definition i2c.h:267
PROTOCORE_INLINE proto_bool protocore_i2c_write_on(uint8_t bus, uint16_t addr, const uint8_t *buf, size_t len)
Definition i2c.h:208
PROTOCORE_INLINE proto_bool protocore_i2c_write_read_on(uint8_t bus, uint16_t addr, const uint8_t *w, size_t wlen, uint8_t *r, size_t rlen)
Definition i2c.h:236
PROTOCORE_INLINE proto_bool protocore_i2c_set_clock_on(uint8_t bus, uint32_t hz)
Definition i2c.h:254
PROTOCORE_INLINE proto_bool protocore_i2c_write(uint16_t addr, const uint8_t *buf, size_t len)
Definition i2c.h:217
#define PROTOCORE_I2C_TIMEOUT_MS
Per-transfer timeout in milliseconds.
Definition i2c.h:43
PROTOCORE_INLINE proto_bool protocore_i2c_write_read(uint16_t addr, const uint8_t *w, size_t wlen, uint8_t *r, size_t rlen)
Definition i2c.h:248
PROTOCORE_INLINE proto_bool protocore_i2c_general_call(const uint8_t *buf, size_t len)
Definition i2c.h:303
PROTOCORE_BEGIN_DECLS PROTOCORE_INLINE proto_bool protocore_i2c_begin_on(uint8_t bus, int sda, int scl, uint32_t hz)
Definition i2c.h:194
PROTOCORE_INLINE proto_bool protocore_i2c_begin(void)
Definition i2c.h:203
#define PROTOCORE_I2C_HZ
Bus clock for the shared peripheral bus; 100 kHz standard mode.
Definition i2c.h:38
#define PROTOCORE_I2C_SCAN_FIRST
Lowest and highest 7-bit addresses a scan reports; 0x00 - 0x07 and 0x78 - 0x7F are reserved.
Definition i2c.h:60
PROTOCORE_INLINE proto_bool protocore_i2c_read(uint16_t addr, uint8_t *buf, size_t len)
Definition i2c.h:231
PROTOCORE_INLINE proto_bool protocore_i2c_read_on(uint8_t bus, uint16_t addr, uint8_t *buf, size_t len)
Definition i2c.h:222
#define PROTOCORE_I2C_SCAN_LAST
Definition i2c.h:61
PROTOCORE_INLINE proto_bool protocore_i2c_recover(void)
Definition i2c.h:316
PROTOCORE_INLINE proto_bool protocore_i2c_probe(uint16_t addr)
Definition i2c.h:274
PROTOCORE_INLINE proto_bool protocore_i2c_set_clock(uint32_t hz)
Definition i2c.h:261
PROTOCORE_INLINE proto_bool protocore_i2c_general_call_on(uint8_t bus, const uint8_t *buf, size_t len)
Definition i2c.h:298
#define PROTOCORE_I2C_BUS
Controller the plain verbs drive.
Definition i2c.h:48
PROTOCORE_INLINE size_t protocore_i2c_scan(uint8_t *out, size_t cap)
Definition i2c.h:293
PROTOCORE_INLINE size_t protocore_i2c_scan_on(uint8_t bus, uint8_t *out, size_t cap)
Definition i2c.h:279
PROTOCORE_INLINE proto_bool protocore_i2c_recover_on(uint8_t bus, int sda, int scl)
Definition i2c.h:308
The platform contract: what the library asks of a target, in the library's own words.
#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