Skip to content

Commit 7c4e0e7

Browse files
committed
Added ina228 i2c example
1 parent 4c3a3dc commit 7c4e0e7

File tree

7 files changed

+177
-0
lines changed

7 files changed

+177
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ App|Description
159159
[ht16k33_i2c](i2c/ht16k33_i2c) | Drive a 4 digit 14 segment LED with an HT16K33.
160160
[slave_mem_i2c](i2c/slave_mem_i2c) | i2c slave example where the slave implements a 256 byte memory.
161161
[slave_mem_i2c_burst](i2c/slave_mem_i2c) | i2c slave example where the slave implements a 256 byte memory. This version inefficiently writes each byte in a separate call to demonstrate read and write burst mode.
162+
[ina228_i2c](i2c/ina228) | Read voltage, current, temperature, power, energy and charge from INA228 sensor, via I2C.
162163

163164
### Interpolator
164165

i2c/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ if (TARGET hardware_i2c)
1212
add_subdirectory_exclude_platforms(pcf8523_i2c)
1313
add_subdirectory_exclude_platforms(ht16k33_i2c)
1414
add_subdirectory_exclude_platforms(slave_mem_i2c)
15+
add_subdirectory_exclude_platforms(ina228_i2c)
1516
else()
1617
message("Skipping I2C examples as hardware_i2c is unavailable on this platform")
1718
endif()

i2c/ina228_i2c/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
add_executable(ina228_i2c ina228_i2c.c)
2+
3+
# pull in common dependencies and additional i2c hardware support
4+
target_link_libraries(ina228_i2c
5+
pico_stdlib
6+
hardware_i2c)
7+
8+
# create map/bin/hex file etc.
9+
pico_add_extra_outputs(ina228_i2c)
10+
11+
# add url via pico_set_program_url
12+
example_auto_set_url(ina228_i2c)
13+

