Skip to content

Commit 366d26a

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 d027ba1 commit 366d26a

File tree

2 files changed

+296
-10
lines changed

2 files changed

+296
-10
lines changed

variants/arduino_nesso_n1/expander.cpp

Lines changed: 227 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,252 @@ 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, uint16_t dpm_voltage, 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+
setVinDPMVoltage(dpm_voltage);
102+
setHiZ(false);
103+
setChargeEnable(true);
104+
}
105+
88106
void NessoBattery::enableCharge() {
89-
// AW32001E - address 0x49
90-
// set CEB bit low (charge enable)
107+
setChargeEnable(true);
108+
}
109+
110+
void NessoBattery::setChargeEnable(bool enable) {
111+
if (!wireInitialized) {
112+
WireInternal.begin(SDA, SCL);
113+
wireInitialized = true;
114+
}
115+
// bit 3 set charge enable
116+
writeBitRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG, 3, !enable);
117+
}
118+
119+
void NessoBattery::setVinDPMVoltage(uint16_t voltage) {
120+
if (!wireInitialized) {
121+
WireInternal.begin(SDA, SCL);
122+
wireInitialized = true;
123+
}
124+
if (voltage < 3880) {
125+
voltage = 3880;
126+
}
127+
if (voltage > 5080) {
128+
voltage = 5080;
129+
}
130+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_INPUT_SRC);
131+
// bits 7-4 set Vin DPM voltage
132+
reg_value &= ~0b01111000;
133+
reg_value |= ((voltage - 3880) / 80) << 4;
134+
writeRegister(AW32001_I2C_ADDR, AW3200_INPUT_SRC, reg_value);
135+
}
136+
137+
void NessoBattery::setIinLimitCurrent(uint16_t current) {
138+
if (!wireInitialized) {
139+
WireInternal.begin(SDA, SCL);
140+
wireInitialized = true;
141+
}
142+
if (current < 50) {
143+
current = 50;
144+
}
145+
if (current > 500) {
146+
current = 500;
147+
}
148+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_INPUT_SRC);
149+
// bits 3-0 set Iin limit current
150+
reg_value &= ~0b00001111;
151+
reg_value |= ((current - 50) / 30) & 0b00001111;
152+
writeRegister(AW32001_I2C_ADDR, AW3200_INPUT_SRC, reg_value);
153+
}
154+
155+
void NessoBattery::setBatUVLO(UnderVoltageLockout uvlo) {
156+
if (!wireInitialized) {
157+
WireInternal.begin(SDA, SCL);
158+
wireInitialized = true;
159+
}
160+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG);
161+
// bits 2-0 set UVLO
162+
reg_value &= ~0b00000111;
163+
reg_value |= (uvlo & 0b00000111);
164+
writeRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG, reg_value);
165+
}
166+
167+
void NessoBattery::setChargeCurrent(uint16_t current) {
168+
if (!wireInitialized) {
169+
WireInternal.begin(SDA, SCL);
170+
wireInitialized = true;
171+
}
172+
if (current < 8) {
173+
current = 8;
174+
}
175+
if (current > 456) {
176+
current = 456;
177+
}
178+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_CHG_CURRENT);
179+
// bits 5-0 set charge current
180+
reg_value &= ~0b00111111;
181+
reg_value |= ((current - 8) / 8) & 0b00111111;
182+
writeRegister(AW32001_I2C_ADDR, AW3200_CHG_CURRENT, reg_value);
183+
}
184+
185+
void NessoBattery::setDischargeCurrent(uint16_t current) {
186+
if (!wireInitialized) {
187+
WireInternal.begin(SDA, SCL);
188+
wireInitialized = true;
189+
}
190+
if (current < 200) {
191+
current = 200;
192+
}
193+
if (current > 3200) {
194+
current = 3200;
195+
}
196+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_TERM_CURRENT);
197+
// bits 7-4 set discharge current
198+
reg_value &= ~0b11110000;
199+
reg_value |= (((current - 200) / 200) & 0b00001111) << 4;
200+
writeRegister(AW32001_I2C_ADDR, AW3200_TERM_CURRENT, reg_value);
201+
}
202+
203+
void NessoBattery::setChargeVoltage(uint16_t voltage) {
204+
if (!wireInitialized) {
205+
WireInternal.begin(SDA, SCL);
206+
wireInitialized = true;
207+
}
208+
if (voltage < 3600) {
209+
voltage = 3600;
210+
}
211+
if (voltage > 4545) {
212+
voltage = 4545;
213+
}
214+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_CHG_VOLTAGE);
215+
// bits 7-2 set charge voltage
216+
reg_value &= ~0b11111100;
217+
reg_value |= ((voltage - 3600) / 15) << 2;
218+
writeRegister(AW32001_I2C_ADDR, AW3200_CHG_VOLTAGE, reg_value);
219+
}
220+
221+
void NessoBattery::setWatchdogTimer(uint8_t sec) {
222+
if (!wireInitialized) {
223+
WireInternal.begin(SDA, SCL);
224+
wireInitialized = true;
225+
}
226+
227+
uint8_t reg_value = readRegister(AW32001_I2C_ADDR, AW3200_TIMER_WD);
228+
uint8_t bits = 0;
229+
switch (sec) {
230+
case 0:
231+
bits = 0b00; // disable watchdog
232+
break;
233+
case 40: bits = 0b01; break;
234+
case 80: bits = 0b10; break;
235+
case 160: bits = 0b11; break;
236+
default: bits = 0b11; break;
237+
}
238+
// bits 6-5 set watchdog timer
239+
reg_value &= ~(0b11 << 5);
240+
reg_value |= (bits << 5);
241+
writeRegister(AW32001_I2C_ADDR, AW3200_TIMER_WD, reg_value);
242+
}
243+
244+
void NessoBattery::feedWatchdog() {
245+
if (!wireInitialized) {
246+
WireInternal.begin(SDA, SCL);
247+
wireInitialized = true;
248+
}
249+
// bit 6 set feed watchdog
250+
writeBitRegister(AW32001_I2C_ADDR, AW3200_CHG_CURRENT, 6, true);
251+
}
252+
253+
void NessoBattery::setShipMode(bool en) {
254+
if (!wireInitialized) {
255+
WireInternal.begin(SDA, SCL);
256+
wireInitialized = true;
257+
}
258+
// bit 5 set ship mode
259+
writeBitRegister(AW32001_I2C_ADDR, AW3200_MAIN_CTRL, 5, en);
260+
}
261+
262+
NessoBattery::ChargeStatus NessoBattery::getChargeStatus() {
263+
if (!wireInitialized) {
264+
WireInternal.begin(SDA, SCL);
265+
wireInitialized = true;
266+
}
267+
uint8_t status = readRegister(AW32001_I2C_ADDR, AW3200_SYS_STATUS);
268+
// bits 4-3 set charge status
269+
uint8_t charge_status = (status >> 3) & 0b11;
270+
return static_cast<ChargeStatus>(charge_status);
271+
}
272+
273+
void NessoBattery::setHiZ(bool enable) {
91274
if (!wireInitialized) {
92275
WireInternal.begin(SDA, SCL);
93276
wireInitialized = true;
94277
}
95-
writeBitRegister(0x49, 0x1, 3, false);
278+
// bit 4 set Hi-Z mode
279+
writeBitRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG, 4, enable);
96280
}
97281

