mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-15 04:53:34 +08:00
feat(voxl2): add ina231 power monitor driver
Add a standalone INA231 power monitor driver modeled after the existing INA226 driver. The INA231 is register-compatible with the INA226 but lacks manufacturer/die ID registers, so probe reads the config register instead. Default parameter values (config, shunt, max current) are aligned with the voxlpm driver's INA231 configuration. Enable the driver on the voxl2 board and add INA231 support to the voxl-px4-start script. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
17bf9ccb5d
commit
0cc603b88d
@@ -39,6 +39,7 @@ CONFIG_DRIVERS_MAGNETOMETER_ST_IIS2MDC=y
|
||||
CONFIG_DRIVERS_POWER_MONITOR_VOXLPM=y
|
||||
CONFIG_DRIVERS_POWER_MONITOR_INA226=y
|
||||
CONFIG_DRIVERS_POWER_MONITOR_INA228=y
|
||||
CONFIG_DRIVERS_POWER_MONITOR_INA231=y
|
||||
CONFIG_DRIVERS_QSHELL_QURT=y
|
||||
CONFIG_DRIVERS_RC_CRSF_RC=y
|
||||
CONFIG_DRIVERS_VOXL2_IO=y
|
||||
|
||||
@@ -221,6 +221,9 @@ elif [ "$POWER_MANAGER" == "INA226" ]; then
|
||||
elif [ "$POWER_MANAGER" == "INA228" ]; then
|
||||
/bin/echo "Starting INA228 power monitor"
|
||||
qshell ina228 start -X -b 2
|
||||
elif [ "$POWER_MANAGER" == "INA231" ]; then
|
||||
/bin/echo "Starting INA231 power monitor"
|
||||
qshell ina231 start -X -b 2
|
||||
fi
|
||||
|
||||
if [ "$AIRSPEED_SENSOR" == "MS4525DO" ]; then
|
||||
|
||||
@@ -241,6 +241,7 @@
|
||||
#define DRV_POWER_DEVTYPE_VOXLPM 0xD2
|
||||
#define DRV_POWER_DEVTYPE_INA220 0xD3
|
||||
#define DRV_POWER_DEVTYPE_INA238 0xD4
|
||||
#define DRV_POWER_DEVTYPE_INA231 0xD5
|
||||
|
||||
#define DRV_DIST_DEVTYPE_TF02PRO 0xE0
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
############################################################################
|
||||
#
|
||||
# Copyright (c) 2019-2026 PX4 Development Team. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# 3. Neither the name PX4 nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
############################################################################
|
||||
px4_add_module(
|
||||
MODULE drivers__ina231
|
||||
MAIN ina231
|
||||
COMPILE_FLAGS
|
||||
-Wno-cast-align # TODO: fix and enable
|
||||
SRCS
|
||||
ina231_main.cpp
|
||||
ina231.cpp
|
||||
MODULE_CONFIG
|
||||
ina231_params.yaml
|
||||
DEPENDS
|
||||
battery
|
||||
px4_work_queue
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
menuconfig DRIVERS_POWER_MONITOR_INA231
|
||||
bool "ina231"
|
||||
default n
|
||||
---help---
|
||||
Enable support for ina231
|
||||
@@ -0,0 +1,338 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2019-2026 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file ina231.cpp
|
||||
*
|
||||
* Driver for the I2C attached INA231
|
||||
*/
|
||||
|
||||
#include "ina231.h"
|
||||
|
||||
|
||||
INA231::INA231(const I2CSPIDriverConfig &config, int battery_index) :
|
||||
I2C(config),
|
||||
ModuleParams(nullptr),
|
||||
I2CSPIDriver(config),
|
||||
_sample_perf(perf_alloc(PC_ELAPSED, "ina231_read")),
|
||||
_comms_errors(perf_alloc(PC_COUNT, "ina231_com_err")),
|
||||
_collection_errors(perf_alloc(PC_COUNT, "ina231_collection_err")),
|
||||
_measure_errors(perf_alloc(PC_COUNT, "ina231_measurement_err")),
|
||||
_battery(battery_index, this, INA231_SAMPLE_INTERVAL_US, battery_status_s::SOURCE_POWER_MODULE)
|
||||
{
|
||||
float fvalue = MAX_CURRENT;
|
||||
_max_current = fvalue;
|
||||
param_t ph = param_find("INA231_CURRENT");
|
||||
|
||||
if (ph != PARAM_INVALID && param_get(ph, &fvalue) == PX4_OK) {
|
||||
_max_current = fvalue;
|
||||
}
|
||||
|
||||
fvalue = INA231_SHUNT;
|
||||
_rshunt = fvalue;
|
||||
ph = param_find("INA231_SHUNT");
|
||||
|
||||
if (ph != PARAM_INVALID && param_get(ph, &fvalue) == PX4_OK) {
|
||||
_rshunt = fvalue;
|
||||
}
|
||||
|
||||
ph = param_find("INA231_CONFIG");
|
||||
int32_t value = INA231_CONFIG;
|
||||
_config = (uint16_t)value;
|
||||
|
||||
if (ph != PARAM_INVALID && param_get(ph, &value) == PX4_OK) {
|
||||
_config = (uint16_t)value;
|
||||
}
|
||||
|
||||
_mode_triggered = ((_config & INA231_MODE_MASK) >> INA231_MODE_SHIFTS) <=
|
||||
((INA231_MODE_SHUNT_BUS_TRIG & INA231_MODE_MASK) >>
|
||||
INA231_MODE_SHIFTS);
|
||||
|
||||
_current_lsb = _max_current / DN_MAX;
|
||||
_power_lsb = 25 * _current_lsb;
|
||||
|
||||
// We need to publish immediately, to guarantee that the first instance of the driver publishes to uORB instance 0
|
||||
setConnected(false);
|
||||
_battery.updateAndPublishBatteryStatus(hrt_absolute_time());
|
||||
|
||||
I2C::_retries = 5;
|
||||
}
|
||||
|
||||
INA231::~INA231()
|
||||
{
|
||||
/* free perf counters */
|
||||
perf_free(_sample_perf);
|
||||
perf_free(_comms_errors);
|
||||
perf_free(_collection_errors);
|
||||
perf_free(_measure_errors);
|
||||
}
|
||||
|
||||
int INA231::read(uint8_t address, int16_t &data)
|
||||
{
|
||||
// read desired little-endian value via I2C
|
||||
uint16_t received_bytes;
|
||||
int ret = PX4_ERROR;
|
||||
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
ret = transfer(&address, 1, (uint8_t *)&received_bytes, sizeof(received_bytes));
|
||||
|
||||
if (ret == PX4_OK) {
|
||||
data = swap16(received_bytes);
|
||||
break;
|
||||
|
||||
} else {
|
||||
perf_count(_comms_errors);
|
||||
PX4_DEBUG("i2c::transfer returned %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int INA231::write(uint8_t address, uint16_t value)
|
||||
{
|
||||
uint8_t data[3] = {address, ((uint8_t)((value & 0xff00) >> 8)), (uint8_t)(value & 0xff)};
|
||||
return transfer(data, sizeof(data), nullptr, 0);
|
||||
}
|
||||
|
||||
int
|
||||
INA231::init()
|
||||
{
|
||||
int ret = PX4_ERROR;
|
||||
|
||||
/* do I2C init (and probe) first */
|
||||
if (I2C::init() != PX4_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
write(INA231_REG_CONFIGURATION, INA231_RST);
|
||||
|
||||
_cal = INA231_CONST / (_current_lsb * _rshunt);
|
||||
|
||||
if (write(INA231_REG_CALIBRATION, _cal) < 0) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
// If we run in continuous mode then start it here
|
||||
|
||||
if (!_mode_triggered) {
|
||||
ret = write(INA231_REG_CONFIGURATION, _config);
|
||||
|
||||
} else {
|
||||
ret = PX4_OK;
|
||||
}
|
||||
|
||||
start();
|
||||
_sensor_ok = true;
|
||||
|
||||
_initialized = ret == PX4_OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
INA231::force_init()
|
||||
{
|
||||
int ret = init();
|
||||
|
||||
start();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
INA231::probe()
|
||||
{
|
||||
int16_t value{0};
|
||||
|
||||
if (read(INA231_REG_CONFIGURATION, value) != PX4_OK) {
|
||||
PX4_DEBUG("probe failed to read config register");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return PX4_OK;
|
||||
}
|
||||
|
||||
int
|
||||
INA231::measure()
|
||||
{
|
||||
int ret = PX4_OK;
|
||||
|
||||
if (_mode_triggered) {
|
||||
ret = write(INA231_REG_CONFIGURATION, _config);
|
||||
|
||||
if (ret < 0) {
|
||||
perf_count(_comms_errors);
|
||||
PX4_DEBUG("i2c::transfer returned %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
INA231::collect()
|
||||
{
|
||||
perf_begin(_sample_perf);
|
||||
|
||||
if (_parameter_update_sub.updated()) {
|
||||
// Read from topic to clear updated flag
|
||||
parameter_update_s parameter_update;
|
||||
_parameter_update_sub.copy(¶meter_update);
|
||||
|
||||
updateParams();
|
||||
}
|
||||
|
||||
// read from the sensor
|
||||
// Note: If the power module is connected backwards, then the values of _power, _current, and _shunt will be negative but otherwise valid.
|
||||
bool success{true};
|
||||
success = success && (read(INA231_REG_BUSVOLTAGE, _bus_voltage) == PX4_OK);
|
||||
// success = success && (read(INA231_REG_POWER, _power) == PX4_OK);
|
||||
success = success && (read(INA231_REG_CURRENT, _current) == PX4_OK);
|
||||
// success = success && (read(INA231_REG_SHUNTVOLTAGE, _shunt) == PX4_OK);
|
||||
|
||||
if (setConnected(success)) {
|
||||
_battery.updateVoltage(static_cast<float>(_bus_voltage * INA231_VSCALE));
|
||||
_battery.updateCurrent(static_cast<float>(_current * _current_lsb));
|
||||
}
|
||||
|
||||
_battery.updateAndPublishBatteryStatus(hrt_absolute_time());
|
||||
|
||||
perf_end(_sample_perf);
|
||||
|
||||
if (success) {
|
||||
return PX4_OK;
|
||||
|
||||
} else {
|
||||
return PX4_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
INA231::start()
|
||||
{
|
||||
ScheduleClear();
|
||||
|
||||
/* reset the report ring and state machine */
|
||||
_collect_phase = false;
|
||||
|
||||
_measure_interval = INA231_CONVERSION_INTERVAL;
|
||||
|
||||
/* schedule a cycle to start things */
|
||||
ScheduleDelayed(5);
|
||||
}
|
||||
|
||||
void
|
||||
INA231::RunImpl()
|
||||
{
|
||||
if (_initialized) {
|
||||
if (_collect_phase) {
|
||||
/* perform collection */
|
||||
if (collect() != PX4_OK) {
|
||||
perf_count(_collection_errors);
|
||||
/* if error restart the measurement state machine */
|
||||
start();
|
||||
return;
|
||||
}
|
||||
|
||||
/* next phase is measurement */
|
||||
_collect_phase = !_mode_triggered;
|
||||
|
||||
if (_measure_interval > INA231_CONVERSION_INTERVAL) {
|
||||
/* schedule a fresh cycle call when we are ready to measure again */
|
||||
ScheduleDelayed(_measure_interval - INA231_CONVERSION_INTERVAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Measurement phase */
|
||||
|
||||
/* Perform measurement */
|
||||
if (measure() != PX4_OK) {
|
||||
perf_count(_measure_errors);
|
||||
}
|
||||
|
||||
/* next phase is collection */
|
||||
_collect_phase = true;
|
||||
|
||||
/* schedule a fresh cycle call when the measurement is done */
|
||||
ScheduleDelayed(INA231_CONVERSION_INTERVAL);
|
||||
|
||||
} else {
|
||||
setConnected(false);
|
||||
_battery.updateAndPublishBatteryStatus(hrt_absolute_time());
|
||||
|
||||
if (init() != PX4_OK) {
|
||||
ScheduleDelayed(INA231_INIT_RETRY_INTERVAL_US);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool INA231::setConnected(bool state)
|
||||
{
|
||||
// Filter out brief I2C failures for 2s
|
||||
if (state) {
|
||||
_connected = INA231_SAMPLE_FREQUENCY_HZ * 2;
|
||||
|
||||
} else if (_connected > 0) {
|
||||
_connected--;
|
||||
}
|
||||
|
||||
if (_connected > 0) {
|
||||
_battery.setConnected(true);
|
||||
|
||||
} else {
|
||||
_battery.setConnected(false);
|
||||
_battery.updateVoltage(0);
|
||||
_battery.updateCurrent(0);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void
|
||||
INA231::print_status()
|
||||
{
|
||||
I2CSPIDriverBase::print_status();
|
||||
|
||||
if (_initialized) {
|
||||
perf_print_counter(_sample_perf);
|
||||
perf_print_counter(_comms_errors);
|
||||
|
||||
printf("poll interval: %u \n", _measure_interval);
|
||||
|
||||
} else {
|
||||
PX4_INFO("Device not initialized. Retrying every %d ms until battery is plugged in.",
|
||||
INA231_INIT_RETRY_INTERVAL_US / 1000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2019-2026 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file ina231.h
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <px4_platform_common/px4_config.h>
|
||||
#include <px4_platform_common/getopt.h>
|
||||
#include <drivers/device/i2c.h>
|
||||
#include <lib/perf/perf_counter.h>
|
||||
#include <battery/battery.h>
|
||||
#include <drivers/drv_hrt.h>
|
||||
#include <uORB/SubscriptionInterval.hpp>
|
||||
#include <uORB/topics/parameter_update.h>
|
||||
#include <px4_platform_common/i2c_spi_buses.h>
|
||||
|
||||
using namespace time_literals;
|
||||
|
||||
/* Configuration Constants */
|
||||
#define INA231_BASEADDR 0x44 /* 7-bit address. 8-bit address is 0x88 */
|
||||
// If initialization is forced (with the -f flag on the command line), but it fails, the drive will try again to
|
||||
// connect to the INA231 every this many microseconds
|
||||
#define INA231_INIT_RETRY_INTERVAL_US 500000
|
||||
|
||||
/* INA231 Registers addresses */
|
||||
#define INA231_REG_CONFIGURATION (0x00)
|
||||
#define INA231_REG_SHUNTVOLTAGE (0x01)
|
||||
#define INA231_REG_BUSVOLTAGE (0x02)
|
||||
#define INA231_REG_POWER (0x03)
|
||||
#define INA231_REG_CURRENT (0x04)
|
||||
#define INA231_REG_CALIBRATION (0x05)
|
||||
#define INA231_REG_MASKENABLE (0x06)
|
||||
#define INA231_REG_ALERTLIMIT (0x07)
|
||||
|
||||
/* INA231 Configuration Register */
|
||||
#define INA231_MODE_SHIFTS (0)
|
||||
#define INA231_MODE_MASK (7 << INA231_MODE_SHIFTS)
|
||||
#define INA231_MODE_SHUTDOWN (0 << INA231_MODE_SHIFTS)
|
||||
#define INA231_MODE_SHUNT_TRIG (1 << INA231_MODE_SHIFTS)
|
||||
#define INA231_MODE_BUS_TRIG (2 << INA231_MODE_SHIFTS)
|
||||
#define INA231_MODE_SHUNT_BUS_TRIG (3 << INA231_MODE_SHIFTS)
|
||||
#define INA231_MODE_ADC_OFF (4 << INA231_MODE_SHIFTS)
|
||||
#define INA231_MODE_SHUNT_CONT (5 << INA231_MODE_SHIFTS)
|
||||
#define INA231_MODE_BUS_CONT (6 << INA231_MODE_SHIFTS)
|
||||
#define INA231_MODE_SHUNT_BUS_CONT (7 << INA231_MODE_SHIFTS)
|
||||
|
||||
#define INA231_VSHCT_SHIFTS (3)
|
||||
#define INA231_VSHCT_MASK (7 << INA231_VSHCT_SHIFTS)
|
||||
#define INA231_VSHCT_140US (0 << INA231_VSHCT_SHIFTS)
|
||||
#define INA231_VSHCT_204US (1 << INA231_VSHCT_SHIFTS)
|
||||
#define INA231_VSHCT_332US (2 << INA231_VSHCT_SHIFTS)
|
||||
#define INA231_VSHCT_588US (3 << INA231_VSHCT_SHIFTS)
|
||||
#define INA231_VSHCT_1100US (4 << INA231_VSHCT_SHIFTS)
|
||||
#define INA231_VSHCT_2116US (5 << INA231_VSHCT_SHIFTS)
|
||||
#define INA231_VSHCT_4156US (6 << INA231_VSHCT_SHIFTS)
|
||||
#define INA231_VSHCT_8244US (7 << INA231_VSHCT_SHIFTS)
|
||||
|
||||
#define INA231_VBUSCT_SHIFTS (6)
|
||||
#define INA231_VBUSCT_MASK (7 << INA231_VBUSCT_SHIFTS)
|
||||
#define INA231_VBUSCT_140US (0 << INA231_VBUSCT_SHIFTS)
|
||||
#define INA231_VBUSCT_204US (1 << INA231_VBUSCT_SHIFTS)
|
||||
#define INA231_VBUSCT_332US (2 << INA231_VBUSCT_SHIFTS)
|
||||
#define INA231_VBUSCT_588US (3 << INA231_VBUSCT_SHIFTS)
|
||||
#define INA231_VBUSCT_1100US (4 << INA231_VBUSCT_SHIFTS)
|
||||
#define INA231_VBUSCT_2116US (5 << INA231_VBUSCT_SHIFTS)
|
||||
#define INA231_VBUSCT_4156US (6 << INA231_VBUSCT_SHIFTS)
|
||||
#define INA231_VBUSCT_8244US (7 << INA231_VBUSCT_SHIFTS)
|
||||
|
||||
#define INA231_AVERAGES_SHIFTS (9)
|
||||
#define INA231_AVERAGES_MASK (7 << INA231_AVERAGES_SHIFTS)
|
||||
#define INA231_AVERAGES_1 (0 << INA231_AVERAGES_SHIFTS)
|
||||
#define INA231_AVERAGES_4 (1 << INA231_AVERAGES_SHIFTS)
|
||||
#define INA231_AVERAGES_16 (2 << INA231_AVERAGES_SHIFTS)
|
||||
#define INA231_AVERAGES_64 (3 << INA231_AVERAGES_SHIFTS)
|
||||
#define INA231_AVERAGES_128 (4 << INA231_AVERAGES_SHIFTS)
|
||||
#define INA231_AVERAGES_256 (5 << INA231_AVERAGES_SHIFTS)
|
||||
#define INA231_AVERAGES_512 (6 << INA231_AVERAGES_SHIFTS)
|
||||
#define INA231_AVERAGES_1024 (7 << INA231_AVERAGES_SHIFTS)
|
||||
|
||||
#define INA231_CONFIG (INA231_MODE_SHUNT_BUS_CONT | INA231_VSHCT_1100US | INA231_VBUSCT_1100US | INA231_AVERAGES_16)
|
||||
|
||||
#define INA231_RST (1 << 15)
|
||||
|
||||
/* INA231 Enable / Mask Register */
|
||||
|
||||
#define INA231_LEN (1 << 0)
|
||||
#define INA231_APOL (1 << 1)
|
||||
#define INA231_OVF (1 << 2)
|
||||
#define INA231_CVRF (1 << 3)
|
||||
#define INA231_AFF (1 << 4)
|
||||
|
||||
#define INA231_CNVR (1 << 10)
|
||||
#define INA231_POL (1 << 11)
|
||||
#define INA231_BUL (1 << 12)
|
||||
#define INA231_BOL (1 << 13)
|
||||
#define INA231_SUL (1 << 14)
|
||||
#define INA231_SOL (1 << 15)
|
||||
|
||||
#define INA231_SAMPLE_FREQUENCY_HZ 10
|
||||
#define INA231_SAMPLE_INTERVAL_US (1_s / INA231_SAMPLE_FREQUENCY_HZ)
|
||||
#define INA231_CONVERSION_INTERVAL (INA231_SAMPLE_INTERVAL_US - 7)
|
||||
#define MAX_CURRENT 90.0f /* 90 Amps */
|
||||
#define DN_MAX 32768.0f /* 2^15 */
|
||||
#define INA231_CONST 0.00512f /* is an internal fixed value used to ensure scaling is maintained properly */
|
||||
#define INA231_SHUNT 0.0005f /* Shunt is 500 uOhm */
|
||||
#define INA231_VSCALE 0.00125f /* LSB of voltage is 1.25 mV */
|
||||
|
||||
#define swap16(w) __builtin_bswap16((w))
|
||||
|
||||
class INA231 : public device::I2C, public ModuleParams, public I2CSPIDriver<INA231>
|
||||
{
|
||||
public:
|
||||
INA231(const I2CSPIDriverConfig &config, int battery_index);
|
||||
virtual ~INA231();
|
||||
|
||||
static I2CSPIDriverBase *instantiate(const I2CSPIDriverConfig &config, int runtime_instance);
|
||||
static void print_usage();
|
||||
|
||||
void RunImpl();
|
||||
|
||||
int init() override;
|
||||
|
||||
/**
|
||||
* Tries to call the init() function. If it fails, then it will schedule to retry again in
|
||||
* INA231_INIT_RETRY_INTERVAL_US microseconds. It will keep retrying at this interval until initialization succeeds.
|
||||
*
|
||||
* @return PX4_OK if initialization succeeded on the first try. Negative value otherwise.
|
||||
*/
|
||||
int force_init();
|
||||
|
||||
/**
|
||||
* Diagnostics - print some basic information about the driver.
|
||||
*/
|
||||
void print_status() override;
|
||||
|
||||
protected:
|
||||
int probe() override;
|
||||
|
||||
private:
|
||||
bool _sensor_ok{false};
|
||||
unsigned _measure_interval{0};
|
||||
bool _collect_phase{false};
|
||||
bool _initialized{false};
|
||||
|
||||
perf_counter_t _sample_perf;
|
||||
perf_counter_t _comms_errors;
|
||||
perf_counter_t _collection_errors;
|
||||
perf_counter_t _measure_errors;
|
||||
|
||||
int16_t _bus_voltage{0};
|
||||
int16_t _power{0};
|
||||
int16_t _current{0};
|
||||
int16_t _shunt{0};
|
||||
int16_t _cal{0};
|
||||
bool _mode_triggered{false};
|
||||
|
||||
float _max_current{MAX_CURRENT};
|
||||
float _rshunt{INA231_SHUNT};
|
||||
uint16_t _config{INA231_CONFIG};
|
||||
float _current_lsb{_max_current / DN_MAX};
|
||||
float _power_lsb{25.0f * _current_lsb};
|
||||
|
||||
Battery _battery;
|
||||
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
|
||||
|
||||
int read(uint8_t address, int16_t &data);
|
||||
int write(uint8_t address, uint16_t data);
|
||||
|
||||
uint8_t _connected{0};
|
||||
// returns state unchanged
|
||||
bool setConnected(bool state);
|
||||
|
||||
/**
|
||||
* Initialise the automatic measurement state machine and start it.
|
||||
*
|
||||
* @note This function is called at open and error time. It might make sense
|
||||
* to make it more aggressive about resetting the bus in case of errors.
|
||||
*/
|
||||
void start();
|
||||
|
||||
int measure();
|
||||
int collect();
|
||||
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2021-2026 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
#include <px4_platform_common/getopt.h>
|
||||
#include <px4_platform_common/module.h>
|
||||
|
||||
#include "ina231.h"
|
||||
|
||||
I2CSPIDriverBase *INA231::instantiate(const I2CSPIDriverConfig &config, int runtime_instance)
|
||||
{
|
||||
INA231 *instance = new INA231(config, config.custom1);
|
||||
|
||||
if (instance == nullptr) {
|
||||
PX4_ERR("alloc failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (config.keep_running) {
|
||||
if (instance->force_init() != PX4_OK) {
|
||||
PX4_INFO("Failed to init INA231 on bus %d, but will try again periodically.", config.bus);
|
||||
}
|
||||
|
||||
} else if (instance->init() != PX4_OK) {
|
||||
delete instance;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void
|
||||
INA231::print_usage()
|
||||
{
|
||||
PRINT_MODULE_DESCRIPTION(
|
||||
R"DESCR_STR(
|
||||
### Description
|
||||
Driver for the INA231 power monitor.
|
||||
|
||||
Multiple instances of this driver can run simultaneously, if each instance has a separate bus OR I2C address.
|
||||
|
||||
For example, one instance can run on Bus 2, address 0x44, and one can run on Bus 2, address 0x45.
|
||||
|
||||
If the INA231 module is not powered, then by default, initialization of the driver will fail. To change this, use
|
||||
the -f flag. If this flag is set, then if initialization fails, the driver will keep trying to initialize again
|
||||
every 0.5 seconds. With this flag set, you can plug in a battery after the driver starts, and it will work. Without
|
||||
this flag set, the battery must be plugged in before starting the driver.
|
||||
|
||||
)DESCR_STR");
|
||||
|
||||
PRINT_MODULE_USAGE_NAME("ina231", "driver");
|
||||
|
||||
PRINT_MODULE_USAGE_COMMAND("start");
|
||||
PRINT_MODULE_USAGE_PARAMS_I2C_SPI_DRIVER(true, false);
|
||||
PRINT_MODULE_USAGE_PARAMS_I2C_ADDRESS(0x44);
|
||||
PRINT_MODULE_USAGE_PARAMS_I2C_KEEP_RUNNING_FLAG();
|
||||
PRINT_MODULE_USAGE_PARAM_INT('t', 1, 1, 3, "battery index for calibration values (1 or 3)", true);
|
||||
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
ina231_main(int argc, char *argv[])
|
||||
{
|
||||
int ch;
|
||||
using ThisDriver = INA231;
|
||||
BusCLIArguments cli{true, false};
|
||||
cli.i2c_address = INA231_BASEADDR;
|
||||
cli.default_i2c_frequency = 100000;
|
||||
cli.support_keep_running = true;
|
||||
cli.custom1 = 1;
|
||||
|
||||
while ((ch = cli.getOpt(argc, argv, "t:")) != EOF) {
|
||||
switch (ch) {
|
||||
case 't': // battery index
|
||||
cli.custom1 = (int)strtol(cli.optArg(), NULL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const char *verb = cli.optArg();
|
||||
if (!verb) {
|
||||
ThisDriver::print_usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
BusInstanceIterator iterator(MODULE_NAME, cli, DRV_POWER_DEVTYPE_INA231);
|
||||
|
||||
if (!strcmp(verb, "start")) {
|
||||
return ThisDriver::module_start(cli, iterator);
|
||||
}
|
||||
|
||||
if (!strcmp(verb, "stop")) {
|
||||
return ThisDriver::module_stop(iterator);
|
||||
}
|
||||
|
||||
if (!strcmp(verb, "status")) {
|
||||
return ThisDriver::module_status(iterator);
|
||||
}
|
||||
|
||||
ThisDriver::print_usage();
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
module_name: ina231
|
||||
parameters:
|
||||
- group: Sensors
|
||||
definitions:
|
||||
SENS_EN_INA231:
|
||||
description:
|
||||
short: Enable INA231 Power Monitor
|
||||
long: For systems with an INA231 Power Monitor, this should be set to true
|
||||
type: boolean
|
||||
default: 0
|
||||
reboot_required: true
|
||||
INA231_CONFIG:
|
||||
description:
|
||||
short: INA231 Power Monitor Config
|
||||
type: int32
|
||||
default: 1319
|
||||
min: 0
|
||||
max: 65535
|
||||
decimal: 1
|
||||
increment: 1
|
||||
reboot_required: true
|
||||
INA231_CURRENT:
|
||||
description:
|
||||
short: INA231 Power Monitor Max Current
|
||||
type: float
|
||||
default: 90.0
|
||||
min: 0.1
|
||||
max: 200.0
|
||||
decimal: 2
|
||||
increment: 0.1
|
||||
reboot_required: true
|
||||
INA231_SHUNT:
|
||||
description:
|
||||
short: INA231 Power Monitor Shunt
|
||||
type: float
|
||||
default: 0.00063
|
||||
min: 1.0e-09
|
||||
max: 0.1
|
||||
decimal: 10
|
||||
increment: 1.0e-09
|
||||
reboot_required: true
|
||||
Reference in New Issue
Block a user