board_reform2.c (40447B)
1 #include "projectconfig.h" 2 3 #ifdef CFG_BRD_REFORM2 4 5 #include <string.h> /* strlen */ 6 7 #include "boards/board.h" 8 #include "core/gpio/gpio.h" 9 #include "core/delay/delay.h" 10 #include "core/eeprom/eeprom.h" 11 #include "core/pmu/pmu.h" 12 #include "core/i2c/i2c.h" 13 #include "core/ssp0/ssp0.h" 14 #include "core/ssp1/ssp1.h" 15 #include "core/uart/uart.h" 16 17 #ifdef CFG_USB 18 #include "core/usb/usbd.h" 19 #ifdef CFG_USB_CDC 20 #include "core/usb/usb_cdc.h" 21 #endif 22 #endif 23 24 #ifdef CFG_INTERFACE 25 #include "cli/cli.h" 26 #endif 27 28 #ifdef CFG_PROTOCOL 29 #include "protocol/protocol.h" 30 #endif 31 32 #define REFORM_MBREV_D2 2 33 #define REFORM_MBREV_D3 3 34 #define REFORM_MBREV_D4 4 35 #define REFORM_MBREV_R1 11 36 #define REFORM_MBREV_R2 12 // stock R2 37 #define REFORM_MBREV_20_R3 13 // R2 with "NTC instead of RNG/SS" fix 38 #define REFORM_MBREV_25_R2 25 // motherboard 2.5 39 40 // don't forget to set this to the correct rev for your motherboard! 41 // if you have a motherboard made in 2023 (2.5 R-2): 42 //#define REFORM_MOTHERBOARD_REV REFORM_MBREV_25_R2 43 // if you have a motherboard made before 2023 (2.0 R-3): 44 //#define REFORM_MOTHERBOARD_REV REFORM_MBREV_20_R3 45 46 #ifndef REFORM_MOTHERBOARD_REV 47 # error You must have REFORM_MOTHERBOARD_REV set to the firmware you intend to build 48 #endif 49 50 //#define REF2_DEBUG 1 51 #define FW_STRING1 "MREF2LPC" 52 #if REFORM_MOTHERBOARD_REV == 2 53 # define FW_STRING2 "D2" 54 #elif REFORM_MOTHERBOARD_REV == 3 55 # define FW_STRING2 "D3" 56 #elif REFORM_MOTHERBOARD_REV == 4 57 # define FW_STRING2 "D4" 58 #elif REFORM_MOTHERBOARD_REV == 11 59 # define FW_STRING2 "R1" 60 #elif REFORM_MOTHERBOARD_REV == 12 61 # define FW_STRING2 "R2" 62 #elif REFORM_MOTHERBOARD_REV == 13 63 # define FW_STRING2 "R3" 64 #elif REFORM_MOTHERBOARD_REV == 25 65 # define FW_STRING2 "25_R2" 66 #endif 67 #ifndef FW_STRING3 68 # define FW_STRING3 "20231124" 69 #endif 70 #define FW_REV FW_STRING1 FW_STRING2 FW_STRING3 71 72 #define POWERSAVE_SLEEP_SECONDS 1 73 #define POWERSAVE_HOLDOFF_CYCLES (60*15) 74 75 #define INA260_ADDRESS 0x4e 76 #define LTC4162F_ADDRESS 0x68 77 #define I2C_READ 1 78 79 #define ST_EXPECT_DIGIT_0 0 80 #define ST_EXPECT_DIGIT_1 1 81 #define ST_EXPECT_DIGIT_2 2 82 #define ST_EXPECT_DIGIT_3 3 83 #define ST_EXPECT_CMD 4 84 #define ST_SYNTAX_ERROR 5 85 #define ST_EXPECT_RETURN 6 86 #define ST_EXPECT_MAGIC 7 87 88 extern volatile uint8_t i2c_write_buf[I2C_BUFSIZE]; 89 extern volatile uint8_t i2c_read_buf[I2C_BUFSIZE]; 90 extern volatile uint32_t i2c_read_len, i2c_write_len; 91 92 err_t i2c_write8(uint8_t i2c_addr, uint8_t reg, uint8_t value) 93 { 94 i2c_write_len = 3; 95 i2c_read_len = 0; 96 i2c_write_buf[0] = i2c_addr << 1; 97 i2c_write_buf[1] = reg; 98 i2c_write_buf[2] = value; 99 i2cEngine(); 100 101 return ERROR_NONE; 102 } 103 104 int16_t i2c_read16_be(uint8_t i2c_addr, uint8_t reg) 105 { 106 i2c_write_len = 2; 107 i2c_read_len = 2; 108 i2c_write_buf[0] = i2c_addr << 1; 109 i2c_write_buf[1] = reg; 110 i2c_write_buf[2] = (i2c_addr << 1) | I2C_READ; 111 i2cEngine(); 112 113 int16_t value = (i2c_read_buf[0] << 8) | i2c_read_buf[1]; 114 return value; 115 } 116 117 err_t i2c_write16_be(uint8_t i2c_addr, uint8_t reg, uint16_t value) 118 { 119 i2c_write_len = 4; 120 i2c_read_len = 0; 121 i2c_write_buf[0] = i2c_addr << 1; 122 i2c_write_buf[1] = reg; 123 i2c_write_buf[2] = value>>8; 124 i2c_write_buf[3] = value&0xff; 125 i2cEngine(); 126 127 return ERROR_NONE; 128 } 129 130 int16_t i2c_read16_le(uint8_t i2c_addr, uint8_t reg) 131 { 132 i2c_write_len = 2; 133 i2c_read_len = 2; 134 i2c_write_buf[0] = i2c_addr << 1; 135 i2c_write_buf[1] = reg; 136 i2c_write_buf[2] = (i2c_addr << 1) | I2C_READ; 137 i2cEngine(); 138 139 int16_t value = (i2c_read_buf[1] << 8) | i2c_read_buf[0]; 140 return value; 141 } 142 143 err_t i2c_write16_le(uint8_t i2c_addr, uint8_t reg, uint16_t value) 144 { 145 i2c_write_len = 4; 146 i2c_read_len = 0; 147 i2c_write_buf[0] = i2c_addr << 1; 148 i2c_write_buf[1] = reg; 149 i2c_write_buf[2] = value&0xff; 150 i2c_write_buf[3] = value>>8; 151 i2cEngine(); 152 153 return ERROR_NONE; 154 } 155 156 // see https://www.analog.com/media/en/technical-documentation/data-sheets/680324fa.pdf 157 // default = 0x41 158 uint8_t calc_pec(uint8_t d, uint8_t pec) { 159 //uint8_t pec = 0x41; // 0100 0001 160 161 for (int i=0; i<8; i++) { 162 uint8_t bit = (d>>7)&1; // pop off upper bit 163 d = d<<1; 164 uint8_t in0 = bit ^ (pec>>7); 165 uint8_t in1 = (pec & 1) ^ in0; 166 uint8_t in2 = ((pec & 2)>>1) ^ in0; 167 pec = (pec<<1)&0xf8; 168 pec |= in2<<2; 169 pec |= in1<<1; 170 pec |= in0; 171 } 172 173 return pec; 174 } 175 176 // main charger state machine 177 enum state_t { 178 ST_CHARGE, 179 ST_OVERVOLTED, 180 ST_COOLDOWN, 181 ST_UNDERVOLTED, 182 ST_MISSING, 183 ST_FULLY_CHARGED, 184 ST_POWERSAVE 185 }; 186 187 // charging state machine 188 int state = ST_CHARGE; 189 int cycles_in_state = 0; 190 int cycles_uptime = 0; 191 int charge_current = 1; 192 uint32_t cur_second = 0; 193 uint32_t last_second = 0; 194 int powersave_holdoff_cycles = POWERSAVE_HOLDOFF_CYCLES; 195 196 // 1.8A x 3600 seconds/hour 197 #define MAX_CAPACITY (1.8)*3600.0 198 float capacity_max_ampsecs = MAX_CAPACITY; 199 float capacity_accu_ampsecs = MAX_CAPACITY; 200 float capacity_min_ampsecs = 430*3.6; // TODO save this in flash after learning 201 int capacity_percentage = 0; 202 float volts = 0; 203 float current = 0; 204 unsigned long lastTime = 0; 205 char uartBuffer[255] = {0}; 206 float cells_v[8] = {0,0,0,0,0,0,0,0}; 207 int num_undervolted_cells = 0; 208 int num_undervolted_critical_cells = 0; 209 int num_fully_charged_cells = 0; 210 int num_overvolted_cells = 0; 211 int num_missing_cells = 0; 212 uint8_t reached_full_charge = 0; 213 uint16_t discharge_bits = 0; 214 uint16_t overvoltage_bits = 0; 215 uint16_t undervoltage_bits = 0; 216 uint16_t missing_bits = 0; 217 uint16_t missing_reason = 0; 218 uint8_t spir[64]; 219 bool som_is_powered = false; 220 bool imx_uart_enabled = false; 221 222 #if (REFORM_MOTHERBOARD_REV >= REFORM_MBREV_20_R3) 223 #define OVERVOLTAGE_START_VALUE 3.8 224 #define OVERVOLTAGE_STOP_VALUE 3.6 225 #else 226 #define OVERVOLTAGE_START_VALUE 3.61 227 #define OVERVOLTAGE_STOP_VALUE 3.5 228 #endif 229 #define UNDERVOLTAGE_VALUE 2.45 230 #define UNDERVOLTAGE_CRITICAL_VALUE 2.3 231 #define MISSING_VALUE_HI 4.5 232 #define MISSING_VALUE_LO 0.4 233 #if (REFORM_MOTHERBOARD_REV >= REFORM_MBREV_20_R3) 234 #define FULLY_CHARGED_VOLTAGE 3.4 235 #define FULLY_CHARGED_CURRENT -0.3 236 #else 237 #define FULLY_CHARGED_VOLTAGE 3.5 238 #define FULLY_CHARGED_CURRENT -0.6 239 #endif 240 #define WALLPOWER_DETECT_VOLTAGE 24 241 242 void set_discharge_bits(uint16_t bits) { 243 // 0x10: WRCFG, write config 244 spir[0] = 0x01; 245 spir[1] = 0xc7; 246 247 spir[2] = 0xe1; // set cdc to 2 = continuous measurement 248 spir[3] = bits&0xff; // first 8 bits of discharge switches 249 spir[4] = (bits&0xf00)>>8; // last 4 bits of discharge switches 250 251 spir[5] = 0x0; 252 spir[6] = 0x0; 253 spir[7] = 0x0; 254 255 uint8_t pec = 0x41; 256 for (int i=2; i<=7; i++) { 257 pec = calc_pec(spir[i], pec); 258 } 259 spir[8] = pec; 260 261 LPC_GPIO->CLR[1] = (1 << 23); 262 ssp1Send(spir, 9); 263 LPC_GPIO->SET[1] = (1 << 23); 264 } 265 266 void disable_charge_current(void) { 267 if (REFORM_MOTHERBOARD_REV >= REFORM_MBREV_R1) { 268 LPC_GPIO->SET[1] |= (1 << 25); 269 } else { 270 LPC_GPIO->CLR[1] |= (1 << 25); 271 } 272 } 273 274 void enable_charge_current(void) { 275 if (REFORM_MOTHERBOARD_REV >= REFORM_MBREV_R1) { 276 LPC_GPIO->CLR[1] |= (1 << 25); 277 } else { 278 LPC_GPIO->SET[1] |= (1 << 25); 279 } 280 } 281 282 void measure_cell_voltages_and_control_discharge() { 283 // first, turn off any discharging 284 set_discharge_bits(0); 285 286 // defensive: make sure not to charge with any discharge bits 287 // turned on, regardless of state 288 if (state == ST_CHARGE && discharge_bits == 0) { 289 // we're in normal charge mode, so remove charge current limit 290 enable_charge_current(); 291 } else { 292 // we're discharging (balancing), or full charged, 293 // so limit charge current 294 // also don't charge if we have missing cells 295 disable_charge_current(); 296 set_discharge_bits(discharge_bits); 297 } 298 299 // 0x10: STCVDC, start adc 300 spir[0] = 0x60; 301 spir[1] = 0xe7; 302 LPC_GPIO->CLR[1] = (1 << 23); 303 ssp1Send(spir, 2); 304 LPC_GPIO->SET[1] = (1 << 23); 305 306 // delay is measured in "ticks", which are basically ms? 307 delay(50); // FIXME tunable 308 309 // 0x4: RDCV, read all cell voltages 310 spir[0] = 0x4; 311 spir[1] = 0xdc; 312 313 LPC_GPIO->CLR[1] = (1 << 23); 314 ssp1Send(spir, 2); 315 memset(spir, 0, 32); 316 ssp1Receive(spir, 19); 317 LPC_GPIO->SET[1] = (1 << 23); 318 319 int j=0; 320 for (int i=0; i<8; i+=2) { 321 cells_v[i] = ((float)((spir[j]|((spir[j+1]&0xf)<<8))-512)) * 1.5 / 1000.0; 322 cells_v[i+1] = ((float)((spir[j+1]&0xf0)>>4|(spir[j+2]<<4))-512) * 1.5 / 1000.0; 323 j+=3; 324 325 #ifdef REF2_DEBUG 326 //sprintf(uartBuffer,"cell %d: %fV cell %d: %fV\r\n", 327 // i, cells_v[i], i+1, cells_v[i+1]); 328 //uartSend((uint8_t *)uartBuffer, strlen(uartBuffer)); 329 #endif 330 } 331 332 num_missing_cells = 0; 333 num_undervolted_cells = 0; 334 num_fully_charged_cells = 0; 335 num_overvolted_cells = 0; 336 num_undervolted_critical_cells = 0; 337 338 missing_bits = 0; 339 undervoltage_bits = 0; 340 overvoltage_bits = 0; 341 for (int i=0; i<8; i++) { 342 if (cells_v[i] >= MISSING_VALUE_HI || cells_v[i] <= MISSING_VALUE_LO) { 343 missing_bits |= (1<<i); 344 num_missing_cells++; 345 } 346 else if ((state == ST_OVERVOLTED && cells_v[i] >= OVERVOLTAGE_STOP_VALUE) || 347 (state != ST_OVERVOLTED && cells_v[i] >= OVERVOLTAGE_START_VALUE)) { 348 overvoltage_bits |= (1<<i); 349 num_overvolted_cells++; 350 // we assume that overvoltage also means fully charged 351 num_fully_charged_cells++; 352 } 353 else if (cells_v[i] >= FULLY_CHARGED_VOLTAGE) { 354 num_fully_charged_cells++; 355 } 356 else if (cells_v[i] < UNDERVOLTAGE_VALUE) { 357 undervoltage_bits |= (1<<i); 358 num_undervolted_cells++; 359 360 if (cells_v[i] < UNDERVOLTAGE_CRITICAL_VALUE) { 361 num_undervolted_critical_cells++; 362 } 363 } 364 } 365 366 // 0x2: RDCFG, read config registers 367 /*spir[0] = 0x2; 368 spir[1] = 0xce; 369 370 LPC_GPIO->CLR[1] = (1 << 23); 371 delay(2); 372 ssp1Send(spir, 2); 373 memset(spir, 0, 32); 374 ssp1Receive(spir, 7); 375 delay(2); 376 LPC_GPIO->SET[1] = (1 << 23); 377 378 sprintf(uartBuffer,"CFG00 %02x %02x %02x %02x %02x %02x %02x %02x\r\n", 379 spir[0], spir[1], spir[2], spir[3], spir[4], spir[5], spir[6], spir[7]); 380 uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));*/ 381 } 382 383 void discharge_overvolted_cells() { 384 discharge_bits = overvoltage_bits; 385 } 386 387 void reset_discharge_bits() { 388 discharge_bits = 0; 389 } 390 391 // using INA260 current monitor, count amp-secs going in and out of battery 392 // (battery gauge) 393 void measure_and_accumulate_current() { 394 float raw_volts = (float)i2c_read16_be(INA260_ADDRESS, 0x2); 395 float raw_current = (float)i2c_read16_be(INA260_ADDRESS, 0x1); 396 397 volts = raw_volts * 0.00125; 398 current = raw_current * 0.001; 399 400 if (current>-0.02 && current<0.02) current = 0; // clamp to zero 401 402 unsigned long thisTime = delayGetSecondsActive(); 403 if (lastTime>0 && thisTime>lastTime) { 404 unsigned long secondsPassed = thisTime - lastTime; 405 406 if (secondsPassed >= 1) { 407 lastTime = thisTime; 408 409 // decrease estimated battery capacity 410 capacity_accu_ampsecs -= current*(secondsPassed); 411 } 412 } else { 413 // timer uninitialized or timer wrap 414 lastTime = thisTime; 415 } 416 417 #ifdef REF2_DEBUG 418 //sprintf(uartBuffer,"\033[H\033[2JINA Ah: %f V: %f A: %f\r\n",capacity_accu_ampsecs/3600,volts,current); 419 //uartSend((uint8_t *)uartBuffer, strlen(uartBuffer)); 420 #endif 421 } 422 423 #define INA233_ADDRESS 0x40 424 425 void ina233_calibrate() { 426 /* 427 current_lsb = i_max / (pow(2,15)); // 0.00030517578125 (i_max = 10 amperes) 428 power_lsb = 25 * current_lsb; // 0.00762939453125 429 cal = 0.00512/(r_shunt * current_lsb); // 838.8608 (r_shunt = 0.02 ohms) 430 431 m_c = 1/current_lsb // 3276.8 432 m_b = 1/power_lsb // 131.072 433 434 see also: https://github.com/scottvr/pi-ina233/blob/master/ina233.py 435 */ 436 437 uint16_t cal = 838; 438 439 i2c_write16_le(INA233_ADDRESS, 0xd4, cal); 440 } 441 442 void measure_and_accumulate_current_v25() { 443 float raw_volts = (float)i2c_read16_le(INA233_ADDRESS, 0x88); 444 float raw_current = (float)i2c_read16_le(INA233_ADDRESS, 0x89); 445 446 volts = raw_volts * 0.00125; // TODO adjust for 20mOhms 447 current = raw_current * 0.00030517578125; 448 449 if (current>-0.02 && current<0.02) current = 0; // clamp to zero 450 451 unsigned long thisTime = delayGetSecondsActive(); 452 if (lastTime>0 && thisTime>lastTime) { 453 unsigned long secondsPassed = thisTime - lastTime; 454 455 if (secondsPassed >= 1) { 456 lastTime = thisTime; 457 458 // decrease estimated battery capacity 459 capacity_accu_ampsecs -= current * secondsPassed; 460 } 461 } else { 462 // timer uninitialized or timer wrap 463 lastTime = thisTime; 464 } 465 } 466 467 void turn_som_power_on(void) { 468 LPC_GPIO->CLR[1] = (1 << 28); // hold in reset 469 470 LPC_GPIO->CLR[0] = (1 << 20); // PCIe off 471 LPC_GPIO->CLR[1] = (1 << 19); // 1v2 off 472 LPC_GPIO->CLR[1] = (1 << 31); // USB 5v off (R1+) 473 LPC_GPIO->CLR[0] = (1 << 7); // AUX 3v3 off (R1+) 474 475 if (REFORM_MOTHERBOARD_REV >= REFORM_MBREV_R1 && REFORM_MOTHERBOARD_REV < REFORM_MBREV_25_R2) { 476 LPC_GPIO->CLR[1] = (1 << 16); // 3v3 on 477 LPC_GPIO->CLR[1] = (1 << 15); // 5v on 478 } else { 479 LPC_GPIO->SET[1] = (1 << 16); // 3v3 on 480 LPC_GPIO->SET[1] = (1 << 15); // 5v on 481 } 482 483 LPC_GPIO->SET[0] = (1 << 20); // PCIe on 484 LPC_GPIO->SET[1] = (1 << 19); // 1v2 on 485 LPC_GPIO->SET[1] = (1 << 31); // USB 5v on (R1+) 486 LPC_GPIO->SET[0] = (1 << 7); // AUX 3v3 on (R1+) 487 488 LPC_GPIO->SET[1] = (1 << 28); // release reset 489 490 som_is_powered = true; 491 } 492 493 void turn_som_power_off(void) { 494 LPC_GPIO->CLR[1] = (1 << 28); // hold in reset 495 496 if (REFORM_MOTHERBOARD_REV >= REFORM_MBREV_R1 && REFORM_MOTHERBOARD_REV < REFORM_MBREV_25_R2) { 497 LPC_GPIO->SET[1] = (1 << 16); // 3v3 off 498 LPC_GPIO->SET[1] = (1 << 15); // 5v off 499 } else { 500 LPC_GPIO->CLR[1] = (1 << 16); // 3v3 off 501 LPC_GPIO->CLR[1] = (1 << 15); // 5v off 502 } 503 504 LPC_GPIO->CLR[0] = (1 << 20); // PCIe off 505 LPC_GPIO->CLR[1] = (1 << 19); // 1v2 off 506 LPC_GPIO->CLR[1] = (1 << 31); // USB 5v off (R1+) 507 LPC_GPIO->CLR[0] = (1 << 7); // AUX 3v3 off (R1+) 508 509 som_is_powered = false; 510 } 511 512 // just a reset pulse to IMX, no power toggling 513 void reset_som(void) { 514 LPC_GPIO->CLR[1] = (1 << 28); // hold reset 515 delay(1); 516 LPC_GPIO->SET[1] = (1 << 28); // release reset 517 } 518 519 void turn_aux_power_on(void) { 520 LPC_GPIO->SET[0] = (1 << 20); // PCIe on 521 //LPC_GPIO->SET[1] = (1 << 19); // 1v2 on 522 //LPC_GPIO->SET[1] = (1 << 31); // USB 5v on (R1+) 523 //LPC_GPIO->SET[0] = (1 << 7); // AUX 3v3 on (R1+) 524 } 525 526 void turn_aux_power_off(void) { 527 LPC_GPIO->CLR[0] = (1 << 20); // PCIe off 528 //LPC_GPIO->CLR[1] = (1 << 19); // 1v2 off 529 //LPC_GPIO->CLR[1] = (1 << 31); // USB 5v off (R1+) 530 //LPC_GPIO->CLR[0] = (1 << 7); // AUX 3v3 off (R1+) 531 } 532 533 void brownout_enable(void) { 534 // Set brownout threshold to 1.46-1.63V (lowest level) 535 // and enable brownout reset (BODRSTENA) 536 LPC_SYSCON->BODCTRL = 0x0 | (1<<4); 537 } 538 539 void brownout_disable(void) { 540 // disable brownout reset (BODRSTENA) 541 LPC_SYSCON->BODCTRL = 0x0; 542 } 543 544 void watchdog_feed(void) { 545 __disable_irq(); 546 LPC_WWDT->FEED = 0xAA; 547 LPC_WWDT->FEED = 0x55; 548 __enable_irq(); 549 } 550 551 #define WWDT_WDMOD_WDEN ((uint32_t) (1 << 0)) 552 #define WWDT_WDMOD_WDRESET ((uint32_t) (1 << 1)) 553 554 void boardInit(void) 555 { 556 brownout_enable(); 557 558 SystemCoreClockUpdate(); 559 GPIOInit(); 560 delayInit(); 561 562 // Set up GPIO directions 563 564 // 5V regulator (U7) on/off 565 LPC_GPIO->DIR[1] |= (1 << 15); 566 // 3V3 regulator (U12) on/off 567 LPC_GPIO->DIR[1] |= (1 << 16); 568 // 1V2 regulator (U13) on/off 569 LPC_GPIO->DIR[1] |= (1 << 19); 570 // PCIe 1 power supply transistor (1V5 regulator U19 and 3V3 load switch) 571 LPC_GPIO->DIR[0] |= (1 << 20); 572 // USB 5V rail on/off (U24) (board revision R-1+) 573 LPC_GPIO->DIR[1] |= (1 << 31); 574 // AUX 3V3 power rail on/off (U27), and downstream 1V8 (U17) (board revision R-1+) 575 LPC_GPIO->DIR[0] |= (1 << 7); 576 577 // IMX Wake 578 LPC_GPIO->DIR[1] |= (1 << 24); 579 // IMX Reset 580 LPC_GPIO->DIR[1] |= (1 << 28); 581 582 // RNG/SS pin of LTC4020: control/limit charge current 583 LPC_GPIO->DIR[1] |= (1 << 25); 584 585 // initially turn the system off 586 turn_som_power_off(); 587 588 // start with low charge current 589 disable_charge_current(); 590 591 uartInit(CFG_UART_BAUDRATE); 592 i2cInit(I2CMASTER); 593 594 // SPI1 connected to battery monitor (we're controller) 595 ssp1Init(); 596 ssp1ClockSlow(); 597 598 // SPI0 connected to the main SOM (they're controller) 599 ssp0Init(); 600 ssp0ClockFast(); 601 602 // SPI chip select 603 LPC_GPIO->DIR[1] |= (1 << 23); 604 LPC_GPIO->SET[1] = (1 << 23); // active low 605 606 // UART connected to i.MX8M ttymxc2 607 /* Set 0.14 UART RXD */ 608 // this disrupts keyboard communication when main power turned off 609 //LPC_IOCON->PIO1_14 &= ~0x07; 610 //LPC_IOCON->PIO1_14 |= 0x03; 611 612 // only send to reform, don't receive from it 613 /* Set 0.13 UART TXD */ 614 LPC_IOCON->PIO1_13 &= ~0x07; 615 616 // enable debug UART on expansion header 617 LPC_IOCON->PIO0_19 = 0x01; // TXD 618 imx_uart_enabled = true; 619 620 // motherboard 2.5 introduces INA233 instead of INA260 621 if (REFORM_MOTHERBOARD_REV >= REFORM_MBREV_25_R2) { 622 ina233_calibrate(); 623 } 624 625 #ifdef REF2_DEBUG 626 sprintf(uartBuffer, "\r\nMNT Reform 2.0 MCU initialized.\r\n"); 627 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 628 #endif 629 } 630 631 char remote_cmd = 0; 632 uint8_t remote_arg = 0; 633 unsigned char cmd_state = ST_EXPECT_DIGIT_0; 634 unsigned int cmd_number = 0; 635 int cmd_echo = 0; 636 int force_sleep = 0; 637 638 void handle_commands() { 639 if (!uartRxBufferDataPending()) return; 640 641 // reset sleep counter on any interaction 642 powersave_holdoff_cycles = POWERSAVE_HOLDOFF_CYCLES; 643 644 char chr = uartRxBufferRead(); 645 646 if (cmd_echo) { 647 sprintf(uartBuffer, "%c", chr); 648 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 649 } 650 651 // states: 652 // 0-3 digits of optional command argument 653 // 4 command letter expected 654 // 5 syntax error (unexpected character) 655 // 6 command letter entered 656 657 if (cmd_state>=ST_EXPECT_DIGIT_0 && cmd_state<=ST_EXPECT_DIGIT_3) { 658 // read number or command 659 if (chr >= '0' && chr <= '9') { 660 cmd_number*=10; 661 cmd_number+=(chr-'0'); 662 cmd_state++; 663 } else if ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z')) { 664 // command entered instead of digit 665 remote_cmd = chr; 666 cmd_state = ST_EXPECT_RETURN; 667 } else if (chr == '\n' || chr == ' ') { 668 // ignore newlines or spaces 669 } else if (chr == '\r') { 670 sprintf(uartBuffer, "error:syntax\r\n"); 671 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 672 cmd_state = ST_EXPECT_DIGIT_0; 673 cmd_number = 0; 674 } else { 675 // syntax error 676 cmd_state = ST_SYNTAX_ERROR; 677 } 678 } 679 else if (cmd_state == ST_EXPECT_CMD) { 680 // read command 681 if ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z')) { 682 remote_cmd = chr; 683 cmd_state = ST_EXPECT_RETURN; 684 } else { 685 cmd_state = ST_SYNTAX_ERROR; 686 } 687 } 688 else if (cmd_state == ST_SYNTAX_ERROR) { 689 // syntax error 690 if (chr == '\r') { 691 sprintf(uartBuffer, "error:syntax\r\n"); 692 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 693 cmd_state = ST_EXPECT_DIGIT_0; 694 cmd_number = 0; 695 } 696 } 697 else if (cmd_state == ST_EXPECT_RETURN) { 698 if (chr == '\n' || chr == ' ') { 699 // ignore newlines or spaces 700 } 701 else if (chr == '\r') { 702 if (cmd_echo) { 703 // FIXME 704 sprintf(uartBuffer,"\n"); 705 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 706 } 707 708 // execute 709 if (remote_cmd == 'p') { 710 // toggle system power and/or reset imx 711 if (cmd_number == 0) { 712 turn_som_power_off(); 713 sprintf(uartBuffer,"system: off\r\n"); 714 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 715 } else if (cmd_number == 2) { 716 reset_som(); 717 sprintf(uartBuffer,"system: reset\r\n"); 718 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 719 } else if (cmd_number == 3) { 720 turn_aux_power_off(); 721 sprintf(uartBuffer,"system: auxpwr off\r\n"); 722 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 723 } else if (cmd_number == 4) { 724 turn_aux_power_on(); 725 sprintf(uartBuffer,"system: auxpwr on\r\n"); 726 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 727 } else { 728 if (som_is_powered) { 729 sprintf(uartBuffer,"system: already on\r\n"); 730 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 731 } else { 732 turn_som_power_on(); 733 sprintf(uartBuffer,"system: on\r\n"); 734 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 735 } 736 } 737 } 738 else if (remote_cmd == 'x') { 739 // test sleep 740 force_sleep = cmd_number; 741 sprintf(uartBuffer,"sleep: %d\r\n", force_sleep); 742 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 743 } 744 else if (remote_cmd == 'a') { 745 // get system current (mA) 746 sprintf(uartBuffer,"%d\r\n",(int)(current*1000.0)); 747 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 748 } 749 else if (remote_cmd == 'v' && cmd_number>=0 && cmd_number<=7) { 750 // get cell voltage 751 sprintf(uartBuffer,"%d\r\n",(int)(cells_v[cmd_number]*1000.0)); 752 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 753 } 754 else if (remote_cmd == 'V') { 755 // get system voltage 756 sprintf(uartBuffer,"%d\r\n",(int)(volts*1000.0)); 757 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 758 } 759 else if (remote_cmd == 's') { 760 int min_mah = (int)(capacity_min_ampsecs/3.6); 761 int acc_mah = (int)(capacity_accu_ampsecs/3.6); 762 int max_mah = (int)(capacity_max_ampsecs/3.6); 763 764 // get charger system state 765 if (state == ST_CHARGE) { 766 sprintf(uartBuffer,"norm:%d mAh:%d:%d:%d up:%d "FW_REV"\r",cycles_in_state,min_mah,acc_mah,max_mah,cycles_uptime); 767 } else if (state == ST_OVERVOLTED) { 768 sprintf(uartBuffer,"baln:%d mAh:%d:%d:%d up:%d "FW_REV"\r",cycles_in_state,min_mah,acc_mah,max_mah,cycles_uptime); 769 } else if (state == ST_COOLDOWN) { 770 sprintf(uartBuffer,"cool:%d mAh:%d:%d:%d up:%d "FW_REV"\r",cycles_in_state,min_mah,acc_mah,max_mah,cycles_uptime); 771 } else if (state == ST_UNDERVOLTED) { 772 sprintf(uartBuffer,"uvol:%d mAh:%d:%d:%d up:%d "FW_REV"\r",cycles_in_state,min_mah,acc_mah,max_mah,cycles_uptime); 773 } else if (state == ST_MISSING) { 774 sprintf(uartBuffer,"miss:%d:%d mAh:%d:%d:%d up:%d "FW_REV"\r",missing_reason,cycles_in_state,min_mah,acc_mah,max_mah,cycles_uptime); 775 } else if (state == ST_FULLY_CHARGED) { 776 sprintf(uartBuffer,"full:%d mAh:%d:%d:%d up:%d "FW_REV"\r",cycles_in_state,min_mah,acc_mah,max_mah,cycles_uptime); 777 } else if (state == ST_POWERSAVE) { 778 sprintf(uartBuffer,"psav:%d mAh:%d:%d:%d up:%d "FW_REV"\r",cycles_in_state,min_mah,acc_mah,max_mah,cycles_uptime); 779 } else { 780 sprintf(uartBuffer,"unkn:%d:%d mAh:%d:%d:%d up:%d "FW_REV"\r",state,cycles_in_state,min_mah,acc_mah,max_mah,cycles_uptime); 781 } 782 783 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 784 } 785 else if (remote_cmd == 'f') { 786 // print the firmware string 787 if (cmd_number == 1) { 788 sprintf(uartBuffer,FW_STRING1"\r\n"); 789 } else if (cmd_number == 2) { 790 sprintf(uartBuffer,FW_STRING2"\r\n"); 791 } else if (cmd_number == 3) { 792 sprintf(uartBuffer,FW_STRING3"\r\n"); 793 } else { 794 // if cmd_number is 0, print all of them concatenated as it is 795 // done for "s" 796 sprintf(uartBuffer,FW_REV"\r\n"); 797 } 798 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 799 } 800 else if (remote_cmd == 'u') { 801 // turn reporting to i.MX on or off 802 if (cmd_number>0) { 803 // turn i.MX UART output on 804 imx_uart_enabled = true; 805 LPC_IOCON->PIO1_13 |= 0x3; 806 } else { 807 // turn i.MX UART output off 808 imx_uart_enabled = false; 809 LPC_IOCON->PIO1_13 &= ~0x07; 810 } 811 } 812 else if (remote_cmd == 'w') { 813 // wake i.MX 814 if (cmd_number>0) { 815 // send a string via UART 816 LPC_IOCON->PIO1_13 |= 0x3; 817 sprintf(uartBuffer,"wake!\r"); 818 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 819 LPC_IOCON->PIO1_13 &= ~0x07; 820 } else { 821 // pulse wake GPIO 822 LPC_GPIO->SET[1] = (1 << 24); 823 delay(100); 824 LPC_GPIO->CLR[1] = (1 << 24); 825 } 826 } 827 else if (remote_cmd == 'c') { 828 // get status of cells, current, voltage, fuel gauge 829 char gauge[5] = {0,0,0,0,0}; 830 831 // get fuel gauge (percent) 832 if (reached_full_charge > 0) { 833 sprintf(gauge,"%3d%%", capacity_percentage); 834 } else { 835 // if we never reached full charge, 836 // we don't really know where we are. 837 sprintf(gauge,"???%%"); 838 } 839 840 int mA = (int)(current*1000.0); 841 char mA_sign = ' '; 842 if (mA<0) { 843 mA = -mA; 844 mA_sign = '-'; 845 } 846 int mV = (int)(volts*1000.0); 847 848 sprintf(uartBuffer,"%02d%c%02d%c%02d%c%02d%c%02d%c%02d%c%02d%c%02d%cmA%c%04dmV%05d %s P%d\r\n", 849 (int)(cells_v[0]*10), 850 (discharge_bits &(1<<0))?'!':' ', 851 (int)(cells_v[1]*10), 852 (discharge_bits &(1<<1))?'!':' ', 853 (int)(cells_v[2]*10), 854 (discharge_bits &(1<<2))?'!':' ', 855 (int)(cells_v[3]*10), 856 (discharge_bits &(1<<3))?'!':' ', 857 (int)(cells_v[4]*10), 858 (discharge_bits &(1<<4))?'!':' ', 859 (int)(cells_v[5]*10), 860 (discharge_bits &(1<<5))?'!':' ', 861 (int)(cells_v[6]*10), 862 (discharge_bits &(1<<6))?'!':' ', 863 (int)(cells_v[7]*10), 864 (discharge_bits &(1<<7))?'!':' ', 865 mA_sign, 866 mA, 867 mV, 868 gauge, 869 som_is_powered); 870 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 871 } 872 else if (remote_cmd == 'U') { 873 // get uptime 874 sprintf(uartBuffer, "%d\r\n", cycles_uptime); 875 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 876 } 877 else if (remote_cmd == 'S') { 878 // get charger system cycles in current state 879 sprintf(uartBuffer, "%d\r\n", cycles_in_state); 880 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 881 } 882 else if (remote_cmd == 'C') { 883 // get battery capacity (mAh) 884 sprintf(uartBuffer,"%d/%d/%d\r\n", 885 (int)(capacity_accu_ampsecs/3.6), 886 (int)(capacity_min_ampsecs/3.6), 887 (int)(capacity_max_ampsecs/3.6)); 888 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 889 } 890 else if (remote_cmd == 'e') { 891 // toggle serial echo 892 cmd_echo = cmd_number?1:0; 893 } 894 else { 895 sprintf(uartBuffer, "error:command\r\n"); 896 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 897 } 898 899 cmd_state = ST_EXPECT_DIGIT_0; 900 cmd_number = 0; 901 } else { 902 cmd_state = ST_SYNTAX_ERROR; 903 } 904 } 905 } 906 907 unsigned char spi_cmd_state = ST_EXPECT_MAGIC; 908 unsigned char spi_command = '\0'; 909 910 uint8_t spi_arg1 = 0; 911 /** 912 * @brief SPI command from imx poll function 913 * Attempts to handle spi communication asynchronously and in a non-blocking way. 914 * 915 */ 916 void handle_spi_commands() { 917 STATIC_ASSERT(SSP0_FIFOSIZE >= 8); 918 uint8_t spi_buf[SSP0_FIFOSIZE]; 919 uint8_t len = SSP0_FIFOSIZE; 920 921 int all_zeroes = 1; 922 923 // non blocking read 924 // read until requested buffer full or receive buffer empty 925 for (uint8_t i = 0; i < len; i++) { 926 // No more data in receive buffer 927 if ((LPC_SSP0->SR & SSP0_SR_RNE_MASK) == SSP0_SR_RNE_EMPTY) { 928 len = i; 929 break; 930 } 931 spi_buf[i] = LPC_SSP0->DR; 932 if (spi_buf[i] != 0) all_zeroes = 0; 933 } 934 935 // states: 936 // 0 arg1 byte expected 937 // 4 command byte expected 938 // 6 execute command 939 // 7 magic byte expected 940 for (uint8_t s = 0; s < len; s++) { 941 if (spi_cmd_state == ST_EXPECT_MAGIC) { 942 // magic byte found, prevents garbage data 943 // in the bus from triggering a command 944 if (spi_buf[s] == 0xB5) { 945 spi_cmd_state = ST_EXPECT_CMD; 946 } 947 } 948 else if (spi_cmd_state == ST_EXPECT_CMD) { 949 // read command 950 spi_command = spi_buf[s]; 951 spi_cmd_state = ST_EXPECT_DIGIT_0; 952 } 953 else if (spi_cmd_state == ST_EXPECT_DIGIT_0) { 954 // read arg1 byte 955 spi_arg1 = spi_buf[s]; 956 spi_cmd_state = ST_EXPECT_RETURN; 957 } 958 } 959 960 if (spi_cmd_state == ST_EXPECT_MAGIC && !all_zeroes) { 961 // reset SPI0 block 962 // this is a workaround for confusion with 963 // software spi from BPI-CM4 where we get 964 // bit-shifted bytes 965 966 // dump the buffer to serial 967 if (imx_uart_enabled) { 968 for (int i = 0; i < len; i++) { 969 sprintf(uartBuffer, "S%d:%X\r\n", i, spi_buf[i]); 970 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 971 } 972 } 973 974 ssp0Init(); 975 } 976 977 if (spi_cmd_state != ST_EXPECT_RETURN) { 978 // waiting for more data 979 return; 980 } 981 982 /*if (imx_uart_enabled) { 983 sprintf(uartBuffer, "spi:exec:%X,%X\r\n", spi_command, spi_arg1); 984 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 985 }*/ 986 987 // clear receive buffer, reuse as send buffer 988 memset(spi_buf, 0, 8); 989 990 // execute power state command 991 if (spi_command == 'p') { 992 // toggle system power and/or reset imx 993 if (spi_arg1 == 1) { 994 turn_som_power_off(); 995 } 996 if (spi_arg1 == 2) { 997 turn_som_power_on(); 998 } 999 if (spi_arg1 == 3) { 1000 reset_som(); 1001 } 1002 if (spi_arg1 == 4) { 1003 turn_aux_power_off(); 1004 } 1005 if (spi_arg1 == 5) { 1006 turn_aux_power_on(); 1007 } 1008 1009 spi_buf[0] = som_is_powered; 1010 } 1011 // return firmware version and api info 1012 else if (spi_command == 'f') { 1013 if(spi_arg1 == 0) { 1014 memcpy(spi_buf, FW_STRING1, 8); 1015 } 1016 else if(spi_arg1 == 1) { 1017 memcpy(spi_buf, FW_STRING2, 8); 1018 } 1019 else { 1020 memcpy(spi_buf, FW_STRING3, 8); 1021 } 1022 } 1023 // execute status query command 1024 else if (spi_command == 'q') { 1025 uint8_t percentage = (uint8_t)capacity_percentage; 1026 // if (!reached_full_charge) { 1027 // percentage = 255; 1028 // } 1029 uint16_t voltsInt = (uint16_t)(volts*1000.0); 1030 uint16_t currentInt = (uint16_t)(current*1000.0); 1031 1032 spi_buf[0] = (uint8_t)voltsInt; 1033 spi_buf[1] = (uint8_t)(voltsInt >> 8); 1034 spi_buf[2] = (uint8_t)currentInt; 1035 spi_buf[3] = (uint8_t)(currentInt >> 8); 1036 spi_buf[4] = (uint8_t)percentage; 1037 spi_buf[5] = (uint8_t)state; 1038 //spi_buf[6] = bitfield of power power rails 1039 } 1040 // get cell voltage 1041 else if (spi_command == 'v') { 1042 uint16_t volts = 0; 1043 uint8_t cell1 = 0; 1044 1045 if (spi_arg1 == 1) { 1046 cell1 = 4; 1047 } 1048 1049 for(uint8_t c = 0; c < 4; c++) { 1050 volts = cells_v[c + cell1]*1000.0; 1051 spi_buf[c*2] = (uint8_t)volts; 1052 spi_buf[(c*2)+1] = (uint8_t)(volts >> 8); 1053 } 1054 } 1055 // get calculated capacity 1056 else if (spi_command == 'c') { 1057 uint16_t cap_accu = (uint16_t) capacity_accu_ampsecs / 3.6; 1058 uint16_t cap_min = (uint16_t) capacity_min_ampsecs / 3.6; 1059 uint16_t cap_max = (uint16_t) capacity_max_ampsecs / 3.6; 1060 1061 spi_buf[0] = (uint8_t) cap_accu; 1062 spi_buf[1] = (uint8_t) (cap_accu >> 8); 1063 spi_buf[2] = (uint8_t) cap_min; 1064 spi_buf[3] = (uint8_t) (cap_min >> 8); 1065 spi_buf[4] = (uint8_t) cap_max; 1066 spi_buf[5] = (uint8_t) (cap_max >> 8); 1067 } 1068 // turn reporting to i.MX on or off 1069 else if (spi_command == 'u') { 1070 if (spi_arg1 == 1) { 1071 // turn i.MX UART output on 1072 imx_uart_enabled = true; 1073 LPC_IOCON->PIO1_13 |= 0x3; 1074 } else { 1075 // turn i.MX UART output off 1076 imx_uart_enabled = false; 1077 LPC_IOCON->PIO1_13 &= ~0x07; 1078 } 1079 } 1080 1081 /*if (imx_uart_enabled) { 1082 sprintf(uartBuffer, "spi:res: %X %X %X %X %X %X %X %X\r\n", 1083 spi_buf[0], spi_buf[1], spi_buf[2], spi_buf[3], 1084 spi_buf[4], spi_buf[5], spi_buf[6], spi_buf[7]); 1085 uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); 1086 }*/ 1087 1088 // Host must wait while the LPC prepares response buffer 1089 // If host does not read 8 bytes the previous response buffer will be stuck in here. 1090 for (uint8_t i = 0; i < SSP0_FIFOSIZE; i++) 1091 LPC_SSP0->DR = spi_buf[i]; 1092 1093 // Clear RX FIFO 1094 for (uint8_t i = 0; i < SSP0_FIFOSIZE; i++) 1095 spi_buf[i] = LPC_SSP0->DR; 1096 1097 spi_cmd_state = ST_EXPECT_MAGIC; 1098 spi_command = 0; 1099 spi_arg1 = 0; 1100 1101 return; 1102 } 1103 1104 void calculate_capacity_percentage() { 1105 if (capacity_accu_ampsecs <= capacity_min_ampsecs) { 1106 capacity_percentage = 0; 1107 } else if (capacity_accu_ampsecs >= capacity_max_ampsecs) { 1108 capacity_percentage = 100; 1109 } else { 1110 capacity_percentage = (int)(100.0*((float)(capacity_accu_ampsecs - capacity_min_ampsecs)) / (float)(capacity_max_ampsecs - capacity_min_ampsecs)); 1111 } 1112 } 1113 1114 void WDT_IRQHandler(void) { 1115 // Disable WDT interrupt 1116 NVIC_DisableIRQ(WDT_IRQn); 1117 NVIC_ClearPendingIRQ(WDT_IRQn); 1118 } 1119 1120 // WARNING: take care not to overflow TC (11786 * secs) 1121 void deep_sleep_seconds(int secs) { 1122 // brownout reset seems to interfere with deep sleep 1123 brownout_disable(); 1124 1125 // make WWDTINT wake the LPC up from sleep 1126 // STARTERP1 WWDTINT bit 12 1127 LPC_SYSCON->STARTERP1 |= (1 << 12); 1128 1129 NVIC_DisableIRQ(WDT_IRQn); 1130 1131 // Configure watchdog timer to wake us up 1132 LPC_SYSCON->SYSAHBCLKCTRL |= (1<<15); // WWDT enable 1133 1134 // 9375Hz?? 1135 LPC_SYSCON->WDTOSCCTRL = 1136 (1<<5) | // FREQSEL 0.6MHz 1137 31; // DIVSEL 64 (31+1)*2 1138 1139 // Power configuration register 1140 LPC_SYSCON->PDRUNCFG &= ~(1<<6); // WDTOSC_PD disable (power down disable) 1141 LPC_SYSCON->PDSLEEPCFG &= ~(1<<6); // WDTOSC_PD disable (power down disable) 1142 LPC_SYSCON->PDAWAKECFG = LPC_SYSCON->PDRUNCFG; // when waking up, power up the default blocks 1143 1144 LPC_WWDT->CLKSEL = 1; // WDOSC 1145 1146 //LPC_WWDT->TC = 0xffff/5; // timeout counter, ~5 seconds 1147 // FIXME isn't that 1.39s? (0xffff/5.0937/5.0) 1148 // no, apparently it is 5.56s (x4) 1149 1150 LPC_WWDT->TC = 11786 * secs; // timeout counter, ~1 second 1151 1152 LPC_WWDT->MOD = 0; 1153 //LPC_WWDT->MOD |= WWDT_WDMOD_WDRESET; // WDRESET (watchdog resets system) 1154 LPC_WWDT->MOD |= WWDT_WDMOD_WDEN; // watchdog enable 1155 1156 // counter value that triggers interrupt 1157 LPC_WWDT->WARNINT = 0; 1158 1159 // need to feed WD once to apply WDMOD values 1160 watchdog_feed(); 1161 1162 // NVIC exception 25 (WWDT) interrupt source: 1163 // ISER SETENA bit 25 -> 0xE000E100 1164 NVIC_ClearPendingIRQ(WDT_IRQn); 1165 NVIC_EnableIRQ(WDT_IRQn); 1166 1167 // Go to deep sleep mode 1168 LPC_PMU->PCON = 1<<11; // clear DPDFLAG 1169 LPC_PMU->PCON = 1; // select deep power down mode 1170 SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; 1171 __WFI(); 1172 1173 NVIC_DisableIRQ(WDT_IRQn); 1174 LPC_WWDT->MOD = 0; 1175 1176 brownout_enable(); 1177 } 1178 1179 int main(void) { 1180 boardInit(); 1181 reset_discharge_bits(); 1182 1183 state = ST_CHARGE; 1184 cycles_in_state = 0; 1185 cycles_uptime = 0; 1186 powersave_holdoff_cycles = POWERSAVE_HOLDOFF_CYCLES; 1187 1188 last_second = delayGetSecondsActive(); 1189 1190 int next_state = state; 1191 1192 while (1) { 1193 // algorithm idea: 1194 // - check all cell voltages 1195 // - charging state: if a cell voltage is higher than nominal, do not charge, enter balancing state 1196 // - option 1: suspend_charger (CONFIG_BITS_REG, 0x14, bit [5]) 1197 // - option 2: charge_current_setting (0x1A) to a low level 1198 // - charging state: if cell voltages are on average lower than nominal, enter charging state 1199 // - balancing state: discharge the cell with highest voltage (enable discharge switch) 1200 // - idle state: if one cell voltages is below undervoltage threshold, enter alert state 1201 // - alert state: constantly signal undervoltage alert to host. after timeout, switch off 5v rail and 3v3 rail 1202 1203 // charge current 2: ~0.2A 1204 1205 if (REFORM_MOTHERBOARD_REV >= REFORM_MBREV_25_R2) { 1206 measure_and_accumulate_current_v25(); 1207 } else { 1208 measure_and_accumulate_current(); 1209 } 1210 measure_cell_voltages_and_control_discharge(); 1211 calculate_capacity_percentage(); 1212 1213 if (state == ST_CHARGE) { 1214 reset_discharge_bits(); 1215 1216 if (force_sleep) { 1217 // debug sleeping 1218 if (powersave_holdoff_cycles <= 0) { 1219 next_state = ST_POWERSAVE; 1220 cycles_in_state = 0; 1221 } 1222 } else if (num_missing_cells >= 1) { 1223 if (cycles_in_state > 5) { 1224 missing_reason = num_missing_cells; 1225 // if cells were unplugged, we don't know the capacity anymore. 1226 reached_full_charge = 0; 1227 next_state = ST_MISSING; 1228 cycles_in_state = 0; 1229 } 1230 } 1231 else if (num_missing_cells == 0 && current >= 0 && num_undervolted_cells > 0) { 1232 // when transitioning to undervoltage, we assume we reached the bottom 1233 // of usable capacity, so record it 1234 // but only if we reached top charge once, or our counter will 1235 // be off. 1236 if (cycles_in_state > 5) { 1237 if (reached_full_charge > 0) { 1238 capacity_min_ampsecs = capacity_accu_ampsecs; 1239 } 1240 next_state = ST_UNDERVOLTED; 1241 cycles_in_state = 0; 1242 } 1243 } 1244 else if (current < 0.01 && current > FULLY_CHARGED_CURRENT && num_fully_charged_cells >= 8) { 1245 if (cycles_in_state > 5) { 1246 // when transitioning to fully charged, we assume that we're at max capacity 1247 capacity_accu_ampsecs = capacity_max_ampsecs; 1248 next_state = ST_FULLY_CHARGED; 1249 reached_full_charge = 1; 1250 cycles_in_state = 0; 1251 } 1252 } 1253 else if (num_overvolted_cells > 0) { 1254 if (cycles_in_state > 2) { 1255 // some cool-off time 1256 next_state = ST_OVERVOLTED; 1257 cycles_in_state = 0; 1258 } 1259 } 1260 else if (current < 0.05 && current >= 0 && !som_is_powered) { 1261 // if not charging and the system is off, we can sleep regularly to save power 1262 if (powersave_holdoff_cycles <= 0) { 1263 next_state = ST_POWERSAVE; 1264 cycles_in_state = 0; 1265 } 1266 } 1267 } 1268 else if (state == ST_UNDERVOLTED) { 1269 reset_discharge_bits(); 1270 deep_sleep_seconds(POWERSAVE_SLEEP_SECONDS); 1271 1272 // TODO: find safe heuristic. here we turn off if half 1273 // of the cells are undervolted and there's no wall power. 1274 if (volts < WALLPOWER_DETECT_VOLTAGE && (num_undervolted_critical_cells >= 1 || num_undervolted_cells >= 4)) { 1275 turn_som_power_off(); 1276 } 1277 1278 next_state = ST_CHARGE; 1279 cycles_in_state = 0; 1280 } 1281 else if (state == ST_OVERVOLTED) { 1282 discharge_overvolted_cells(); 1283 1284 // discharge 1285 if (cycles_in_state > 3 && (num_overvolted_cells==0 || num_undervolted_cells>0)) { 1286 reset_discharge_bits(); 1287 1288 next_state = ST_COOLDOWN; 1289 cycles_in_state = 0; 1290 } else if (cycles_in_state > 5) { 1291 // don't discharge for more than 5 cycles 1292 next_state = ST_COOLDOWN; 1293 cycles_in_state = 0; 1294 } 1295 } 1296 else if (state == ST_COOLDOWN) { 1297 // avoid overheating the resistors 1298 reset_discharge_bits(); 1299 1300 if (cycles_in_state > 3) { 1301 next_state = ST_CHARGE; 1302 cycles_in_state = 0; 1303 } 1304 } 1305 else if (state == ST_MISSING) { 1306 reset_discharge_bits(); 1307 1308 if (cycles_in_state > 5) { 1309 if (num_missing_cells == 0) { 1310 next_state = ST_CHARGE; 1311 cycles_in_state = 0; 1312 } 1313 } 1314 } 1315 else if (state == ST_FULLY_CHARGED) { 1316 reset_discharge_bits(); 1317 1318 if (cycles_in_state > 5) { 1319 // if none of the cells are fully charged anymore, allow charging again 1320 if (num_fully_charged_cells < 1) { 1321 next_state = ST_CHARGE; 1322 cycles_in_state = 0; 1323 } 1324 else if (num_overvolted_cells > 0) { 1325 next_state = ST_OVERVOLTED; 1326 cycles_in_state = 0; 1327 } 1328 } 1329 } 1330 else if (state == ST_POWERSAVE) { 1331 deep_sleep_seconds(POWERSAVE_SLEEP_SECONDS); 1332 next_state = ST_CHARGE; 1333 cycles_in_state = 0; 1334 } 1335 1336 // handle keyboard commands 1337 // this also resets powersave holdoff counter 1338 handle_commands(); 1339 1340 //TODO: if chip select high 1341 handle_spi_commands(); 1342 1343 if (state == ST_POWERSAVE || state == ST_UNDERVOLTED) { 1344 cur_second += POWERSAVE_SLEEP_SECONDS; 1345 } else { 1346 cur_second = delayGetSecondsActive(); 1347 } 1348 1349 if (last_second != cur_second) { 1350 if (cur_second-last_second<10) { 1351 // prevent rollovers 1352 cycles_in_state += cur_second-last_second; 1353 cycles_uptime += cur_second-last_second; 1354 if (cycles_in_state < 0) cycles_in_state = 0; 1355 if (cycles_uptime < 0) cycles_uptime = 0; 1356 } 1357 last_second = cur_second; 1358 1359 if (powersave_holdoff_cycles > 0) { 1360 powersave_holdoff_cycles--; 1361 } 1362 } 1363 1364 state = next_state; 1365 } 1366 } 1367 1368 #endif