98282
float NessoBattery::getVoltage() {
99-
// BQ27220 - address 0x55
100283
if (!wireInitialized) {
101284
WireInternal.begin(SDA, SCL);
102285
wireInitialized = true;
103286
}
104-
uint16_t voltage = (readRegister(0x55, 0x9) << 8) | readRegister(0x55, 0x8);
287+
uint16_t voltage = (readRegister(BQ27220_I2C_ADDR, BQ27220_VOLTAGE + 1) << 8) | readRegister(BQ27220_I2C_ADDR, BQ27220_VOLTAGE);
105288
return (float)voltage / 1000.0f;
106289
}
107290

291+
float NessoBattery::getCurrent() {
292+
if (!wireInitialized) {
293+
WireInternal.begin(SDA, SCL);
294+
wireInitialized = true;
295+
}
296+
int16_t current = (readRegister(BQ27220_I2C_ADDR, BQ27220_CURRENT + 1) << 8) | readRegister(BQ27220_I2C_ADDR, BQ27220_CURRENT);
297+
return (float)current / 1000.0f;
298+
}
299+
108300
uint16_t NessoBattery::getChargeLevel() {
109-
// BQ27220 - address 0x55
110301
if (!wireInitialized) {
111302
WireInternal.begin(SDA, SCL);
112303
wireInitialized = true;
113304
}
114-
uint16_t current_capacity = readRegister(0x55, 0x11) << 8 | readRegister(0x55, 0x10);
115-
uint16_t total_capacity = readRegister(0x55, 0x13) << 8 | readRegister(0x55, 0x12);
305+
uint16_t current_capacity = readRegister(BQ27220_I2C_ADDR, BQ27220_REMAIN_CAPACITY + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_REMAIN_CAPACITY);
306+
uint16_t total_capacity = readRegister(BQ27220_I2C_ADDR, BQ27220_FULL_CAPACITY + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_FULL_CAPACITY);
116307
return (current_capacity * 100) / total_capacity;
117308
}
118309