i2c/ina228_i2c/README.adoc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
= Reading an INA228 power sensor via I2C
2+
3+
This example code shows how to interface the Raspberry Pi Pico with the INA228 current/voltage/power/temperature/energy/charge sensor. The sensor features a 20-bit ADC and supports up to 85V DC, making it a great choice for high precision measurements. It is capable of high-side and low-side measurements (which can be configured by soldering the jumper on the back of the board), but this example focuses on low-side measurements.
4+
5+
The sensor breakout board has an integrated 0.015 ohm, high precision shunt resistor, which is used for measuring voltage drop, which also gives current since the resistor value is known. This current, combined with the load voltage, allows load power and energy to be calculated. This example measures the power consumption of an LED.
6+
7+
[TIP]
8+
======
9+
The INA228 is highly configurable. Find the datasheet online (https://www.ti.com/lit/ds/symlink/ina228.pdf) to explore all of its capabilities beyond the simple example given here.
10+
======
11+
12+
== Wiring information
13+
14+
[[ina228_i2c_wiring]]
15+
[pdfwidth=75%]
16+
.Wiring Diagram for INA228 sensor via I2C.
17+
image::ina228_i2c.png[]
18+
19+
== List of Files
20+
21+
CMakeLists.txt:: CMake file to incorporate the example into the examples build tree.
22+
ina228_i2c.c:: The example code.
23+
24+
== Bill of Materials
25+
26+
.A list of materials required for the example
27+
[[ina228_i2c-bom-table]]
28+
[cols=3]
29+
|===
30+
| *Item* | *Quantity* | Details
31+
| Breadboard | 1 | generic part
32+
| Raspberry Pi Pico 2 | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico-2/
33+
| INA228-based breakout board | 1 | https://www.adafruit.com/product/5832
34+
| M/M Jumper wires | 8 | generic part
35+
| LED | 1 | generic part
36+
| 330 ohm resistor | 1 | generic part
37+
|===

i2c/ina228_i2c/ina228_i2c.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#include <stdio.h>
2+
#include "pico/stdlib.h"
3+
#include "hardware/i2c.h"
4+
#include <math.h>
5+
6+
// LED GPIO
7+
#define EXT_LED_GPIO 2
8+
9+
// INA228 breakout board shunt resistance in Ohms
10+
#define R_SHUNT 0.015
11+
12+
// Max expected current through shunt resistor in Amps
13+
#define MAX_EXPECTED_CURRENT 0.1
14+
#define I2C_ADDR 0x40
15+
16+
// Macros for bit-shifting register reads
17+
#define FLATTEN2(a2) ((a2[0] << 8) + a2[1])
18+
#define FLATTEN3(a3) ((a3[0] << 16) | (a3[1] << 8) | a3[2])
19+
#define FLATTEN3_RESERVED(a3) ((a3[0] << 12) | (a3[1] << 4) | (a3[2] >> 4))
20+
#define FLATTEN5(a5) (((uint64_t) a5[0] << 32) | (a5[1] << 24) | (a5[2] << 16) | (a5[3] << 8) | a5[4])
21+
22+
// ina228 registers (see datasheet)
23+
uint8_t VSHUNT_REG = 0x04;
24+
uint8_t VBUS_REG = 0x05;
25+
uint8_t DIETEMP_REG = 0x06;
26+
uint8_t CURRENT_REG = 0x07;
27+
uint8_t POWER_REG = 0x08;
28+
uint8_t ENERGY_REG = 0x09;
29+
uint8_t CHARGE_REG = 0x0A;
30+
31+
uint8_t ADC_CONFIG_REG = 0x01;
32+
uint8_t SHUNT_CAL_REG = 0x02;
33+
34+
// conversion factors (see datasheet)
35+
// note: VSHUNT_FACTOR assumes ADCRANGE = 0
36+
const double VSHUNT_FACTOR = 312.5 * 1e-9;
37+
const double VBUS_FACTOR = 195.3125 * 1e-6;
38+
const double DIETEMP_FACTOR = 7.8125 * 1e-3;
39+
const double CURRENT_FACTOR = MAX_EXPECTED_CURRENT / (1<<19);
40+
const double POWER_FACTOR = 3.2 * CURRENT_FACTOR ;
41+
const double ENERGY_FACTOR = 16 * 3.2 * CURRENT_FACTOR;
42+
const double CHARGE_FACTOR = CURRENT_FACTOR;
43+
44+
const uint16_t SHUNT_CAL = 13107.2 * 1e6 * CURRENT_FACTOR * R_SHUNT;
45+
46+
float vshunt, vbus, dietemp, current, power, energy, charge;
47+
48+
static void ina228_init() {
49+
// Write to shunt register
50+
uint8_t shunt_msb = (SHUNT_CAL >> 8) & 0xFF;
51+
uint8_t shunt_lsb = SHUNT_CAL & 0xFF;
52+
uint8_t shunt_buf[3] = {SHUNT_CAL_REG, shunt_msb, shunt_lsb};
53+
i2c_write_blocking(i2c_default, I2C_ADDR, shunt_buf, 3, false);
54+
55+
// Write to ADC config register (need to enable continuous mode to access accumulation variables energy and charge)
56+
uint8_t adc_msb = 0xF0;
57+
uint8_t adc_lsb = 0x00;
58+
uint8_t adc_buf[3] = {ADC_CONFIG_REG, adc_msb, adc_lsb};
59+
i2c_write_blocking(i2c_default, I2C_ADDR, adc_buf, 3, false);
60+
}
61+
62+
static void ina228_read(float *vshunt, float *vbus, float *dietemp, float *current, float *power, float *energy, float *charge) {
63+
// Buffers for writing register measurements to
64+
// Some registers are 2 bytes, some are 3 bytes, the accumulation registers are 5 bytes
65+
uint8_t buffer_2[2];
66+
uint8_t buffer_3[3];
67+
uint8_t buffer_5[5];
68+
69+
// For the vshunt, vbus and current registers the 4 least significant bits are reserved and always read zero, so use variant of flatten macro to extract value
70+
i2c_write_blocking(i2c_default, I2C_ADDR, &VSHUNT_REG, 1, true);
71+
i2c_read_blocking(i2c_default, I2C_ADDR, buffer_3, 3, false);
72+
*vshunt = FLATTEN3_RESERVED(buffer_3) * VSHUNT_FACTOR;
73+
74+
i2c_write_blocking(i2c_default, I2C_ADDR, &VBUS_REG, 1, true);
75+
i2c_read_blocking(i2c_default, I2C_ADDR, buffer_3, 3, false);
76+
*vbus = FLATTEN3_RESERVED(buffer_3) * VBUS_FACTOR;
77+
78+
i2c_write_blocking(i2c_default, I2C_ADDR, &DIETEMP_REG, 1, true);
79+
i2c_read_blocking(i2c_default, I2C_ADDR, buffer_2, 2, false);
80+
*dietemp = FLATTEN2(buffer_2) * DIETEMP_FACTOR;
81+
82+
i2c_write_blocking(i2c_default, I2C_ADDR, &CURRENT_REG, 1, true);
83+
i2c_read_blocking(i2c_default, I2C_ADDR, buffer_3, 3, false);
84+
*current = FLATTEN3_RESERVED(buffer_3) * CURRENT_FACTOR;
85+
86+
i2c_write_blocking(i2c_default, I2C_ADDR, &POWER_REG, 1, true);
87+
i2c_read_blocking(i2c_default, I2C_ADDR, buffer_3, 3, false);
88+
*power = FLATTEN3(buffer_3) * POWER_FACTOR;
89+
90+
i2c_write_blocking(i2c_default, I2C_ADDR, &ENERGY_REG, 1, true);
91+
i2c_read_blocking(i2c_default, I2C_ADDR, buffer_5, 5, false);
92+
*energy = FLATTEN5(buffer_5) * ENERGY_FACTOR;
93+
94+
i2c_write_blocking(i2c_default, I2C_ADDR, &CHARGE_REG, 1, true);
95+
i2c_read_blocking(i2c_default, I2C_ADDR, buffer_5, 5, false);
96+
*charge = FLATTEN5(buffer_5) * CHARGE_FACTOR;
97+
}
98+
99+
int main()
100+
{
101+
stdio_init_all();
102+
103+
// Initialise external LED and turn it on (so we can measure some current)
104+
gpio_init(EXT_LED_GPIO);
105+
gpio_set_dir(EXT_LED_GPIO, GPIO_OUT);
106+
gpio_put(EXT_LED_GPIO, true);
107+
108+
// I2C initialisation
109+
i2c_init(i2c_default, 400*1000);
110+
111+
// GPIO initialisation
112+
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
113+
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
114+
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
115+
gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
116+
117+
// Initialise ina228
118+
ina228_init();
119+
120+
while (true) {
121+
ina228_read(&vshunt, &vbus, &dietemp, &current, &power, &energy, &charge);
122+
printf("INA228 Measurements:\nVSHUNT: %f V\nVBUS: %f V\nDIETEMP: %f °C\nCURRENT: %f A\nPOWER: %f W\nENERGY: %f J\nCHARGE: %f C\n-----------------\n", vshunt, vbus, dietemp, current, power, energy, charge);
123+
sleep_ms(1000);
124+
}
125+
}

i2c/ina228_i2c/ina228_i2c.fzz

87.4 KB
Binary file not shown.

i2c/ina228_i2c/ina228_i2c.png

250 KB
Loading

0 commit comments

Comments
 (0)