Skip to content
Open
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
236 changes: 227 additions & 9 deletions variants/arduino_nesso_n1/expander.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@

static bool wireInitialized = false;

// From https://www.diodes.com/datasheet/download/PI4IOE5V6408.pdf
// IO expander datasheet from https://www.diodes.com/datasheet/download/PI4IOE5V6408.pdf
// Battery charger datasheet from https://www.awinic.com/en/productDetail/AW32001ACSR
// battery gauge datasheet from https://www.ti.com/product/BQ27220

static void writeRegister(uint8_t address, uint8_t reg, uint8_t value) {
WireInternal.beginTransmission(address);
WireInternal.write(reg);
Expand Down Expand Up @@ -85,37 +88,252 @@ int digitalRead(ExpanderPin pin) {
return readBitRegister(pin.address, 0xF, pin.pin);
}

void NessoBattery::begin(uint16_t current, uint16_t voltage, UnderVoltageLockout uvlo, uint16_t dpm_voltage, uint8_t timeout) {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}

setChargeCurrent(current);
setChargeVoltage(voltage);
setWatchdogTimer(timeout);
setBatUVLO(uvlo);
setVinDPMVoltage(dpm_voltage);
setHiZ(false);
setChargeEnable(true);
}

void NessoBattery::enableCharge() {
// AW32001E - address 0x49
// set CEB bit low (charge enable)
setChargeEnable(true);
}

void NessoBattery::setChargeEnable(bool enable) {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
// bit 3 set charge enable
writeBitRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG, 3, !enable);
}

void NessoBattery::setVinDPMVoltage(uint16_t voltage) {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
if (voltage < 3880) {
voltage = 3880;
}
if (voltage > 5080) {
voltage = 5080;
}
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_INPUT_SRC);
// bits 7-4 set Vin DPM voltage
reg_value &= ~0b01111000;
reg_value |= ((voltage - 3880) / 80) << 4;
writeRegister(AW32001_I2C_ADDR, AW3200_INPUT_SRC, reg_value);
}

void NessoBattery::setIinLimitCurrent(uint16_t current) {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
if (current < 50) {
current = 50;
}
if (current > 500) {
current = 500;
}
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_INPUT_SRC);
// bits 3-0 set Iin limit current
reg_value &= ~0b00001111;
reg_value |= ((current - 50) / 30) & 0b00001111;
writeRegister(AW32001_I2C_ADDR, AW3200_INPUT_SRC, reg_value);
}

void NessoBattery::setBatUVLO(UnderVoltageLockout uvlo) {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG);
// bits 2-0 set UVLO
reg_value &= ~0b00000111;
reg_value |= (uvlo & 0b00000111);
writeRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG, reg_value);
}

void NessoBattery::setChargeCurrent(uint16_t current) {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
if (current < 8) {
current = 8;
}
if (current > 456) {
current = 456;
}
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_CHG_CURRENT);
// bits 5-0 set charge current
reg_value &= ~0b00111111;
reg_value |= ((current - 8) / 8) & 0b00111111;
writeRegister(AW32001_I2C_ADDR, AW3200_CHG_CURRENT, reg_value);
}

void NessoBattery::setDischargeCurrent(uint16_t current) {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
if (current < 200) {
current = 200;
}
if (current > 3200) {
current = 3200;
}
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_TERM_CURRENT);
// bits 7-4 set discharge current
reg_value &= ~0b11110000;
reg_value |= (((current - 200) / 200) & 0b00001111) << 4;
writeRegister(AW32001_I2C_ADDR, AW3200_TERM_CURRENT, reg_value);
}

void NessoBattery::setChargeVoltage(uint16_t voltage) {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
if (voltage < 3600) {
voltage = 3600;
}
if (voltage > 4545) {
voltage = 4545;
}
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_CHG_VOLTAGE);
// bits 7-2 set charge voltage
reg_value &= ~0b11111100;
reg_value |= ((voltage - 3600) / 15) << 2;
writeRegister(AW32001_I2C_ADDR, AW3200_CHG_VOLTAGE, reg_value);
}

