Skip to content

Commit f2a7437

Browse files
committed
ina230 driver
1 parent 832f005 commit f2a7437

File tree

5 files changed

+700
-0
lines changed

5 files changed

+700
-0
lines changed

obc/app/drivers/ina230/ina230.c

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
#include "ina230.h"
2+
#include "obc_i2c_io.h"
3+
#include "tca6424.h"
4+
#include "obc_logging.h"
5+
#include <stdio.h>
6+
#include <assert.h>
7+
8+
#define INA230_I2C_ADDRESS_ONE 0b1000000U
9+
#define INA230_I2C_ADDRESS_TWO 0b1000001U
10+
11+
// ------------------ TCA6424 IC Related Pins ------------- //
12+
13+
// enable pins for device one
14+
#define INA230_ONE_ENABLE_PIN TCA6424A_PIN_03
15+
#define INA230_TWO_ENABLE_PIN TCA6424A_PIN_01
16+
17+
// alert pins for device two
18+
#define INA230_ONE_ALERT_PIN TCA6424A_PIN_02
19+
#define INA230_TWO_ALERT_PIN TCA6424A_PIN_00
20+
21+
#define INA230_ENABLE_LOAD TCA6424A_GPIO_HIGH
22+
#define INA230_DISABLE_LOAD TCA6424A_GPIO_LOW
23+
#define INA230_ALERT_HIGH TCA6424A_GPIO_HIGH
24+
25+
// ------------------ INA230 IC Related General Configuration Addresses/Bitfields ------------- //
26+
#define INA230_CONFIG_REGISTER_ADDR 0x00U
27+
#define INA230_MASK_ENABLE_REGISTER_ADDR 0x06U
28+
#define INA230_ALERT_LIMIT_REGISTER_ADDR 0x07U
29+
#define INA230_CALIBRATION_REGISTER_ADDR 0x05U
30+
#define INA230_SHUNT_VOLTAGE_REGISTER_ADDR 0x01U
31+
#define INA230_BUS_VOLTAGE_REGISTER_ADDR 0x02U
32+
#define INA230_POWER_REGISTER_ADDR 0x03U
33+
#define INA230_CURRENT_REGISTER_ADDR 0x04U
34+
35+
#define INA230_CONFIG_MODE_SHIFT 0U
36+
#define INA230_CONFIG_SHU_SHIFT 3U
37+
#define INA230_CONFIG_AVG_SHIFT 9U
38+
#define INA230_CONFIG_BUS_SHIFT 6U
39+
40+
// ------------------ INA230 IC Configuration Masks/Flags ------------- //
41+
#define INA230_MASK_ENABLE_SHUNT_OVER_ALERT_MASK (0b1 << 15)
42+
#define INA230_MASK_ENABLE_SHUNT_UNDER_ALERT_MASK (0b1 << 14)
43+
#define INA230_MASK_ENABLE_BUS_OVER_ALERT_MASK (0b1 << 13)
44+
#define INA230_MASK_ENABLE_BUS_UNDER_ALERT_MASK (0b1 << 12)
45+
#define INA230_MASK_ENABLE_POWER_OVER_ALERT_MASK (0b1 << 11)
46+
47+
#define INA230_MASK_ENABLE_TRANSPARENT_MODE_SET_MASK 1U
48+
49+
// macros for lsb, shunt resistor, and calibration value
50+
#define INA230_SHUNT_VOLTAGE_LSB 0.0000025f
51+
#define INA230_CURRENT_LSB 0.001f // 1 mA, current least significant bit
52+
#define INA230_SHUNT_RESISTOR 0.1f // 0.1 ohms, shunt resistor value
53+
#define INA230_CALIBRATION_VALUE (uint16_t)(0.00512 / (INA230_CURRENT_LSB * INA230_SHUNT_RESISTOR))
54+
55+
// buffer sizes
56+
#define INA_REG_CONF_BUFF_SIZE 2
57+
58+
#define I2C_TRANSFER_TIMEOUT_TICKS pdMS_TO_TICKS(100) // 100 ms in RTOS ticks
59+
60+
// function pointers to switch between mock and real data
61+
obc_error_code_t (*i2cReadRegFuncPtr)(uint8_t, uint8_t, uint8_t*, uint16_t, TickType_t) = NULL;
62+
obc_error_code_t (*i2cWriteRegFuncPtr)(uint8_t, uint8_t, uint8_t*, uint16_t) = NULL;
63+
64+
// ------------------ INA230 Device Configuration ------------- //
65+
typedef struct {
66+
uint8_t i2cDeviceAddress;
67+
68+
// TCA GPIO Expander ports
69+
uint8_t tcaAlertPort;
70+
uint8_t tcaEnablePort;
71+
72+
// Values for the configuration register
73+
uint8_t configurationMode;
74+
uint8_t configurationShunt;
75+
uint8_t configurationAvg;
76+
uint8_t configurationBus;
77+
78+
// Values for other registers
79+
uint16_t maskEnableRegister;
80+
uint16_t calibrationRegister;
81+
uint16_t alertRegister;
82+
} ina230_config_t;
83+
84+
static const ina230_config_t ina230Devices[] = {[INA230_DEVICE_ONE] = {.i2cDeviceAddress = INA230_I2C_ADDRESS_ONE,
85+
.tcaAlertPort = INA230_ONE_ALERT_PIN,
86+
.tcaEnablePort = INA230_ONE_ENABLE_PIN},
87+
88+
[INA230_DEVICE_TWO] = {.i2cDeviceAddress = INA230_I2C_ADDRESS_TWO,
89+
.tcaAlertPort = INA230_TWO_ALERT_PIN,
90+
.tcaEnablePort = INA230_TWO_ENABLE_PIN}};
91+
92+
static obc_error_code_t writeINA230Register(uint8_t regAddress, uint8_t* data, uint8_t size, uint8_t i2cAddress);
93+
static obc_error_code_t initTca6424PinState();
94+
95+
/**
96+
* @brief Initializes the INA230 devices
97+
*
98+
* This function initializes all INA230 devices by configuring their registers
99+
* and setting up the corresponding TCA6424 pins for alerts and enable control via initTca6424PinState().
100+
*
101+
* @return OBC_ERR_CODE_SUCCESS if initialization is successful,
102+
* otherwise returns an appropriate error code
103+
*/
104+
obc_error_code_t initINA230() {
105+
#ifndef USE_MOCK_I2C
106+
i2cReadRegFuncPtr = i2cReadReg;
107+
i2cWriteRegFuncPtr = i2cWriteReg;
108+
#endif
109+
obc_error_code_t errCode;
110+
for (uint8_t i = 0; i < INA230_DEVICE_COUNT; ++i) {
111+
const ina230_config_t device = ina230Devices[i];
112+
const uint16_t configurationRegister = (device.configurationMode << INA230_CONFIG_MODE_SHIFT) |
113+
(device.configurationShunt << INA230_CONFIG_SHU_SHIFT) |
114+
(device.configurationAvg << INA230_CONFIG_AVG_SHIFT) |
115+
(device.configurationBus << INA230_CONFIG_BUS_SHIFT);
116+
117+
uint8_t configRegisterUnpacked[] = {configurationRegister & 0xFF, configurationRegister >> 8};
118+
uint8_t maskEnRegisterUnpacked[] = {device.maskEnableRegister & 0xFF, device.maskEnableRegister >> 8};
119+
uint8_t alertRegisterUnpacked[] = {device.alertRegister & 0xFF, device.alertRegister >> 8};
120+
uint8_t calibrationRegisterUnpacked[] = {device.calibrationRegister & 0xFF, device.calibrationRegister >> 8};
121+
122+
uint8_t deviceAddress = device.i2cDeviceAddress;
123+
RETURN_IF_ERROR_CODE(writeINA230Register(INA230_CONFIG_REGISTER_ADDR, configRegisterUnpacked,
124+
sizeof(configRegisterUnpacked) / sizeof(configRegisterUnpacked[0]),
125+
deviceAddress));
126+
RETURN_IF_ERROR_CODE(writeINA230Register(INA230_MASK_ENABLE_REGISTER_ADDR, maskEnRegisterUnpacked,
127+
sizeof(maskEnRegisterUnpacked) / sizeof(maskEnRegisterUnpacked[0]),
128+
deviceAddress));
129+
RETURN_IF_ERROR_CODE(writeINA230Register(INA230_ALERT_LIMIT_REGISTER_ADDR, alertRegisterUnpacked,
130+
sizeof(alertRegisterUnpacked) / sizeof(alertRegisterUnpacked[0]),
131+
deviceAddress));
132+
// uint16_t calibrationValue = INA230_CALIBRATION_VALUE;
133+
// uint8_t calibrationRegisterUnpacked[] = {calibrationValue & 0xFF, calibrationValue >> 8};
134+
135+
RETURN_IF_ERROR_CODE(writeINA230Register(
136+
INA230_CALIBRATION_REGISTER_ADDR, calibrationRegisterUnpacked,
137+
sizeof(calibrationRegisterUnpacked) / sizeof(calibrationRegisterUnpacked[0]), deviceAddress));
138+
}
139+
140+
RETURN_IF_ERROR_CODE(initTca6424PinState());
141+
return OBC_ERR_CODE_SUCCESS;
142+
}
143+
144+
/**
145+
* @brief Reads the TCA6424 input and disables INA230 devices if an alert is detected
146+
*
147+
* This function reads the complete input from the TCA6424 and checks for alerts
148+
* on the INA230 devices. If an alert is detected, it disables the corresponding device.
149+
*
150+
* @param device The INA230 device to check (currently unused in the function)
151+
* @return OBC_ERR_CODE_SUCCESS if operation is successful,
152+
* otherwise returns an appropriate error code
153+
*/
154+
inline obc_error_code_t readAndDisableIfAlert(ina230_device_t device) {
155+
uint32_t IOPortValue = 0;
156+
obc_error_code_t errCode;
157+
RETURN_IF_ERROR_CODE(readTCA642CompleteInput(&IOPortValue)); // reads 24 bit input of TCA GPIO Expander
158+
159+
for (uint8_t i = 0; i < INA230_DEVICE_COUNT; ++i) {
160+
uint8_t pinLocation =
161+
ina230Devices[i].tcaEnablePort; // specific pin on TCA that this ina230 controls, should this be alertPort?
162+
uint8_t index = ((pinLocation & 0x0F) +
163+
((pinLocation >> 1) & 0x18)); // converts the pinLocation to an index in the 24 bit IOPortValue
164+
if (IOPortValue & (0b1 << index)) { // if it's high
165+
uint8_t drivePort = INA230_DISABLE_LOAD;
166+
RETURN_IF_ERROR_CODE(driveTCA6424APinOutput(pinLocation, drivePort));
167+
}
168+
}
169+
return OBC_ERR_CODE_SUCCESS;
170+
}
171+
/**
172+
* @brief Writes data to an INA230 register
173+
*
174+
* This function writes data to a specified register of an INA230 device.
175+
*
176+
* @param regAddress The address of the register to write to
177+
* @param data Pointer to the data to be written
178+
* @param size The size of the data to be written
179+
* @param i2cAddress The I2C address of the INA230 device
180+
* @return OBC_ERR_CODE_SUCCESS if write is successful,
181+
* otherwise returns an appropriate error code
182+
*/
183+
184+
static obc_error_code_t writeINA230Register(uint8_t regAddress, uint8_t* data, uint8_t size, uint8_t i2cAddress) {
185+
if (data == NULL) return OBC_ERR_CODE_INVALID_ARG;
186+
if (i2cAddress != INA230_I2C_ADDRESS_ONE && i2cAddress != INA230_I2C_ADDRESS_TWO) return OBC_ERR_CODE_INVALID_ARG;
187+
188+
obc_error_code_t errCode;
189+
RETURN_IF_ERROR_CODE(i2cWriteRegFuncPtr(i2cAddress, regAddress, data, size));
190+
return OBC_ERR_CODE_SUCCESS;
191+
}
192+
/**
193+
* @brief Initializes the TCA6424 pin states for INA230 devices
194+
*
195+
* This function configures the TCA6424 pins used for alerts and enable control
196+
* of the INA230 devices. It sets up the alert pins as inputs and the enable pins as outputs.
197+
*
198+
* @return OBC_ERR_CODE_SUCCESS if initialization is successful,
199+
* otherwise returns an appropriate error code
200+
*/
201+
static obc_error_code_t initTca6424PinState() {
202+
obc_error_code_t errCode;
203+
for (uint8_t i = 0; i < INA230_DEVICE_COUNT; ++i) {
204+
ina230_config_t device = ina230Devices[i];
205+
RETURN_IF_ERROR_CODE(configureTCA6424APin(
206+
device.tcaAlertPort, TCA6424A_GPIO_CONFIG_INPUT)); // alert pin is output of ina230 and input to tca
207+
RETURN_IF_ERROR_CODE(configureTCA6424APin(
208+
device.tcaEnablePort, TCA6424A_GPIO_CONFIG_OUTPUT)); // alert pin is input of ina230 and output of tca
209+
RETURN_IF_ERROR_CODE(
210+
driveTCA6424APinOutput(device.tcaEnablePort, INA230_ENABLE_LOAD)); // set the pin enable pin to high
211+
}
212+
return OBC_ERR_CODE_SUCCESS;
213+
}
214+
215+
// function to get shunt voltage
216+
// i2cAddress = ina230 device address
217+
// shuntVoltage = pointer to store shuntVoltage
218+
219+
obc_error_code_t getINA230ShuntVoltage(uint8_t i2cAddress, float* shuntVoltage) {
220+
if (shuntVoltage == NULL) return OBC_ERR_CODE_INVALID_ARG;
221+
222+
uint8_t shuntVoltageRaw[INA_REG_CONF_BUFF_SIZE] = {0}; // store 2 bytes of shunt voltage
223+
obc_error_code_t errCode;
224+
225+
if (i2cAddress != INA230_I2C_ADDRESS_ONE && i2cAddress != INA230_I2C_ADDRESS_TWO) return OBC_ERR_CODE_INVALID_ARG;
226+
227+
// Read the 16-bit shunt voltage register
228+
errCode = i2cReadRegFuncPtr(i2cAddress, INA230_SHUNT_VOLTAGE_REGISTER_ADDR, shuntVoltageRaw, 2,
229+
I2C_TRANSFER_TIMEOUT_TICKS); // last param not sure
230+
if (errCode != OBC_ERR_CODE_SUCCESS) return errCode;
231+
232+
// Combine the two bytes into a 16-bit value
233+
int16_t shuntVoltageValue = (shuntVoltageRaw[0] << 8) | shuntVoltageRaw[1];
234+
235+
236+
// Convert to actual voltage (signed value)
237+
*shuntVoltage = shuntVoltageValue * INA230_SHUNT_VOLTAGE_LSB;
238+
239+
return OBC_ERR_CODE_SUCCESS;
240+
}
241+
242+
obc_error_code_t getINA230ShuntVoltageForDevice(uint8_t deviceIndex, float* shuntVoltage) {
243+
if (shuntVoltage == NULL) return OBC_ERR_CODE_INVALID_ARG;
244+
245+
if (deviceIndex >= INA230_DEVICE_COUNT) return OBC_ERR_CODE_INVALID_ARG; // Check valid device index
246+
247+
// Retrieve the I2C address for the specified INA230 device
248+
uint8_t i2cAddress = ina230Devices[deviceIndex].i2cDeviceAddress;
249+
250+
// Get the shunt voltage
251+
return getINA230ShuntVoltage(i2cAddress, shuntVoltage);
252+
}
253+
254+
void main_usage() {
255+
float shuntVoltage = 0;
256+
257+
// Call the function for INA230 device 1 (index 0)
258+
obc_error_code_t errCode = getINA230ShuntVoltageForDevice(0, &shuntVoltage);
259+
// print shunt volatge
260+
printf("Shunt Voltage for INA230 Device 1: %f V\n", shuntVoltage);
261+
262+
if (errCode == OBC_ERR_CODE_SUCCESS) {
263+
printf("Shunt Voltage for INA230 Device 1: %f V\n", shuntVoltage);
264+
} else {
265+
printf("Error reading shunt voltage for INA230 Device 1\n");
266+
}
267+
}
268+
269+
// general disable function for ina230 device
270+
271+
obc_error_code_t disableNoAlert(ina230_device_t device) {
272+
uint32_t IOPortValue = 0;
273+
obc_error_code_t errCode;
274+
275+
for (uint8_t i = 0; i < INA230_DEVICE_COUNT; ++i) {
276+
uint8_t pinLocation =
277+
ina230Devices[i].tcaEnablePort; // specific pin on TCA that this ina230 controls, should this be alertPort?
278+
uint8_t index = ((pinLocation & 0x0F) +
279+
((pinLocation >> 1) & 0x18)); // converts the pinLocation to an index in the 24 bit IOPortValue
280+
// disbale
281+
uint8_t drivePort = INA230_DISABLE_LOAD;
282+
RETURN_IF_ERROR_CODE(driveTCA6424APinOutput(pinLocation, drivePort));
283+
}
284+
return OBC_ERR_CODE_SUCCESS;
285+
}
286+
287+
// Notes (delete later)
288+
// Bus voltage register:
289+
// Address: 0x02
290+
// Size: 16 bits (2 bytes)
291+
// Each bit on the register represents 1.25 mV
292+
293+
/**
294+
* @brief Gets the bus voltage
295+
*
296+
* @param i2cAddress The I2C address of the INA230 device
297+
* @return OBC_ERR_CODE_SUCCESS if write is successful,
298+
* otherwise return an appropriate error code
299+
*/
300+
// function to get bus voltage
301+
obc_error_code_t getINA230BusVoltage(uint8_t i2cAddress, float* busVoltage) {
302+
if (busVoltage == NULL) return OBC_ERR_CODE_INVALID_ARG;
303+
if (i2cAddress != INA230_I2C_ADDRESS_ONE && i2cAddress != INA230_I2C_ADDRESS_TWO) return OBC_ERR_CODE_INVALID_ARG;
304+
305+
obc_error_code_t errCode;
306+
uint8_t busVoltageRaw[2] = {};
307+
RETURN_IF_ERROR_CODE(
308+
i2cReadRegFuncPtr(i2cAddress, INA230_BUS_VOLTAGE_REGISTER_ADDR, busVoltageRaw, 2, I2C_TRANSFER_TIMEOUT_TICKS));
309+
uint16_t busVoltageValue = (busVoltageRaw[0] << 8) | busVoltageRaw[1];
310+
*busVoltage = busVoltageValue * 0.00125f;
311+
312+
return OBC_ERR_CODE_SUCCESS;
313+
}
314+
315+
obc_error_code_t getINA230BusVoltageForDevice(uint8_t deviceIndex, float* busVoltage) {
316+
if (busVoltage == NULL || deviceIndex >= INA230_DEVICE_COUNT) return OBC_ERR_CODE_INVALID_ARG;
317+
uint8_t i2cAddress = ina230Devices[deviceIndex].i2cDeviceAddress;
318+
return getINA230BusVoltage(i2cAddress, busVoltage);
319+
}
320+
321+
obc_error_code_t getINA230Power(uint8_t i2cAddress, float* power) {
322+
obc_error_code_t errCode;
323+
if (power == NULL || (i2cAddress != INA230_I2C_ADDRESS_ONE && i2cAddress != INA230_I2C_ADDRESS_TWO)) return OBC_ERR_CODE_INVALID_ARG;
324+
uint8_t powerRaw[INA_REG_CONF_BUFF_SIZE] = {};
325+
RETURN_IF_ERROR_CODE(
326+
i2cReadRegFuncPtr(i2cAddress, INA230_POWER_REGISTER_ADDR, powerRaw, 2, I2C_TRANSFER_TIMEOUT_TICKS));
327+
uint16_t powerValue = (powerRaw[0] << 8) | powerRaw[1];
328+
*power = powerValue * (INA230_CURRENT_LSB * 25);
329+
return OBC_ERR_CODE_SUCCESS;
330+
}
331+
332+
obc_error_code_t getINA230PowerForDevice(uint8_t deviceIndex, float* power) {
333+
if (power == NULL || deviceIndex >= INA230_DEVICE_COUNT) return OBC_ERR_CODE_INVALID_ARG;
334+
uint8_t i2cAddress = ina230Devices[deviceIndex].i2cDeviceAddress;
335+
return getINA230Power(i2cAddress, power);
336+
}
337+
338+
// function to get current
339+
obc_error_code_t getINA230Current(uint8_t i2cAddress, float* current) {
340+
obc_error_code_t errCode;
341+
if (current == NULL || (i2cAddress != INA230_I2C_ADDRESS_ONE && i2cAddress != INA230_I2C_ADDRESS_TWO)) return OBC_ERR_CODE_INVALID_ARG;
342+
uint8_t currentRaw[INA_REG_CONF_BUFF_SIZE] = {};
343+
RETURN_IF_ERROR_CODE(
344+
i2cReadRegFuncPtr(i2cAddress, INA230_CURRENT_REGISTER_ADDR, currentRaw, 2, I2C_TRANSFER_TIMEOUT_TICKS));
345+
int16_t currentValue = (currentRaw[0] << 8) | currentRaw[1];
346+
*current = currentValue * INA230_CURRENT_LSB;
347+
return OBC_ERR_CODE_SUCCESS;
348+
}
349+
350+
obc_error_code_t getINA230CurrentForDevice(uint8_t deviceIndex, float* current) {
351+
if (current == NULL || deviceIndex >= INA230_DEVICE_COUNT) return OBC_ERR_CODE_INVALID_ARG;
352+
uint8_t i2cAddress = ina230Devices[deviceIndex].i2cDeviceAddress;
353+
return getINA230Current(i2cAddress, current);
354+
}
355+
356+
// allow loop through

