reform

MNT Reform: Open Source Portable Computer
Log (Feed) | Files | Refs (Tags) | README

commit 833bf9ab2f214a07640eb959533e33fd8f5e56c4
parent 9e3269081a984b88bca97bc53699526c99c1693f
Author: mntmn <lukas@mntmn.com>
Date:   Thu, 25 Jun 2020 17:40:27 +0200

lpc-fw: implement min/max charge level tracking and gauge.

Diffstat:
Mreform2-lpc-fw/src/boards/reform2/board_reform2.c | 122++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 100 insertions(+), 22 deletions(-)

diff --git a/reform2-lpc-fw/src/boards/reform2/board_reform2.c b/reform2-lpc-fw/src/boards/reform2/board_reform2.c @@ -28,7 +28,7 @@ #include "protocol/protocol.h" #endif -//#define REF2_DEBUG 0 +//#define REF2_DEBUG 1 #define INA260_ADDRESS 0x4e #define LTC4162F_ADDRESS 0x68 @@ -117,12 +117,13 @@ uint8_t calc_pec(uint8_t d, uint8_t pec) { return pec; } - +// main charger state machine enum state_t { ST_CHARGE, ST_OVERVOLTED, ST_UNDERVOLTED, - ST_MISSING + ST_MISSING, + ST_FULLY_CHARGED }; // charging state machine @@ -132,15 +133,21 @@ int charge_current = 1; uint32_t cur_second = 0; uint32_t last_second = 0; -float ampSecs = 10*3600.0; +// 1.8A x 3600 seconds/hour +#define MAX_CAPACITY (1.8)*3600.0 +float capacity_max_ampsecs = MAX_CAPACITY; +float capacity_accu_ampsecs = MAX_CAPACITY; +float capacity_min_ampsecs = 430*3.6; // TODO save this in flash after learning float volts = 0; float current = 0; unsigned long lastTime = 0; char uartBuffer[255] = {0}; float cells_v[8] = {0,0,0,0,0,0,0,0}; int num_undervolted_cells = 0; +int num_undervolted_critical_cells = 0; int num_overvolted_cells = 0; int num_missing_cells = 0; +uint8_t reached_full_charge = 0; uint16_t discharge_bits = 0; uint16_t overvoltage_bits = 0; uint16_t undervoltage_bits = 0; @@ -149,8 +156,10 @@ uint8_t spir[64]; #define OVERVOLTAGE_START_VALUE 3.61 #define OVERVOLTAGE_STOP_VALUE 3.55 -#define UNDERVOLTAGE_VALUE 2.6 +#define UNDERVOLTAGE_VALUE 2.55 +#define UNDERVOLTAGE_CRITICAL_VALUE 2.4 #define MISSING_VALUE 5 +#define FULLY_CHARGED_VOLTAGE (3.5*8.0) void measure_cell_voltages_and_control_discharge() { // delay is measured in "ticks", which are basically ms? @@ -171,7 +180,8 @@ void measure_cell_voltages_and_control_discharge() { // we're in normal charge mode, so remove charge current limit LPC_GPIO->SET[1] |= (1 << 25); } else { - // we're discharging (balancing), so limit charge current + // we're discharging (balancing), or full charged, + // so limit charge current // also don't charge if we have missing cells LPC_GPIO->CLR[1] |= (1 << 25); } @@ -221,6 +231,7 @@ void measure_cell_voltages_and_control_discharge() { num_missing_cells = 0; num_undervolted_cells = 0; num_overvolted_cells = 0; + num_undervolted_critical_cells = 0; missing_bits = 0; undervoltage_bits = 0; @@ -238,6 +249,10 @@ void measure_cell_voltages_and_control_discharge() { else if (cells_v[i] < UNDERVOLTAGE_VALUE) { undervoltage_bits |= (1<<i); num_undervolted_cells++; + + if (cells_v[i] < UNDERVOLTAGE_CRITICAL_VALUE) { + num_undervolted_critical_cells++; + } } } @@ -285,7 +300,7 @@ void measure_and_accumulate_current() { lastTime = thisTime; // decrease estimated battery capacity - ampSecs -= current*(secondsPassed); + capacity_accu_ampsecs -= current*(secondsPassed); } } else { // timer uninitialized or timer wrap @@ -293,7 +308,7 @@ void measure_and_accumulate_current() { } #ifdef REF2_DEBUG - sprintf(uartBuffer,"\033[H\033[2JINA Ah: %f V: %f A: %f\r\n",ampSecs/3600,volts,current); + sprintf(uartBuffer,"\033[H\033[2JINA Ah: %f V: %f A: %f\r\n",capacity_accu_ampsecs/3600,volts,current); uartSend((uint8_t *)uartBuffer, strlen(uartBuffer)); #endif } @@ -311,17 +326,19 @@ void configure_charger(int charge_current) { } void turn_som_power_on(void) { + LPC_GPIO->CLR[1] = (1 << 28); // hold in reset LPC_GPIO->SET[1] = (1 << 16); // 3v3, high = on - // FIXME this turns 1v5 off :/ - LPC_GPIO->SET[0] = (1 << 20); // PCIe, low = on + LPC_GPIO->SET[0] = (1 << 20); // PCIe, high = on LPC_GPIO->SET[1] = (1 << 15); // 5v, high = on LPC_GPIO->SET[1] = (1 << 19); // 1v2, high = on + LPC_GPIO->SET[1] = (1 << 28); // release reset } void turn_som_power_off(void) { + LPC_GPIO->CLR[1] = (1 << 28); // hold in reset LPC_GPIO->CLR[1] = (1 << 19); // 1v2, high = on LPC_GPIO->CLR[1] = (1 << 15); // 5v, high = on - LPC_GPIO->CLR[0] = (1 << 20); // PCIe, low = on + LPC_GPIO->CLR[0] = (1 << 20); // PCIe, high = on LPC_GPIO->CLR[1] = (1 << 16); // 3v3, high = on // FIXME experiment: temp. disable charger to reset its timers @@ -375,14 +392,20 @@ void boardInit(void) // 1V2 regulator on/off LPC_GPIO->DIR[1] |= (1 << 19); // PCIe 1 power supply transistor - LPC_GPIO->DIR[0] |= (1 << 20); + LPC_GPIO->DIR[0] |= (1 << 20); + + // IMX Wake + LPC_GPIO->DIR[1] |= (1 << 24); + // IMX Reset + LPC_GPIO->DIR[1] |= (1 << 28); + // RNG/SS pin of LTC4020: control/limit charge current LPC_GPIO->DIR[1] |= (1 << 25); // start with low charge current LPC_GPIO->CLR[1] |= (1 << 25); - turn_som_power_on(); + turn_som_power_off(); uartInit(CFG_UART_BAUDRATE); i2cInit(I2CMASTER); @@ -395,7 +418,7 @@ void boardInit(void) // SPI chip select LPC_GPIO->DIR[1] |= (1 << 23); LPC_GPIO->SET[1] = (1 << 23); // active low - + #ifdef REF2_DEBUG sprintf(uartBuffer, "\r\nMNT Reform 2.0 MCU initialized.\r\n"); uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); @@ -517,8 +540,10 @@ void handle_commands() { sprintf(uartBuffer,"undervolt [%d]\r",cycles_in_state); } else if (state == ST_MISSING) { sprintf(uartBuffer,"cell missing [%d]\r",cycles_in_state); - } else if (state == ST_MISSING) { - sprintf(uartBuffer,"unknown [%d]\r",cycles_in_state); + } else if (state == ST_FULLY_CHARGED) { + sprintf(uartBuffer,"fully charged [%d]\r",cycles_in_state); + } else { + sprintf(uartBuffer,"unknown %d [%d]\r",state,cycles_in_state); } uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); } @@ -539,13 +564,35 @@ void handle_commands() { uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); } else if (remote_cmd == 'C') { - // set/get battery capacity (mAh) - if (cmd_number>0) { - ampSecs = ((float)cmd_number)*3.6; - } - sprintf(uartBuffer,"%d\r\n",(int)(ampSecs/3.6)); + // get battery capacity (mAh) + sprintf(uartBuffer,"%d/%d/%d\r\n", + (int)(capacity_accu_ampsecs/3.6), + (int)(capacity_min_ampsecs/3.6), + (int)(capacity_max_ampsecs/3.6)); uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); } + else if (remote_cmd == 'g') { + // get fuel gauge (percent) + if (reached_full_charge > 0) { + int percentage = 0; + if (capacity_accu_ampsecs <= capacity_min_ampsecs) { + percentage = 0; + } else if (capacity_accu_ampsecs >= capacity_max_ampsecs) { + percentage = 100; + } else { + percentage = (int)(100.0*((float)capacity_accu_ampsecs - (float)capacity_min_ampsecs) / (float)capacity_max_ampsecs); + } + + sprintf(uartBuffer,"%d%%\r\n", percentage); + uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); + } else { + // if we never reached full charge, + // we don't really know where we are. + + sprintf(uartBuffer,"? %%\r\n"); + uartSend((uint8_t*)uartBuffer, strlen(uartBuffer)); + } + } else if (remote_cmd == 'e') { // toggle serial echo cmd_echo = cmd_number?1:0; @@ -604,9 +651,18 @@ int main(void) // some cool-off time if (num_missing_cells > 0) { state = ST_MISSING; + // if cells were unplugged, we don't know the capacity anymore. + reached_full_charge = 0; cycles_in_state = 0; } else if (num_undervolted_cells > 0) { + // when transitioning to undervoltage, we assume we reached the bottom + // of usable capacity, so record it + // but only if we reached top charge once, or our counter will + // be off. + if (reached_full_charge > 0) { + capacity_min_ampsecs = capacity_accu_ampsecs; + } state = ST_UNDERVOLTED; cycles_in_state = 0; } @@ -614,13 +670,25 @@ int main(void) state = ST_OVERVOLTED; cycles_in_state = 0; } + else if (volts >= FULLY_CHARGED_VOLTAGE) { + // when transitioning to fully charged, we assume that we're at max capacity + capacity_accu_ampsecs = capacity_max_ampsecs; + state = ST_FULLY_CHARGED; + reached_full_charge = 1; + cycles_in_state = 0; + } } } else if (state == ST_UNDERVOLTED) { // TODO: issue alert -- switch off system if critical reset_discharge_bits(); - turn_som_power_off(); + // TODO: find safe heuristic. here we turn off if half + // of the cells are undervolted. + if (num_undervolted_critical_cells > 0 || num_undervolted_cells > 3) { + turn_som_power_off(); + } + if (cycles_in_state > 5) { state = ST_CHARGE; cycles_in_state = 0; @@ -646,6 +714,16 @@ int main(void) } } } + else if (state == ST_FULLY_CHARGED) { + // fully charged. resume charging once we're 0.2mV + // below fully charged state + if (cycles_in_state > 5) { + if (volts < (FULLY_CHARGED_VOLTAGE - 0.2)) { + state = ST_CHARGE; + cycles_in_state = 0; + } + } + } handle_commands(); cur_second = delayGetSecondsActive();