void NessoBattery::setWatchdogTimer(uint8_t sec) {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}

uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_TIMER_WD);
uint8_t bits = 0;
switch (sec) {
case 0:
bits = 0b00; // disable watchdog
break;
case 40: bits = 0b01; break;
case 80: bits = 0b10; break;
case 160: bits = 0b11; break;
default: bits = 0b11; break;
}
// bits 6-5 set watchdog timer
reg_value &= ~(0b11 << 5);
reg_value |= (bits << 5);
writeRegister(AW32001_I2C_ADDR, AW3200_TIMER_WD, reg_value);
}

void NessoBattery::feedWatchdog() {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
// bit 6 set feed watchdog
writeBitRegister(AW32001_I2C_ADDR, AW3200_CHG_CURRENT, 6, true);
}

void NessoBattery::setShipMode(bool en) {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
// bit 5 set ship mode
writeBitRegister(AW32001_I2C_ADDR, AW3200_MAIN_CTRL, 5, en);
}

NessoBattery::ChargeStatus NessoBattery::getChargeStatus() {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
uint8_t status = readRegister(AW32001_I2C_ADDR, AW3200_SYS_STATUS);
// bits 4-3 set charge status
uint8_t charge_status = (status >> 3) & 0b11;
return static_cast<ChargeStatus>(charge_status);
}

void NessoBattery::setHiZ(bool enable) {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
writeBitRegister(0x49, 0x1, 3, false);
// bit 4 set Hi-Z mode
writeBitRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG, 4, enable);
}

float NessoBattery::getVoltage() {
// BQ27220 - address 0x55
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
uint16_t voltage = (readRegister(0x55, 0x9) << 8) | readRegister(0x55, 0x8);
uint16_t voltage = (readRegister(BQ27220_I2C_ADDR, BQ27220_VOLTAGE + 1) << 8) | readRegister(BQ27220_I2C_ADDR, BQ27220_VOLTAGE);
return (float)voltage / 1000.0f;
}

float NessoBattery::getCurrent() {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
int16_t current = (readRegister(BQ27220_I2C_ADDR, BQ27220_CURRENT + 1) << 8) | readRegister(BQ27220_I2C_ADDR, BQ27220_CURRENT);
return (float)current / 1000.0f;
}

uint16_t NessoBattery::getChargeLevel() {
// BQ27220 - address 0x55
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
uint16_t current_capacity = readRegister(0x55, 0x11) << 8 | readRegister(0x55, 0x10);
uint16_t total_capacity = readRegister(0x55, 0x13) << 8 | readRegister(0x55, 0x12);
uint16_t current_capacity = readRegister(BQ27220_I2C_ADDR, BQ27220_REMAIN_CAPACITY + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_REMAIN_CAPACITY);
uint16_t total_capacity = readRegister(BQ27220_I2C_ADDR, BQ27220_FULL_CAPACITY + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_FULL_CAPACITY);
return (current_capacity * 100) / total_capacity;
}

int16_t NessoBattery::getAvgPower() {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
int16_t avg_power = readRegister(BQ27220_I2C_ADDR, BQ27220_AVG_POWER + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_AVG_POWER);
return avg_power;
}

float NessoBattery::getTemperature() {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
uint16_t temp = readRegister(BQ27220_I2C_ADDR, BQ27220_TEMPERATURE + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_TEMPERATURE);
return ((float)temp / 10.0f) - 273.15f;
}

uint16_t NessoBattery::getCycleCount() {
if (!wireInitialized) {
WireInternal.begin(SDA, SCL);
wireInitialized = true;
}
uint16_t cycle_count = readRegister(BQ27220_I2C_ADDR, BQ27220_CYCLE_COUNT + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_CYCLE_COUNT);
return cycle_count;
}

