Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

INA3211 Support and I2C Ammeter rework #27123

Draft
wants to merge 2 commits into
base: bugfix-2.1.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -3704,10 +3704,22 @@
//
// Laser I2C Ammeter (High precision INA226 low/high side module)
//
//#define I2C_AMMETER
#define I2C_AMMETER
#if ENABLED(I2C_AMMETER)
#define I2C_AMMETER_IMAX 0.1 // (Amps) Calibration value for the expected current range
#define I2C_AMMETER_SHUNT_RESISTOR 0.1 // (Ohms) Calibration shunt resistor value

#define I2C_AMMETER_SDA_PIN 66 // D66 (AUX2.9)
#define I2C_AMMETER_SCL_PIN 65 // D65 (AUX2.10)

#define I2C_AMMETER_ADDRESS 0x41

#define I2C_AMMETER_MONITOR_CHANNEL 3
#define I2C_AMMETER_WARN_CURRENT_MA 14 //Hardware Warn Level (INA3221 Only)
#define I2C_AMMETER_ALARM_CURRENT_MA 25 //Hardware Alarm Level

#define I2C_AMMETER_IMAX 0.5 // (Amps) Calibration value for the expected current range
#define I2C_AMMETER_SHUNT_RESISTOR 0.1 // (Ohms) Calibration shunt resistor value

//#define I2C_AMMETER_DEBUGGING
#endif

//
Expand Down
122 changes: 107 additions & 15 deletions Marlin/src/feature/ammeter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,124 @@
#include "../inc/MarlinConfig.h"

#if ENABLED(I2C_AMMETER)

#include "ammeter.h"

#ifndef I2C_AMMETER_SDA_PIN
#define I2C_AMMETER_SDA_PIN SDA
#endif
#ifndef I2C_AMMETER_SCL_PIN
#define I2C_AMMETER_SCL_PIN SCL
#endif
#ifndef I2C_AMMETER_ADDRESS
#define I2C_AMMETER_ADDRESS 0x40
#endif
#ifndef I2C_AMMETER_IMAX
#define I2C_AMMETER_IMAX 0.500 // Calibration range 500 Milliamps
#define I2C_AMMETER_IMAX 0.500 // Calibration range 500 Milliamps
#endif

INA226 ina;
#ifndef I2C_AMMETER_MONITOR_CHANNEL
#define I2C_AMMETER_MONITOR_CHANNEL 3
#endif

Ammeter ammeter;
#define I2C_AMMETER_WARN_CURRENT_MA 14
#define I2C_AMMETER_ALARM_CURRENT_MA 25

float Ammeter::scale;
Ammeter ammeter;
float Ammeter::current;
uint32_t Ammeter::currentLSB;
int Ammeter::sensor_type = 0;

