Skip to content

Commit e28393c

Browse files
committed
feat(nesso-n1): add comprehensive battery management API
Signed-off-by: imliubo <imliubo@makingfun.xyz> Implement full NessoBattery class with: - AW32001 charger control (charge current/voltage, UVLO, watchdog, Hi-Z mode) - BQ27220 fuel gauge telemetry (voltage, current, power, temperature, cycle count) - Register enums and named constants replacing magic numbers - Datasheet references for all ICs
1 parent d39181b commit e28393c

File tree

2 files changed

+257
-10
lines changed

2 files changed

+257
-10
lines changed

variants/arduino_nesso_n1/expander.cpp

Lines changed: 190 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88

99
static bool wireInitialized = false;
1010

11-
// From https://www.diodes.com/datasheet/download/PI4IOE5V6408.pdf
11+
// IO expander datasheet from https://www.diodes.com/datasheet/download/PI4IOE5V6408.pdf
12+
// Battery charger datasheet from https://www.awinic.com/en/productDetail/AW32001ACSR
13+
// battery gauge datasheet from https://www.ti.com/product/BQ27220
14+
1215
static void writeRegister(uint8_t address, uint8_t reg, uint8_t value) {
1316
WireInternal.beginTransmission(address);
1417
WireInternal.write(reg);
@@ -85,37 +88,215 @@ int digitalRead(ExpanderPin pin) {
8588
return readBitRegister(pin.address, 0xF, pin.pin);
8689
}
8790

91+
void NessoBattery::begin(uint16_t current, uint16_t voltage, UnderVoltageLockout uvlo, uint8_t timeout) {
92+
if (!wireInitialized) {
93+
WireInternal.begin(SDA, SCL);
94+
wireInitialized = true;
95+
}
96+
97+
setChargeCurrent(current);
98+
setChargeVoltage(voltage);
99+
setWatchdogTimer(timeout);
100+
setBatUVLO(uvlo);
101+
setHiZ(false);
102+
setChargeEnable(true);
103+
}
104+
88105
void NessoBattery::enableCharge() {
89-
// AW32001E - address 0x49
90-
// set CEB bit low (charge enable)
106+
setChargeEnable(true);
107+
}
108+
109+
void NessoBattery::setChargeEnable(bool enable) {
110+
if (!wireInitialized) {
111+
WireInternal.begin(SDA, SCL);
112+
wireInitialized = true;
113+
}
114+
// bit 3 set charge enable
115+
writeBitRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG, 3, !enable);
116+
}
117+
118+
void NessoBattery::setBatUVLO(UnderVoltageLockout uvlo) {
119+
if (!wireInitialized) {
120+
WireInternal.begin(SDA, SCL);
121+
wireInitialized = true;
122+
}
123+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG);
124+
// bits 2-0 set UVLO
125+
reg_value &= ~0b00000111;
126+
reg_value |= (uvlo & 0b00000111);
127+
writeRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG, reg_value);
128+
}
129+
130+
void NessoBattery::setChargeCurrent(uint16_t current) {
131+
if (!wireInitialized) {
132+
WireInternal.begin(SDA, SCL);
133+
wireInitialized = true;
134+
}
135+
if (current < 8) {
136+
current = 8;
137+
}
138+
if (current > 456) {
139+
current = 456;
140+
}
141+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_CHG_CURRENT);
142+
// bits 5-0 set charge current
143+
reg_value &= ~0b00111111;
144+
reg_value |= ((current - 8) / 8) & 0b00111111;
145+
writeRegister(AW32001_I2C_ADDR, AW3200_CHG_CURRENT, reg_value);
146+
}
147+
148+
void NessoBattery::setDischargeCurrent(uint16_t current) {
149+
if (!wireInitialized) {
150+
WireInternal.begin(SDA, SCL);
151+
wireInitialized = true;
152+
}
153+
if (current < 200) {
154+
current = 200;
155+
}
156+
if (current > 3200) {
157+
current = 3200;
158+
}
159+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_TERM_CURRENT);
160+
// bits 7-4 set discharge current
161+
reg_value &= ~0b11110000;
162+
reg_value |= (((current - 200) / 200) & 0b00001111) << 4;
163+
writeRegister(AW32001_I2C_ADDR, AW3200_TERM_CURRENT, reg_value);
164+
}
165+
166+
void NessoBattery::setChargeVoltage(uint16_t voltage) {
167+
if (!wireInitialized) {
168+
WireInternal.begin(SDA, SCL);
169+
wireInitialized = true;
170+
}
171+
if (voltage < 3600) {
172+
voltage = 3600;
173+
}
174+
if (voltage > 4545) {
175+
voltage = 4545;
176+
}
177+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_CHG_VOLTAGE);
178+
// bits 7-2 set charge voltage
179+
reg_value &= ~0b11111100;
180+
reg_value |= ((voltage - 3600) / 15) << 2;
181+
writeRegister(AW32001_I2C_ADDR, AW3200_CHG_VOLTAGE, reg_value);
182+
}
183+
184+
void NessoBattery::setWatchdogTimer(uint8_t sec) {
185+
if (!wireInitialized) {
186+
WireInternal.begin(SDA, SCL);
187+
wireInitialized = true;
188+
}
189+
190+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_TIMER_WD);
191+
uint8_t bits = 0;
192+
switch (sec) {
193+
case 0:
194+
bits = 0b00; // disable watchdog
195+
break;
196+
case 40: bits = 0b01; break;
197+
case 80: bits = 0b10; break;
198+
case 160: bits = 0b11; break;
199+
default: bits = 0b11; break;
200+
}
201+
// bits 6-5 set watchdog timer
202+
reg_value &= ~(0b11 << 5);
203+
reg_value |= (bits << 5);
204+
writeRegister(AW32001_I2C_ADDR, AW3200_TIMER_WD, reg_value);
205+
}
206+
207+
void NessoBattery::feedWatchdog() {
208+
if (!wireInitialized) {
209+
WireInternal.begin(SDA, SCL);
210+
wireInitialized = true;
211+
}
212+
// bit 6 set feed watchdog
213+
writeBitRegister(AW32001_I2C_ADDR, AW3200_CHG_CURRENT, 6, true);
214+
}
215+
216+
void NessoBattery::setShipMode(bool en) {
217+
if (!wireInitialized) {
218+
WireInternal.begin(SDA, SCL);
219+
wireInitialized = true;
220+
}
221+
// bit 5 set ship mode
222+
writeBitRegister(AW32001_I2C_ADDR, AW3200_MAIN_CTRL, 5, en);
223+
}
224+
225+
NessoBattery::ChargeStatus NessoBattery::getChargeStatus() {
226+
if (!wireInitialized) {
227+
WireInternal.begin(SDA, SCL);
228+
wireInitialized = true;
229+
}
230+
uint8_t status = readRegister(AW32001_I2C_ADDR, AW3200_SYS_STATUS);
231+
// bits 4-3 set charge status
232+
uint8_t charge_status = (status >> 3) & 0b11;
233+
return static_cast<ChargeStatus>(charge_status);
234+
}
235+
236+
void NessoBattery::setHiZ(bool enable) {
91237
if (!wireInitialized) {
92238
WireInternal.begin(SDA, SCL);
93239
wireInitialized = true;
94240
}
95-
writeBitRegister(0x49, 0x1, 3, false);
241+
// bit 4 set Hi-Z mode
242+
writeBitRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG, 4, enable);
96243
}
97244