ExpanderPin LORA_LNA_ENABLE(5);
ExpanderPin LORA_ANTENNA_SWITCH(6);
ExpanderPin LORA_ENABLE(7);
Expand Down
70 changes: 69 additions & 1 deletion variants/arduino_nesso_n1/pins_arduino.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,78 @@ class ExpanderPin {

class NessoBattery {
public:
static constexpr uint8_t AW32001_I2C_ADDR = 0x49;
static constexpr uint8_t BQ27220_I2C_ADDR = 0x55;

enum AW32001Reg : uint8_t {
AW3200_INPUT_SRC = 0x00,
AW3200_POWER_ON_CFG = 0x01,
AW3200_CHG_CURRENT = 0x02,
AW3200_TERM_CURRENT = 0x03,
AW3200_CHG_VOLTAGE = 0x04,
AW3200_TIMER_WD = 0x05,
AW3200_MAIN_CTRL = 0x06,
AW3200_SYS_CTRL = 0x07,
AW3200_SYS_STATUS = 0x08,
AW3200_FAULT_STATUS = 0x09,
AW3200_CHIP_ID = 0x0A,
};

enum BQ27220Reg : uint8_t {
BQ27220_VOLTAGE = 0x08,
BQ27220_CURRENT = 0x0C,
BQ27220_REMAIN_CAPACITY = 0x10,
BQ27220_FULL_CAPACITY = 0x12,
BQ27220_AVG_POWER = 0x24,
BQ27220_TEMPERATURE = 0x28,
BQ27220_CYCLE_COUNT = 0x2A,
};

enum ChargeStatus {
NOT_CHARGING = 0,
PRE_CHARGE = 1,
CHARGING = 2,
FULL_CHARGE = 3,
};

enum UnderVoltageLockout {
UVLO_2430mV = 0,
UVLO_2490mV = 1,
UVLO_2580mV = 2,
UVLO_2670mV = 3,
UVLO_2760mV = 4,
UVLO_2850mV = 5,
UVLO_2940mV = 6,
UVLO_3030mV = 7,
};

NessoBattery(){};
void enableCharge(); // enable charging
void begin(
uint16_t current = 256, uint16_t voltage = 4200, UnderVoltageLockout uvlo = UVLO_2580mV, uint16_t dpm_voltage = 4520, uint8_t timeout = 0
); // default: charge current 256mA, battery 4200mV, uvlo 2580mV, DMP 4520mV, disable watchdog

// AW32001 functions
void enableCharge(); // enable charging
void setChargeEnable(bool enable); // charge control
void setVinDPMVoltage(uint16_t voltage); // set input voltage limit, 3880mV ~ 5080mV(step 80mV, default 4520mV)
void setIinLimitCurrent(uint16_t current); // set input current limit, 50mA ~ 500mA(step 30mA, default 500mA)
void setBatUVLO(UnderVoltageLockout uvlo); // set battery under voltage lockout(2430mV, 2490mV, 2580mV, 2670mV, 2760mV, 2850mV, 2940mV, 3030mV)
void setChargeCurrent(uint16_t current); // set charging current, 8mA ~ 456mA(step 8mA, default 128mA)
void setDischargeCurrent(uint16_t current); // set discharging current, 200mA ~ 3200mA(step 200mA, default 2000mA)
void setChargeVoltage(uint16_t voltage); // set charging voltage, 3600mV ~ 4545mV(step 15mV, default 4200mV)
void setWatchdogTimer(uint8_t sec); // set charge watchdog timeout(0s, 40s, 80s, 160s, default 160s, 0 to disable)
void feedWatchdog(); // feed watchdog timer
void setShipMode(bool en); // set ship mode
ChargeStatus getChargeStatus(); // get charge status
void setHiZ(bool enable); // set Hi-Z mode, true: USB -x-> SYS, false: USB -> SYS

// BQ27220 functions
float getVoltage(); // get battery voltage in Volts
float getCurrent(); // get battery current in Amperes
uint16_t getChargeLevel(); // get battery charge level in percents
int16_t getAvgPower(); // get average power in mWatts, can be negative
float getTemperature(); // get battery temperature in Celsius
uint16_t getCycleCount(); // get battery cycle count
};

extern ExpanderPin LORA_LNA_ENABLE;
Expand Down