obc/app/drivers/ina230/ina230.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#ifdef __cplusplus
2+
extern "C" {
3+
#endif
4+
5+
#pragma once
6+
7+
#include "obc_errors.h"
8+
#include <stdint.h>
9+
10+
#ifdef USE_MOCK_I2C
11+
#ifndef TICK_TYPE_H
12+
typedef uint32_t TickType_t;
13+
#endif
14+
#else
15+
#include "os_portmacro.h"
16+
#endif
17+
18+
typedef enum { INA230_DEVICE_ONE = 0x00, INA230_DEVICE_TWO, INA230_DEVICE_COUNT } ina230_device_t;
19+
20+
// function pointers to switch between mock and real data
21+
extern obc_error_code_t (*i2cReadRegFuncPtr)(uint8_t, uint8_t, uint8_t*, uint16_t, TickType_t);
22+
extern obc_error_code_t (*i2cWriteRegFuncPtr)(uint8_t, uint8_t, uint8_t*, uint16_t);
23+
24+
obc_error_code_t initINA230();
25+
obc_error_code_t readAndDisableIfAlert(ina230_device_t device);
26+
obc_error_code_t getINA230ShuntVoltage(uint8_t i2cAddress, float* shuntVoltage);
27+
obc_error_code_t getINA230ShuntVoltageForDevice(uint8_t deviceIndex, float* shuntVoltage);
28+
void main_usage();
29+
obc_error_code_t disableNoAlert(ina230_device_t device);
30+
obc_error_code_t getINA230BusVoltage(uint8_t i2cAddress, float* busVoltage);
31+
obc_error_code_t getINA230BusVoltageForDevice(uint8_t deviceIndex, float* busVoltage);
32+
obc_error_code_t getINA230Power(uint8_t i2cAddress, float* power);
33+
obc_error_code_t getINA230PowerForDevice(uint8_t deviceIndex, float* power);
34+
obc_error_code_t getINA230Current(uint8_t i2cAddress, float* power);
35+
obc_error_code_t getINA230CurrentForDevice(uint8_t deviceIndex, float* power);
36+
37+
#ifdef __cplusplus
38+
}
39+
#endif

0 commit comments

Comments
 (0)