98245
float NessoBattery::getVoltage() {
99-
// BQ27220 - address 0x55
100246
if (!wireInitialized) {
101247
WireInternal.begin(SDA, SCL);
102248
wireInitialized = true;
103249
}
104-
uint16_t voltage = (readRegister(0x55, 0x9) << 8) | readRegister(0x55, 0x8);
250+
uint16_t voltage = (readRegister(BQ27220_I2C_ADDR, BQ27220_VOLTAGE + 1) << 8) | readRegister(BQ27220_I2C_ADDR, BQ27220_VOLTAGE);
105251
return (float)voltage / 1000.0f;
106252
}
107253

254+
float NessoBattery::getCurrent() {
255+
if (!wireInitialized) {
256+
WireInternal.begin(SDA, SCL);
257+
wireInitialized = true;
258+
}
259+
int16_t current = (readRegister(BQ27220_I2C_ADDR, BQ27220_CURRENT + 1) << 8) | readRegister(BQ27220_I2C_ADDR, BQ27220_CURRENT);
260+
return (float)current / 1000.0f;
261+
}
262+
108263
uint16_t NessoBattery::getChargeLevel() {
109-
// BQ27220 - address 0x55
110264
if (!wireInitialized) {
111265
WireInternal.begin(SDA, SCL);
112266
wireInitialized = true;
113267
}
114-
uint16_t current_capacity = readRegister(0x55, 0x11) << 8 | readRegister(0x55, 0x10);
115-
uint16_t total_capacity = readRegister(0x55, 0x13) << 8 | readRegister(0x55, 0x12);
268+
uint16_t current_capacity = readRegister(BQ27220_I2C_ADDR, BQ27220_REMAIN_CAPACITY + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_REMAIN_CAPACITY);
269+
uint16_t total_capacity = readRegister(BQ27220_I2C_ADDR, BQ27220_FULL_CAPACITY + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_FULL_CAPACITY);
116270
return (current_capacity * 100) / total_capacity;
117271
}
118272