SlowSoftI2CMaster si = SlowSoftI2CMaster(I2C_AMMETER_SDA_PIN, I2C_AMMETER_SCL_PIN, true);
uint16_t _ammeter_read_register(uint8_t reg)
{
if (!si.i2c_start((I2C_AMMETER_ADDRESS << 1) | I2C_WRITE))
return -1; // No ACK from slave

if (!si.i2c_write(reg))
return -2; // failed write

delayMicroseconds(600); // max 586us from datasheet

if (!si.i2c_rep_start((I2C_AMMETER_ADDRESS << 1) | I2C_READ))
return -3; // no ACK from slave on second transaction

void Ammeter::init() {
ina.begin();
ina.configure(INA226_AVERAGES_16, INA226_BUS_CONV_TIME_1100US, INA226_SHUNT_CONV_TIME_1100US, INA226_MODE_SHUNT_BUS_CONT);
ina.calibrate(I2C_AMMETER_SHUNT_RESISTOR, I2C_AMMETER_IMAX);
int ret = (si.i2c_read(false) << 8 | si.i2c_read(true));

si.i2c_stop();
return ret;
}

void _ammeter_write_register(uint8_t reg, uint16_t value)
{
if (!si.i2c_start((I2C_AMMETER_ADDRESS << 1) | I2C_WRITE))
return; // No ACK from slave

si.i2c_write(reg);
si.i2c_write((value >> 8) & 0xFF);
si.i2c_write(value & 0xFF);
si.i2c_stop();
}
void Ammeter::init()
{
if (si.i2c_init())
{
if (_ammeter_read_register(0xFE) == 0x5449)
{

uint16_t id = _ammeter_read_register(0xFF);
sensor_type = id;

#ifdef I2C_AMMETER_DEBUGGING
SERIAL_ECHO("TI I2C Device: 0x");
SERIAL_PRINT(id, PrintBase::Hex);
SERIAL_ECHO(" @ 0x");
SERIAL_PRINT(I2C_AMMETER_ADDRESS, PrintBase::Hex);
SERIAL_ECHOLNPGM(" OK ( SDA: ", I2C_AMMETER_SDA_PIN, " SCL: ", I2C_AMMETER_SCL_PIN, " )");
#endif

switch (sensor_type)
{
case 0x3220:
_ammeter_write_register(0x00, 0x927 | (0x8000 >> I2C_AMMETER_MONITOR_CHANNEL)); // [CH_x_EN, AVERAGES_256, BUS_CONV_TIME_1100US, SHUNT_CONV_TIME_1100US, MODE_SHUNT_BUS_CONT]
TERN_(I2C_AMMETER_WARN_CURRENT_MA, _ammeter_write_register(0x6 + (I2C_AMMETER_MONITOR_CHANNEL * 2), I2C_AMMETER_WARN_CURRENT_MA / I2C_AMMETER_IMAX);)
TERN_(I2C_AMMETER_ALARM_CURRENT_MA, _ammeter_write_register(0x5 + (I2C_AMMETER_MONITOR_CHANNEL * 2), I2C_AMMETER_ALARM_CURRENT_MA / I2C_AMMETER_IMAX);)
break;

case 0x2260:
{
currentLSB = ((I2C_AMMETER_IMAX / 32767) * 100000000);
currentLSB /= 100000000;
currentLSB /= 0.0001; // CHOP CHOP?
currentLSB = ceil(currentLSB);
currentLSB *= 0.0001;

_ammeter_write_register(0x00, 0x927); // [AVERAGES_256, BUS_CONV_TIME_1100US, SHUNT_CONV_TIME_1100US, MODE_SHUNT_BUS_CONT]
_ammeter_write_register(0x05, (uint16_t)((0.00512) / (currentLSB * I2C_AMMETER_SHUNT_RESISTOR)));
break;
}
}
}


}
}

float Ammeter::read()
{
switch (sensor_type)
{
case 0x3220: // INA3221
current = _ammeter_read_register((I2C_AMMETER_MONITOR_CHANNEL * 2) - 1) * (I2C_AMMETER_IMAX * I2C_AMMETER_SHUNT_RESISTOR);
break;

case 0x2260: // INA226
current = _ammeter_read_register(0x04) * currentLSB;
break;

float Ammeter::read() {
scale = 1;
current = ina.readShuntCurrent();
if (current <= 0.0001f) current = 0; // Clean up least-significant-bit amplification errors
if (current < 0.1f) scale = 1000;
return current * scale;
default:
break;
}
return current;
}

#endif // I2C_AMMETER
7 changes: 3 additions & 4 deletions Marlin/src/feature/ammeter.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,13 @@
#pragma once

#include "../inc/MarlinConfigPre.h"
#include <SlowSoftI2CMaster.h>

#include <Wire.h>
#include <INA226.h>

class Ammeter {
private:
static float scale;

static int sensor_type;
static uint32_t currentLSB;
public:
static float current;
static void init();
Expand Down
6 changes: 3 additions & 3 deletions Marlin/src/lcd/HD44780/marlinui_HD44780.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -683,12 +683,12 @@ FORCE_INLINE void _draw_heater_status(const heater_id_t heater_id, const char pr
FORCE_INLINE void _draw_ammeter_status() {
lcd_put_u8str(F(" "));
ammeter.read();
if (ammeter.current <= 0.999f) {
lcd_put_u8str(ui16tostr3rj(uint16_t(ammeter.current * 1000 + 0.5f)));
if (ammeter.current < 1000) {
lcd_put_u8str(ftostr31rj((ammeter.current)));
lcd_put_u8str(F("mA"));
}
else {
lcd_put_u8str(ftostr12ns(ammeter.current));
lcd_put_u8str(ftostr12ns(ammeter.current / 1000));
lcd_put_u8str(F("A"));
}
}
Expand Down
6 changes: 3 additions & 3 deletions Marlin/src/lcd/TFTGLCD/marlinui_TFTGLCD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -557,16 +557,16 @@ FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const
lcd_moveto(10, 5); lcd_put_u8str(F("ILAZ"));
ammeter.read();
lcd_moveto(11, 6);
if (ammeter.current <= 0.999f)
if (ammeter.current < 1.0f)
{
lcd_put_u8str("mA");
lcd_moveto(10, 7);
lcd_put_u8str(F(" ")); lcd_put_u8str(ui16tostr3rj(uint16_t(ammeter.current * 1000 + 0.5f)));
lcd_put_u8str(F(" ")); lcd_put_u8str(ui16tostr3rj(uint16_t(ammeter.current ));
}
else {
lcd_put_u8str(" A");
lcd_moveto(10, 7);
lcd_put_u8str(ftostr12ns(ammeter.current));
lcd_put_u8str(ftostr12ns(ammeter.current*1000));
}

if (ammeter.current) picBits |= ICON_BED;
Expand Down
2 changes: 1 addition & 1 deletion ini/features.ini
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ HAS_MOTOR_CURRENT_I2C = SlowSoftI2CMaster
LIB_INTERNAL_MAX31865 = build_src_filter=+<src/libs/MAX31865.cpp>
NEOPIXEL_LED = adafruit/Adafruit NeoPixel@~1.8.0
build_src_filter=+<src/feature/leds/neopixel.cpp>
I2C_AMMETER = peterus/INA226Lib@1.1.2
I2C_AMMETER = SlowSoftI2CMaster
build_src_filter=+<src/feature/ammeter.cpp>
USES_LIQUIDCRYSTAL = LiquidCrystal=https://github.com/MarlinFirmware/New-LiquidCrystal/archive/1.5.1.zip
USES_LIQUIDCRYSTAL_I2C = marcoschwartz/LiquidCrystal_I2C@1.1.4
Expand Down