diff --git a/variants/arduino_nesso_n1/expander.cpp b/variants/arduino_nesso_n1/expander.cpp index d905cad44ca..e8a6cdbfa25 100644 --- a/variants/arduino_nesso_n1/expander.cpp +++ b/variants/arduino_nesso_n1/expander.cpp @@ -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); @@ -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(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); diff --git a/variants/arduino_nesso_n1/pins_arduino.h b/variants/arduino_nesso_n1/pins_arduino.h index fc1414b361d..30ccb527d33 100644 --- a/variants/arduino_nesso_n1/pins_arduino.h +++ b/variants/arduino_nesso_n1/pins_arduino.h @@ -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;