ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ina219.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 ina219.h
6 * @brief TI INA219 high-side current / power monitor codec (PROTOCORE_ENABLE_INA219).
7 *
8 * The INA219 measures the voltage across a shunt resistor (LSB 10 uV) and the bus voltage (LSB
9 * 4 mV, in the upper 13 bits of its register), and - once a calibration value derived from the
10 * shunt resistance and a chosen current LSB is programmed - reports current and power directly.
11 * From those you get how much current and power a circuit draws.
12 *
13 * This codec is pure and host-tested: ::protocore_ina219_bus_mv / ::protocore_ina219_shunt_uv decode the voltage
14 * registers, ::protocore_ina219_calibration computes the calibration register, and ::protocore_ina219_current_ua /
15 * ::protocore_ina219_power_uw scale the raw current / power registers by the current LSB. On an ESP32 the
16 * binding programs the calibration + config and reads the registers over I2C (Wire); only that
17 * touches hardware.
18 *
19 * A cheap solder-and-bench-test breakout: put it in series with a load and watch the current.
20 *
21 * @author Douglas Quigg (dstroy0)
22 * @date 2026
23 */
24
25#ifndef PROTOCORE_INA219_H
26#define PROTOCORE_INA219_H
27
28#include "protocore_config.h" // the entry point: protocore_types.h for the widths
29
30#if PROTOCORE_ENABLE_INA219
31
33
34// PROTOCORE_I2C_DEVICE_BORROW - the bytes this module runs out of - is stated in protocore_config.h, which sums
35// it into its arena. A caller takes them once and passes the pointer to every call. How they
36// are carved is this module's and is never named here.
37
38#define INA219_REG_CONFIG 0x00 ///< configuration
39
40#define INA219_REG_SHUNT 0x01 ///< shunt voltage
41
42#define INA219_REG_BUS 0x02 ///< bus voltage
43
44#define INA219_REG_POWER 0x03 ///< power
45
46#define INA219_REG_CURRENT 0x04 ///< current
47
48#define INA219_REG_CALIBRATION 0x05 ///< calibration
49
50/** @brief What bus_mv takes: raw. */
51typedef struct
52{
53 uint16_t raw;
54} Ina219BusMvArgs;
55
56/** @brief What shunt_uv takes: raw. */
57typedef struct
58{
59 int16_t raw;
60} Ina219ShuntUvArgs;
61
62/** @brief What calibration takes: current_lsb_ua, shunt_mohm. */
63typedef struct
64{
65 uint32_t current_lsb_ua;
66 uint32_t shunt_mohm;
67} Ina219CalibrationArgs;
68
69/** @brief What current_ua takes: raw, current_lsb_ua. */
70typedef struct
71{
72 int16_t raw;
73 uint32_t current_lsb_ua;
74} Ina219CurrentUaArgs;
75
76/** @brief What power_uw takes: raw, current_lsb_ua. */
77typedef struct
78{
79 int16_t raw;
80 uint32_t current_lsb_ua;
81} Ina219PowerUwArgs;
82
83/** @brief What begin takes: addr, current_lsb_ua, shunt_mohm. */
84typedef struct
85{
86 uint8_t addr;
87 uint32_t current_lsb_ua;
88 uint32_t shunt_mohm;
89} Ina219BeginArgs;
90
91/** @brief What read_bus_mv takes: millivolts. */
92typedef struct
93{
94 int32_t *millivolts;
95} Ina219ReadBusMvArgs;
96
97/** @brief What read_shunt_uv takes: microvolts. */
98typedef struct
99{
100 int32_t *microvolts;
101} Ina219ReadShuntUvArgs;
102
103/** @brief What read_current_ua takes: microamps. */
104typedef struct
105{
106 int32_t *microamps;
107} Ina219ReadCurrentUaArgs;
108
109/** @brief What read_power_uw takes: microwatts. */
110typedef struct
111{
112 int32_t *microwatts;
113} Ina219ReadPowerUwArgs;
114
115/**
116 * @brief TI INA219 high-side current / power monitor codec (PROTOCORE_ENABLE_INA219).
117 *
118 * A caller sets the members a call takes, invokes it through ::Ina219 with the bytes it runs
119 * out of, and reads the outcome off the same handle.
120 *
121 * Ina219.bus_mv_args.raw = ...;
122 * Ina219.bus_mv(work);
123 * // Ina219.value is what the call reports
124 *
125 * @var Ina219Ns::bus_mv_args what bus_mv takes: raw
126 * @var Ina219Ns::shunt_uv_args what shunt_uv takes: raw
127 * @var Ina219Ns::calibration_args what calibration takes: current_lsb_ua, shunt_mohm
128 * @var Ina219Ns::current_ua_args what current_ua takes: raw, current_lsb_ua
129 * @var Ina219Ns::power_uw_args what power_uw takes: raw, current_lsb_ua
130 * @var Ina219Ns::begin_args what begin takes: addr, current_lsb_ua, shunt_mohm
131 * @var Ina219Ns::read_bus_mv_args what read_bus_mv takes: millivolts
132 * @var Ina219Ns::read_shunt_uv_args what read_shunt_uv takes: microvolts
133 * @var Ina219Ns::read_current_ua_args what read_current_ua takes: microamps
134 * @var Ina219Ns::read_power_uw_args what read_power_uw takes: microwatts
135 * @var Ina219Ns::ok a call's true/false outcome
136 * @var Ina219Ns::value the value a call reports
137 * @var Ina219Ns::cal what a call reports
138 * @var Ina219Ns::bus_mv decode the bus-voltage register to millivolts (value is bits ...
139 * @var Ina219Ns::shunt_uv decode the shunt-voltage register to microvolts (signed, LSB 10 uV)
140 * @var Ina219Ns::calibration compute the calibration register from the current LSB (microamps ...
141 * @var Ina219Ns::current_ua scale the raw current register to microamps (raw * current_lsb_ua)
142 * @var Ina219Ns::power_uw scale the raw power register to microwatts (power LSB is 20 * ...
143 * @var Ina219Ns::begin program the INA219 at addr: write the calibration for ...
144 * @var Ina219Ns::read_bus_mv read the bus voltage into millivolts. false on I2C error
145 * @var Ina219Ns::read_shunt_uv read the shunt voltage into microvolts. false on I2C error
146 * @var Ina219Ns::read_current_ua read the current into microamps (needs the calibration set by ...
147 * @var Ina219Ns::read_power_uw read the power into microwatts (needs the calibration set by ...
148 *
149 * @c work is PROTOCORE_I2C_DEVICE_BORROW bytes the CALLER took, at an address it knows. It is not held past the call,
150 * so nothing here aliases it. How those bytes are carved is this module's and is never named here.
151 */
152typedef struct
153{
154 Ina219BusMvArgs bus_mv_args;
155 Ina219ShuntUvArgs shunt_uv_args;
156 Ina219CalibrationArgs calibration_args;
157 Ina219CurrentUaArgs current_ua_args;
158 Ina219PowerUwArgs power_uw_args;
159 Ina219BeginArgs begin_args;
160 Ina219ReadBusMvArgs read_bus_mv_args;
161 Ina219ReadShuntUvArgs read_shunt_uv_args;
162 Ina219ReadCurrentUaArgs read_current_ua_args;
163 Ina219ReadPowerUwArgs read_power_uw_args;
164 proto_bool ok;
165 int32_t value;
166 uint16_t cal;
167} Ina219Vars;
168
169/** @brief The operands and the outcome. */
170extern Ina219Vars Ina219V;
171
172/** @brief The entries. */
173typedef struct
174{
175 void (*const bus_mv)(uint8_t *work);
176 void (*const shunt_uv)(uint8_t *work);
177 void (*const calibration)(uint8_t *work);
178 void (*const current_ua)(uint8_t *work);
179 void (*const power_uw)(uint8_t *work);
180 void (*const begin)(uint8_t *work);
181 void (*const read_bus_mv)(uint8_t *work);
182 void (*const read_shunt_uv)(uint8_t *work);
183 void (*const read_current_ua)(uint8_t *work);
184 void (*const read_power_uw)(uint8_t *work);
185} Ina219Ns;
186
187// What the table binds, defined once in the .c and taking one parameter each: everything
188// else an entry needs is an operand in Ina219V or a region of the borrow at a fixed offset.
189void protocore_ina219_bus_mv(uint8_t *work);
190void protocore_ina219_shunt_uv(uint8_t *work);
191void protocore_ina219_calibration(uint8_t *work);
192void protocore_ina219_current_ua(uint8_t *work);
193void protocore_ina219_power_uw(uint8_t *work);
194void protocore_ina219_begin(uint8_t *work);
195void protocore_ina219_read_bus_mv(uint8_t *work);
196void protocore_ina219_read_shunt_uv(uint8_t *work);
197void protocore_ina219_read_current_ua(uint8_t *work);
198void protocore_ina219_read_power_uw(uint8_t *work);
199
200// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
201// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
202// `Ina219.bus_mv(work)` resolves to a named function and becomes a DIRECT call. An extern table
203// leaves the call indirect and the symbol live at every level, -O2 -flto included.
204static const Ina219Ns Ina219 __attribute__((unused)) = {
205 .bus_mv = protocore_ina219_bus_mv,
206 .shunt_uv = protocore_ina219_shunt_uv,
207 .calibration = protocore_ina219_calibration,
208 .current_ua = protocore_ina219_current_ua,
209 .power_uw = protocore_ina219_power_uw,
210 .begin = protocore_ina219_begin,
211 .read_bus_mv = protocore_ina219_read_bus_mv,
212 .read_shunt_uv = protocore_ina219_read_shunt_uv,
213 .read_current_ua = protocore_ina219_read_current_ua,
214 .read_power_uw = protocore_ina219_read_power_uw,
215};
216
217/**
218 * @brief The PROTOCORE_I2C_DEVICE_BORROW bytes this module's state lives in.
219 *
220 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
221 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
222 * walks, so the state lasts the life of the program.
223 *
224 * @return the span.
225 */
226uint8_t *protocore_ina219_span(void);
227
229
230#endif // PROTOCORE_ENABLE_INA219
231
232#endif // PROTOCORE_INA219_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