@@ -20,23 +20,12 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
2020 // / SDA, or SCL, address can be 0x40..0x4F
2121 static constexpr uint8_t DEFAULT_ADDRESS = 0x40 ; // /< Default I2C address if A0/A1=GND
2222
23- // Register map (datasheet)
24- enum class Reg : uint8_t {
25- CONFIG = 0x00 ,
26- SHUNT_VOLTAGE = 0x01 , // 16-bit signed, 2.5uV/LSB
27- BUS_VOLTAGE = 0x02 , // 16-bit unsigned, 1.25mV/LSB (bits 15..3)
28- POWER = 0x03 , // 16-bit unsigned, 25*CURRENT_LSB per LSB
29- CURRENT = 0x04 , // 16-bit signed, CURRENT_LSB per LSB
30- CALIBRATION = 0x05 , // 16-bit unsigned
31- MASK_ENABLE = 0x06 ,
32- ALERT_LIMIT = 0x07 ,
33- MANUFACTURER_ID = 0xFE , // 0x5449
34- DIE_ID = 0xFF , // 0x2260
35- };
23+ static constexpr uint16_t MANUFACTURER_ID_TI = 0x5449 ; // /< Texas Instruments Manufacturer ID
24+ static constexpr uint16_t DIE_ID_INA226 = 0x2260 ; // /< INA226 Die ID
3625
3726 // / Averaging (AVG) field values (bits 14..12 of CONFIG)
3827 enum class Avg : uint16_t {
39- AVG_1 = 0 ,
28+ AVG_1 = 0 , // NOTE: default on chip reset
4029 AVG_4 = 1 ,
4130 AVG_16 = 2 ,
4231 AVG_64 = 3 ,
@@ -52,7 +41,7 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
5241 US_204 = 1 ,
5342 US_332 = 2 ,
5443 US_588 = 3 ,
55- MS_1_1 = 4 ,
44+ MS_1_1 = 4 , // 1.1 ms, NOTE: this is the default on chip reset
5645 MS_2_116 = 5 ,
5746 MS_4_156 = 6 ,
5847 MS_8_244 = 7 ,
@@ -67,7 +56,7 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
6756 ADC_OFF = 4 ,
6857 SHUNT_CONT = 5 ,
6958 BUS_CONT = 6 ,
70- SHUNT_BUS_CONT = 7 ,
59+ SHUNT_BUS_CONT = 7 , // NOTE: default on chip reset
7160 };
7261
7362 // / Configuration structure for INA226
@@ -124,13 +113,15 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
124113 // / Read manufacturer ID (0x5449 for Texas Instruments)
125114 // / @param ec Error code to capture any read errors
126115 // / @return Manufacturer ID (0x5449) or 0 on error
127- uint16_t manufacturer_id (std::error_code &ec) {
116+ uint16_t manufacturer_id (std::error_code &ec) const {
128117 return read_u16_from_register ((uint8_t )Reg::MANUFACTURER_ID, ec);
129118 }
130119 // / Read die ID (0x2260 for INA226)
131120 // / @param ec Error code to capture any read errors
132121 // / @return Die ID (0x2260) or 0 on error
133- uint16_t die_id (std::error_code &ec) { return read_u16_from_register ((uint8_t )Reg::DIE_ID, ec); }
122+ uint16_t die_id (std::error_code &ec) const {
123+ return read_u16_from_register ((uint8_t )Reg::DIE_ID, ec);
124+ }
134125
135126 // Engineering-unit helpers
136127
@@ -139,7 +130,7 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
139130 // / @return Shunt voltage in volts, or 0.0f on error
140131 // / @note The shunt voltage is signed, so it can be negative if the current
141132 // / flows in the reverse direction. The LSB is 2.5 uV
142- float shunt_voltage_volts (std::error_code &ec) {
133+ float shunt_voltage_volts (std::error_code &ec) const {
143134 int16_t raw = read_shunt_raw (ec);
144135 if (ec)
145136 return 0 .0f ;
@@ -151,12 +142,12 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
151142 // / @return Bus voltage in volts, or 0.0f on error
152143 // / @note The bus voltage is unsigned, so it cannot be negative. The LSB is
153144 // / 1.25 mV
154- float bus_voltage_volts (std::error_code &ec) {
145+ float bus_voltage_volts (std::error_code &ec) const {
155146 uint16_t raw = read_bus_raw (ec);
156147 if (ec)
157148 return 0 .0f ;
158- // Bits [2:0] are reserved/zero; each LSB (of bit 3) is 1.25mV
159- return (( raw >> 3 ) * 1 .25e-3f );
149+ // each LSB is 1.25mV
150+ return (raw * 1 .25e-3f );
160151 }
161152
162153 // / Read current in amps
@@ -165,7 +156,7 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
165156 // / @note The current is signed, so it can be negative if the current flows in
166157 // / the reverse direction. The LSB is set via the calibrate() method and
167158 // / is in Amps/LSB.
168- float current_amps (std::error_code &ec) {
159+ float current_amps (std::error_code &ec) const {
169160 std::lock_guard<std::recursive_mutex> lock (base_mutex_);
170161 int16_t raw = read_current_raw (ec);
171162 if (ec)
@@ -186,6 +177,18 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
186177 return raw * (25 .0f * current_lsb_);
187178 }
188179
180+ // / Reset the INA226 to default settings
181+ // / @param ec Error code to capture any write errors
182+ // / @return true if reset succeeded, false if it failed
183+ bool reset (std::error_code &ec) {
184+ // Set bit 15 of CONFIG to reset
185+ static constexpr uint16_t RESET_BIT = 1 << 15 ;
186+ uint16_t word = RESET_BIT;
187+ logger_.info (" Resetting INA226 to default settings" );
188+ write_u16_to_register ((uint8_t )Reg::CONFIG, word, ec);
189+ return !ec;
190+ }
191+
189192 // / Configure the INA226 with averaging, conversion times, and mode
190193 // / @param avg Averaging mode
191194 // / @param vbus Bus voltage conversion time
@@ -196,18 +199,16 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
196199 bool configure (Avg avg, ConvTime vbus, ConvTime vshunt, Mode mode, std::error_code &ec) {
197200 // Build config: AVG[14:12], VBUSCT[11:9], VSHCT[8:6], MODE[2:0]
198201 uint16_t word = 0 ;
199- word |= (static_cast <uint16_t >(avg) & 0x7 ) << 12 ;
200- word |= (static_cast <uint16_t >(vbus) & 0x7 ) << 9 ;
201- word |= (static_cast <uint16_t >(vshunt) & 0x7 ) << 6 ;
202+ // NOTE: bit 15 is used to reset, so we don't set it here
203+ // NOTE: bits 12-14 are not used, and should always be 0b100 << 12
204+ word |= (static_cast <uint16_t >(avg) & 0x7 ) << 9 ;
205+ word |= (static_cast <uint16_t >(vbus) & 0x7 ) << 6 ;
206+ word |= (static_cast <uint16_t >(vshunt) & 0x7 ) << 3 ;
202207 word |= (static_cast <uint16_t >(mode) & 0x7 );
203208 write_u16_to_register ((uint8_t )Reg::CONFIG, word, ec);
204209 return !ec;
205210 }
206211
207- // Set calibration based on current_lsb (A/LSB) and shunt resistance (Ohms)
208- // CAL = floor(0x8000 / (current_lsb * Rshunt)) per datasheet (5120/ (curr_lsb*R) is common for
209- // INA219, but INA226 uses 0.00512/ (curr_lsb*R) scaled for 16-bit: 0.00512 / (A/LSB * Ohms))
210-
211212 // / Calibrate the INA226 with current LSB and shunt resistance
212213 // / @param current_lsb Current LSB in Amps/LSB
213214 // / @param shunt_res_ohms Shunt resistance in Ohms
@@ -229,6 +230,9 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
229230 // / avoid issues. This function should be called after configuring the
230231 // / INA226.
231232 bool calibrate (float current_lsb, float shunt_res_ohms, std::error_code &ec) {
233+ // Set calibration based on current_lsb (A/LSB) and shunt resistance (Ohms)
234+ // CAL = floor(0x8000 / (current_lsb * Rshunt)) per datasheet (5120/ (curr_lsb*R) is common for
235+ // INA219, but INA226 uses 0.00512/ (curr_lsb*R) scaled for 16-bit: 0.00512 / (A/LSB * Ohms))
232236 std::lock_guard<std::recursive_mutex> lock (base_mutex_);
233237 current_lsb_ = current_lsb;
234238 shunt_res_ohms_ = shunt_res_ohms;
@@ -242,8 +246,43 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
242246 }
243247
244248protected:
249+ // Register map (datasheet)
250+ enum class Reg : uint8_t {
251+ CONFIG = 0x00 ,
252+ SHUNT_VOLTAGE = 0x01 , // 16-bit signed, 2.5uV/LSB
253+ BUS_VOLTAGE = 0x02 , // 16-bit unsigned, 1.25mV/LSB
254+ POWER = 0x03 , // 16-bit unsigned, 25*CURRENT_LSB per LSB
255+ CURRENT = 0x04 , // 16-bit signed, CURRENT_LSB per LSB
256+ CALIBRATION = 0x05 , // 16-bit unsigned
257+ MASK_ENABLE = 0x06 ,
258+ ALERT_LIMIT = 0x07 ,
259+ MANUFACTURER_ID = 0xFE , // 0x5449
260+ DIE_ID = 0xFF , // 0x2260
261+ };
262+
245263 bool init (const Config &c, std::error_code &ec) {
246264 std::lock_guard<std::recursive_mutex> lock (base_mutex_);
265+ // read manufacturer and die ID to verify presence
266+ uint16_t manufacturer = manufacturer_id (ec);
267+ if (ec)
268+ return false ;
269+ if (manufacturer != MANUFACTURER_ID_TI) {
270+ logger_.error (" INA226 manufacturer ID mismatch: expected 0x{:04X}, got 0x{:04X}" ,
271+ MANUFACTURER_ID_TI, manufacturer);
272+ ec = make_error_code (std::errc::no_such_device);
273+ return false ;
274+ }
275+ uint16_t die = die_id (ec);
276+ if (ec)
277+ return false ;
278+ if (die != DIE_ID_INA226) {
279+ logger_.error (" INA226 die ID mismatch: expected 0x{:04X}, got 0x{:04X}" , DIE_ID_INA226, die);
280+ ec = make_error_code (std::errc::no_such_device);
281+ return false ;
282+ }
283+ // if here, device is present, so reset it
284+ if (!reset (ec))
285+ return false ;
247286 // Program config
248287 configure (c.averaging , c.bus_conv_time , c.shunt_conv_time , c.mode , ec);
249288 if (ec)
@@ -259,16 +298,16 @@ class Ina226 : public BasePeripheral<uint8_t, true> {
259298 }
260299
261300 // Raw register reads (signed/unsigned as appropriate by datasheet)
262- int16_t read_shunt_raw (std::error_code &ec) {
301+ int16_t read_shunt_raw (std::error_code &ec) const {
263302 return (int16_t )read_u16_from_register ((uint8_t )Reg::SHUNT_VOLTAGE, ec);
264303 }
265- uint16_t read_bus_raw (std::error_code &ec) {
304+ uint16_t read_bus_raw (std::error_code &ec) const {
266305 return read_u16_from_register ((uint8_t )Reg::BUS_VOLTAGE, ec);
267306 }
268- int16_t read_current_raw (std::error_code &ec) {
307+ int16_t read_current_raw (std::error_code &ec) const {
269308 return (int16_t )read_u16_from_register ((uint8_t )Reg::CURRENT, ec);
270309 }
271- uint16_t read_power_raw (std::error_code &ec) {
310+ uint16_t read_power_raw (std::error_code &ec) const {
272311 return read_u16_from_register ((uint8_t )Reg::POWER, ec);
273312 }
274313
0 commit comments