273+
uint16_t NessoBattery::getAvgPower() {
274+
if (!wireInitialized) {
275+
WireInternal.begin(SDA, SCL);
276+
wireInitialized = true;
277+
}
278+
uint16_t avg_power = readRegister(BQ27220_I2C_ADDR, BQ27220_AVG_POWER + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_AVG_POWER);
279+
return avg_power;
280+
}
281+
282+
float NessoBattery::getTemperature() {
283+
if (!wireInitialized) {
284+
WireInternal.begin(SDA, SCL);
285+
wireInitialized = true;
286+
}
287+
uint16_t temp = readRegister(BQ27220_I2C_ADDR, BQ27220_TEMPERATURE + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_TEMPERATURE);
288+
return ((float)temp / 10.0f) - 273.15f;
289+
}
290+
291+
uint16_t NessoBattery::getCycleCount() {
292+
if (!wireInitialized) {
293+
WireInternal.begin(SDA, SCL);
294+
wireInitialized = true;
295+
}
296+
uint16_t cycle_count = readRegister(BQ27220_I2C_ADDR, BQ27220_CYCLE_COUNT + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_CYCLE_COUNT);
297+
return cycle_count;
298+
}
299+
119300
ExpanderPin LORA_LNA_ENABLE(5);
120301
ExpanderPin LORA_ANTENNA_SWITCH(6);
121302
ExpanderPin LORA_ENABLE(7);

variants/arduino_nesso_n1/pins_arduino.h

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,76 @@ class ExpanderPin {
5151

5252
class NessoBattery {
5353
public:
54+
static constexpr uint8_t AW32001_I2C_ADDR = 0x49;
55+
static constexpr uint8_t BQ27220_I2C_ADDR = 0x55;
56+
57+
enum AW32001Reg : uint8_t {
58+
AW3200_INPUT_SRC = 0x00,
59+
AW3200_POWER_ON_CFG = 0x01,
60+
AW3200_CHG_CURRENT = 0x02,
61+
AW3200_TERM_CURRENT = 0x03,
62+
AW3200_CHG_VOLTAGE = 0x04,
63+
AW3200_TIMER_WD = 0x05,
64+
AW3200_MAIN_CTRL = 0x06,
65+
AW3200_SYS_CTRL = 0x07,
66+
AW3200_SYS_STATUS = 0x08,
67+
AW3200_FAULT_STATUS = 0x09,
68+
AW3200_CHIP_ID = 0x0A,
69+
};
70+
71+
enum BQ27220Reg : uint8_t {
72+
BQ27220_VOLTAGE = 0x08,
73+
BQ27220_CURRENT = 0x0C,
74+
BQ27220_REMAIN_CAPACITY = 0x10,
75+
BQ27220_FULL_CAPACITY = 0x12,
76+
BQ27220_AVG_POWER = 0x24,
77+
BQ27220_TEMPERATURE = 0x28,
78+
BQ27220_CYCLE_COUNT = 0x2A,
79+
};
80+
81+
enum ChargeStatus {
82+
NOT_CHARGING = 0,
83+
PRE_CHARGE = 1,
84+
CHARGING = 2,
85+
FULL_CHARGE = 3,
86+
};
87+
88+
enum UnderVoltageLockout {
89+
UVLO_2430mV = 0,
90+
UVLO_2490mV = 1,
91+
UVLO_2580mV = 2,
92+
UVLO_2670mV = 3,
93+
UVLO_2760mV = 4,
94+
UVLO_2850mV = 5,
95+
UVLO_2940mV = 6,
96+
UVLO_3030mV = 7,
97+
};
98+
5499
NessoBattery(){};
55-
void enableCharge(); // enable charging
100+
void begin(
101+
uint16_t current = 256, uint16_t voltage = 4200, UnderVoltageLockout uvlo = UVLO_2760mV, uint8_t timeout = 0
102+
); // default: 256, 4200mV, 2760mV, disable watchdog
103+
104+
// AW32001 functions
105+
void enableCharge(); // enable charging
106+
void setChargeEnable(bool enable); // charge control
107+
void setBatUVLO(UnderVoltageLockout uvlo); // set battery under voltage lockout(2430mV, 2490mV, 2580mV, 2670mV, 2760mV, 2850mV, 2940mV, 3030mV)
108+
void setChargeCurrent(uint16_t current); // set charging current, 8mA ~ 456mA(step 8mA, default 128mA)
109+
void setDischargeCurrent(uint16_t current); // set discharging current, 200mA ~ 3200mA(step 200mA, default 2000mA)
110+
void setChargeVoltage(uint16_t voltage); // set charging voltage, 3600mV ~ 4545mV(step 15mV, default 4200mV)
111+
void setWatchdogTimer(uint8_t sec); // set charge watchdog timeout(0s, 40s, 80s, 160s, default 160s, 0 to disable)
112+
void feedWatchdog(); // feed watchdog timer
113+
void setShipMode(bool en); // set ship mode
114+
ChargeStatus getChargeStatus(); // get charge status
115+
void setHiZ(bool enable); // set Hi-Z mode, true: USB -x-> SYS, false: USB -> SYS
116+
117+
// BQ27220 functions
56118
float getVoltage(); // get battery voltage in Volts
119+
float getCurrent(); // get battery current in Amperes
57120
uint16_t getChargeLevel(); // get battery charge level in percents
121+
uint16_t getAvgPower(); // get average power in mWatts
122+
float getTemperature(); // get battery temperature in Celsius
123+
uint16_t getCycleCount(); // get battery cycle count
58124
};
59125

60126
extern ExpanderPin LORA_LNA_ENABLE;

0 commit comments

Comments
 (0)