Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 56be13b

Browse files
authored
feat(nesso-n1): add comprehensive battery management API (#12052)
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 1a0c4b3 commit 56be13b

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+
int16_t NessoBattery::getAvgPower() {
311+
if (!wireInitialized) {
312+
WireInternal.begin(SDA, SCL);
313+
wireInitialized = true;
314+
}
315+
int16_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+
int16_t getAvgPower(); // get average power in mWatts, can be negative
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)

AltStyle によって変換されたページ (->オリジナル) /