ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
mpr121.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 mpr121.h
6 * @brief NXP MPR121 12-channel capacitive-touch controller codec (PROTOCORE_ENABLE_MPR121).
7 *
8 * The MPR121 reports a 16-bit touch-status word (registers 0x00/0x01): bits 0-11 are the twelve
9 * electrodes, bit 12 is the proximity electrode, and bit 15 is the over-current flag. It also
10 * exposes 10-bit filtered capacitance and 8-bit baseline per electrode. Bringing it up is a
11 * fixed sequence of register writes (soft reset, the NXP filter/AFE defaults, per-electrode
12 * touch/release thresholds, and the electrode-configuration register that starts it running).
13 *
14 * This codec is pure and host-tested: ::protocore_mpr121_touched / ::protocore_mpr121_word10 decode the reported
15 * words, and ::protocore_mpr121_build_init emits the whole bring-up sequence as `(register, value)` byte
16 * pairs (so the exact bytes are verifiable off-target). On an ESP32 the binding replays that
17 * sequence over I2C (Wire) and reads the status; only that touches hardware.
18 *
19 * A cheap solder-and-bench-test breakout for touch buttons / sliders: wire it up, touch a pad,
20 * watch the bit set.
21 *
22 * @author Douglas Quigg (dstroy0)
23 * @date 2026
24 */
25
26#ifndef PROTOCORE_MPR121_H
27#define PROTOCORE_MPR121_H
28
29#include "protocore_config.h" // the entry point: protocore_types.h for the widths
30
31#if PROTOCORE_ENABLE_MPR121
32
34
35// PROTOCORE_MPR121_BORROW - the bytes this module runs out of - is stated in protocore_config.h, which sums
36// it into its arena. A caller takes them once and passes the pointer to every call. How they
37// are carved is this module's and is never named here.
38
39#define MPR121_ELECTRODES 12
40
41#define MPR121_INIT_MAX 82
42
43/** @brief What touched takes: status_lo, status_hi. */
44typedef struct
45{
46 uint8_t status_lo;
47 uint8_t status_hi;
48} Mpr121TouchedArgs;
49
50/** @brief What is_touched takes: mask, e. */
51typedef struct
52{
53 uint16_t mask;
54 uint8_t e;
55} Mpr121IsTouchedArgs;
56
57/** @brief What proximity takes: status_hi. */
58typedef struct
59{
60 uint8_t status_hi;
61} Mpr121ProximityArgs;
62
63/** @brief What overcurrent takes: status_hi. */
64typedef struct
65{
66 uint8_t status_hi;
67} Mpr121OvercurrentArgs;
68
69/** @brief What word10 takes: lsb, msb. */
70typedef struct
71{
72 uint8_t lsb;
73 uint8_t msb;
74} Mpr121Word10Args;
75
76/** @brief What build_init takes: buf, cap, n_electrodes, touch_thr, ... */
77typedef struct
78{
79 uint8_t *buf;
80 size_t cap;
81 uint8_t n_electrodes;
82 uint8_t touch_thr;
83 uint8_t release_thr;
84} Mpr121BuildInitArgs;
85
86/** @brief What begin takes: addr. */
87typedef struct
88{
89 uint8_t addr;
90} Mpr121BeginArgs;
91
92/** @brief What read_filtered takes: e. */
93typedef struct
94{
95 uint8_t e;
96} Mpr121ReadFilteredArgs;
97
98/**
99 * @brief NXP MPR121 12-channel capacitive-touch controller codec (PROTOCORE_ENABLE_MPR121).
100 *
101 * A caller sets the members a call takes, invokes it through ::Mpr121 with the bytes it runs
102 * out of, and reads the outcome off the same handle.
103 *
104 * Mpr121.touched_args.status_lo = ...;
105 * Mpr121.touched_args.status_hi = ...;
106 * Mpr121.touched(work);
107 * // Mpr121.value is what the call reports
108 *
109 * @var Mpr121Ns::touched_args what touched takes: status_lo, status_hi
110 * @var Mpr121Ns::is_touched_args what is_touched takes: mask, e
111 * @var Mpr121Ns::proximity_args what proximity takes: status_hi
112 * @var Mpr121Ns::overcurrent_args what overcurrent takes: status_hi
113 * @var Mpr121Ns::word10_args what word10 takes: lsb, msb
114 * @var Mpr121Ns::build_init_args what build_init takes: buf, cap, n_electrodes, touch_thr,
115 * @var Mpr121Ns::begin_args what begin takes: addr
116 * @var Mpr121Ns::read_filtered_args what read_filtered takes: e
117 * @var Mpr121Ns::ok a call's true/false outcome
118 * @var Mpr121Ns::value the value a call reports
119 * @var Mpr121Ns::n the number of bytes written (pairs * 2), or 0 if cap is too small / ...
120 * @var Mpr121Ns::touched decode the 12-electrode touch bitmask from the two status registers ...
121 * @var Mpr121Ns::is_touched true if electrode e (0..11) is touched in a mask from ...
122 * @var Mpr121Ns::proximity true if the proximity electrode (status bit 12) is active
123 * @var Mpr121Ns::overcurrent true if the over-current flag (status bit 15) is set (wiring fault ...
124 * @var Mpr121Ns::word10 combine a little-endian LSB/MSB register pair into a 10-bit value ...
125 * @var Mpr121Ns::build_init build the MPR121 bring-up sequence as consecutive `(register, ...
126 * @var Mpr121Ns::begin reset + configure the MPR121 at addr over I2C. true if it ...
127 * @var Mpr121Ns::read_touched read the current 12-electrode touch bitmask (0 if the device is ...
128 * @var Mpr121Ns::read_filtered read electrode e's 10-bit filtered capacitance value
129 *
130 * @c work is PROTOCORE_MPR121_BORROW bytes the CALLER took, at an address it knows. It is not held past the call, so
131 * nothing here aliases it. How those bytes are carved is this module's and is never named here.
132 */
133typedef struct
134{
135 Mpr121TouchedArgs touched_args;
136 Mpr121IsTouchedArgs is_touched_args;
137 Mpr121ProximityArgs proximity_args;
138 Mpr121OvercurrentArgs overcurrent_args;
139 Mpr121Word10Args word10_args;
140 Mpr121BuildInitArgs build_init_args;
141 Mpr121BeginArgs begin_args;
142 Mpr121ReadFilteredArgs read_filtered_args;
143 proto_bool ok;
144 uint16_t value;
145 size_t n;
146} Mpr121Vars;
147
148/** @brief The operands and the outcome. */
149extern Mpr121Vars Mpr121V;
150
151/** @brief The entries. */
152typedef struct
153{
154 void (*const touched)(uint8_t *work);
155 void (*const is_touched)(uint8_t *work);
156 void (*const proximity)(uint8_t *work);
157 void (*const overcurrent)(uint8_t *work);
158 void (*const word10)(uint8_t *work);
159 void (*const build_init)(uint8_t *work);
160 void (*const begin)(uint8_t *work);
161 void (*const read_touched)(uint8_t *work);
162 void (*const read_filtered)(uint8_t *work);
163} Mpr121Ns;
164
165// What the table binds, defined once in the .c and taking one parameter each: everything
166// else an entry needs is an operand in Mpr121V or a region of the borrow at a fixed offset.
167void protocore_mpr121_touched(uint8_t *work);
168void protocore_mpr121_is_touched(uint8_t *work);
169void protocore_mpr121_proximity(uint8_t *work);
170void protocore_mpr121_overcurrent(uint8_t *work);
171void protocore_mpr121_word10(uint8_t *work);
172void protocore_mpr121_build_init(uint8_t *work);
173void protocore_mpr121_begin(uint8_t *work);
174void protocore_mpr121_read_touched(uint8_t *work);
175void protocore_mpr121_read_filtered(uint8_t *work);
176
177// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
178// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
179// `Mpr121.touched(work)` resolves to a named function and becomes a DIRECT call. An extern table
180// leaves the call indirect and the symbol live at every level, -O2 -flto included.
181static const Mpr121Ns Mpr121 __attribute__((unused)) = {
182 .touched = protocore_mpr121_touched,
183 .is_touched = protocore_mpr121_is_touched,
184 .proximity = protocore_mpr121_proximity,
185 .overcurrent = protocore_mpr121_overcurrent,
186 .word10 = protocore_mpr121_word10,
187 .build_init = protocore_mpr121_build_init,
188 .begin = protocore_mpr121_begin,
189 .read_touched = protocore_mpr121_read_touched,
190 .read_filtered = protocore_mpr121_read_filtered,
191};
192
193/**
194 * @brief The PROTOCORE_MPR121_BORROW bytes this module's state lives in.
195 *
196 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
197 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
198 * walks, so the state lasts the life of the program.
199 *
200 * @return the span.
201 */
202uint8_t *protocore_mpr121_span(void);
203
205
206#endif // PROTOCORE_ENABLE_MPR121
207
208#endif // PROTOCORE_MPR121_H
#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 PROTOCORE_END_DECLS
Definition types.h:97