310+
uint16_t NessoBattery::getAvgPower() {
311+
if (!wireInitialized) {
312+
WireInternal.begin(SDA, SCL);
313+
wireInitialized = true;
314+
}
315+
uint16_t avg_power = readRegister(BQ27220_I2C_ADDR, BQ27220_AVG_POWER + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_AVG_POWER);
316+
return avg_power;
317+
}
318+
319+
float NessoBattery::getTemperature() {
320+
if (!wireInitialized) {
321+
WireInternal.begin(SDA, SCL);
322+
wireInitialized = true;
323+
}
324+
uint16_t temp = readRegister(BQ27220_I2C_ADDR, BQ27220_TEMPERATURE + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_TEMPERATURE);
325+
return ((float)temp / 10.0f) - 273.15f;
326+
}
327+
328+
uint16_t NessoBattery::getCycleCount() {
329+
if (!wireInitialized) {
330+
WireInternal.begin(SDA, SCL);
331+
wireInitialized = true;
332+
}
333+
uint16_t cycle_count = readRegister(BQ27220_I2C_ADDR, BQ27220_CYCLE_COUNT + 1) << 8 | readRegister(BQ27220_I2C_ADDR, BQ27220_CYCLE_COUNT);
334+
return cycle_count;
335+
}
336+
119337
ExpanderPin LORA_LNA_ENABLE(5);
120338
ExpanderPin LORA_ANTENNA_SWITCH(6);
121339
ExpanderPin LORA_ENABLE(7);

variants/arduino_nesso_n1/pins_arduino.h

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,78 @@ 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_2580mV, uint16_t dpm_voltage = 4520, uint8_t timeout = 0
102+
); // default: charge current 256mA, battery 4200mV, uvlo 2580mV, DMP 4520mV, disable watchdog
103+
104+
// AW32001 functions
105+
void enableCharge(); // enable charging
106+
void setChargeEnable(bool enable); // charge control
107+
void setVinDPMVoltage(uint16_t voltage); // set input voltage limit, 3880mV ~ 5080mV(step 80mV, default 4520mV)
108+
void setIinLimitCurrent(uint16_t current); // set input current limit, 50mA ~ 500mA(step 30mA, default 500mA)
109+
void setBatUVLO(UnderVoltageLockout uvlo); // set battery under voltage lockout(2430mV, 2490mV, 2580mV, 2670mV, 2760mV, 2850mV, 2940mV, 3030mV)
110+
void setChargeCurrent(uint16_t current); // set charging current, 8mA ~ 456mA(step 8mA, default 128mA)
111+
void setDischargeCurrent(uint16_t current); // set discharging current, 200mA ~ 3200mA(step 200mA, default 2000mA)
112+
void setChargeVoltage(uint16_t voltage); // set charging voltage, 3600mV ~ 4545mV(step 15mV, default 4200mV)
113+
void setWatchdogTimer(uint8_t sec); // set charge watchdog timeout(0s, 40s, 80s, 160s, default 160s, 0 to disable)
114+
void feedWatchdog(); // feed watchdog timer
115+
void setShipMode(bool en); // set ship mode
116+
ChargeStatus getChargeStatus(); // get charge status
117+
void setHiZ(bool enable); // set Hi-Z mode, true: USB -x-> SYS, false: USB -> SYS
118+
119+
// BQ27220 functions
56120
float getVoltage(); // get battery voltage in Volts
121+
float getCurrent(); // get battery current in Amperes
57122
uint16_t getChargeLevel(); // get battery charge level in percents
123+
uint16_t getAvgPower(); // get average power in mWatts
124+
float getTemperature(); // get battery temperature in Celsius
125+
uint16_t getCycleCount(); // get battery cycle count
58126
};
59127

60128
extern ExpanderPin LORA_LNA_ENABLE;

0 commit comments

Comments
 (0)