|
8 | 8 |
|
9 | 9 | static bool wireInitialized = false; |
10 | 10 |
|
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 | + |
12 | 15 | static void writeRegister(uint8_t address, uint8_t reg, uint8_t value) { |
13 | 16 | WireInternal.beginTransmission(address); |
14 | 17 | WireInternal.write(reg); |
@@ -85,37 +88,252 @@ int digitalRead(ExpanderPin pin) { |
85 | 88 | return readBitRegister(pin.address, 0xF, pin.pin); |
86 | 89 | } |
87 | 90 |
|
| 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 | + |
88 | 106 | 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) { |
91 | 274 | if (!wireInitialized) { |
92 | 275 | WireInternal.begin(SDA, SCL); |
93 | 276 | wireInitialized = true; |
94 | 277 | } |
95 | | - writeBitRegister(0x49, 0x1, 3, false); |
| 278 | + // bit 4 set Hi-Z mode |
| 279 | + writeBitRegister(AW32001_I2C_ADDR, AW3200_POWER_ON_CFG, 4, enable); |
96 | 280 | } |
97 | 281 |
|
98 | 282 | float NessoBattery::getVoltage() { |
99 | | - // BQ27220 - address 0x55 |
100 | 283 | if (!wireInitialized) { |
101 | 284 | WireInternal.begin(SDA, SCL); |
102 | 285 | wireInitialized = true; |
103 | 286 | } |
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); |
105 | 288 | return (float)voltage / 1000.0f; |
106 | 289 | } |
107 | 290 |
|
| 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 | + |
108 | 300 | uint16_t NessoBattery::getChargeLevel() { |
109 | | - // BQ27220 - address 0x55 |
110 | 301 | if (!wireInitialized) { |
111 | 302 | WireInternal.begin(SDA, SCL); |
112 | 303 | wireInitialized = true; |
113 | 304 | } |
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); |
116 | 307 | return (current_capacity * 100) / total_capacity; |
117 | 308 | } |
118 | 309 |
|
| 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 | + |
119 | 337 | ExpanderPin LORA_LNA_ENABLE(5); |
120 | 338 | ExpanderPin LORA_ANTENNA_SWITCH(6); |
121 | 339 | ExpanderPin LORA_ENABLE(7); |
|
0 commit comments