Browse Source

WIP first implementation of command channel for battery monitoring

master
mntmn 9 months ago
parent
commit
f0d63e9434
1 changed files with 654 additions and 113 deletions
  1. +654
    -113
      src/boards/reform2/board_reform2.c

+ 654
- 113
src/boards/reform2/board_reform2.c View File

@@ -4,18 +4,14 @@

#include <string.h> /* strlen */

#include "cmsis_os.h"
#include "boards/board.h"
#include "core/gpio/gpio.h"
#include "core/delay/delay.h"
#include "core/eeprom/eeprom.h"
#include "core/pmu/pmu.h"

#ifdef CFG_CHIBI
#include "messages.h"
#include "drivers/rf/802.15.4/chibi/chb.h"
#include "drivers/rf/802.15.4/chibi/chb_drvr.h"
#endif
#include "core/i2c/i2c.h"
#include "core/ssp1/ssp1.h"
#include "core/uart/uart.h"

#ifdef CFG_USB
#include "core/usb/usbd.h"
@@ -24,10 +20,6 @@
#endif
#endif

#ifdef CFG_TFTLCD
#include "drivers/displays/graphic/lcd.h"
#endif

#ifdef CFG_INTERFACE
#include "cli/cli.h"
#endif
@@ -36,134 +28,683 @@
#include "protocol/protocol.h"
#endif

#ifdef CFG_ENABLE_UART
#include "core/uart/uart.h"
//#define REF2_DEBUG

#define INA260_ADDRESS 0x4e
#define LTC4162F_ADDRESS 0x68
#define I2C_READ 1

#define ST_EXPECT_DIGIT_0 0
#define ST_EXPECT_DIGIT_1 1
#define ST_EXPECT_DIGIT_2 2
#define ST_EXPECT_DIGIT_3 3
#define ST_EXPECT_CMD 4
#define ST_SYNTAX_ERROR 5
#define ST_EXPECT_RETURN 6

extern volatile uint8_t I2CMasterBuffer[I2C_BUFSIZE];
extern volatile uint8_t I2CSlaveBuffer[I2C_BUFSIZE];
extern volatile uint32_t I2CReadLength, I2CWriteLength;

err_t i2c_write8(uint8_t i2c_addr, uint8_t reg, uint8_t value)
{
I2CWriteLength = 3;
I2CReadLength = 0;
I2CMasterBuffer[0] = i2c_addr << 1;
I2CMasterBuffer[1] = reg;
I2CMasterBuffer[2] = value;
i2cEngine();

return ERROR_NONE;
}

int16_t i2c_read16_le(uint8_t i2c_addr, uint8_t reg)
{
I2CWriteLength = 2;
I2CReadLength = 2;
I2CMasterBuffer[0] = i2c_addr << 1;
I2CMasterBuffer[1] = reg;
I2CMasterBuffer[2] = (i2c_addr << 1) | I2C_READ;
i2cEngine();

int16_t value = (I2CSlaveBuffer[0] << 8) | I2CSlaveBuffer[1];
return value;
}

int16_t i2c_read16_be(uint8_t i2c_addr, uint8_t reg)
{
I2CWriteLength = 2;
I2CReadLength = 2;
I2CMasterBuffer[0] = i2c_addr << 1;
I2CMasterBuffer[1] = reg;
I2CMasterBuffer[2] = (i2c_addr << 1) | I2C_READ;
i2cEngine();

int16_t value = (I2CSlaveBuffer[1] << 8) | I2CSlaveBuffer[0];
return value;
}

err_t i2c_write16_be(uint8_t i2c_addr, uint8_t reg, uint16_t value)
{
I2CWriteLength = 4;
I2CReadLength = 0;
I2CMasterBuffer[0] = i2c_addr << 1;
I2CMasterBuffer[1] = reg;
I2CMasterBuffer[2] = value&0xff;
I2CMasterBuffer[3] = value>>8;
i2cEngine();

return ERROR_NONE;
}

// see https://www.analog.com/media/en/technical-documentation/data-sheets/680324fa.pdf
// default = 0x41
uint8_t calc_pec(uint8_t d, uint8_t pec) {
//uint8_t pec = 0x41; // 0100 0001

for (int i=0; i<8; i++) {
uint8_t bit = (d>>7)&1; // pop off upper bit
d = d<<1;
uint8_t in0 = bit ^ (pec>>7);
uint8_t in1 = (pec & 1) ^ in0;
uint8_t in2 = ((pec & 2)>>1) ^ in0;
pec = (pec<<1)&0xf8;
pec |= in2<<2;
pec |= in1<<1;
pec |= in0;
}

return pec;
}


enum state_t {
ST_CHARGE,
ST_OVERVOLTED,
ST_UNDERVOLTED,
ST_MISSING
};

// charging state machine
int state = ST_CHARGE;
int cycles_in_state = 0;
int charge_current = 2;
uint32_t cur_second = 0;
uint32_t last_second = 0;

float ampSecs = 10*3600.0;
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_overvolted_cells = 0;
int num_missing_cells = 0;
uint16_t discharge_bits = 0;
uint16_t overvoltage_bits = 0;
uint16_t undervoltage_bits = 0;
uint16_t missing_bits = 0;

#define OVERVOLTAGE_VALUE 3.5
#define UNDERVOLTAGE_VALUE 2.7
#define MISSING_VALUE 5

void measure_cell_voltages_and_control_discharge() {
uint8_t spir[64];

// delay is measured in "ticks", which are basically ms?

// 0x10: WRCFG, write config
spir[0] = 0x01;
spir[1] = 0xc7;

spir[2] = 0xe1; // set cdc to 2 = continuous measurement
spir[3] = discharge_bits&0xff; // first 8 bits of discharge switches
spir[4] = (discharge_bits&0x700)>>8; // last 4 bits of discharge switches
spir[5] = 0x0;
spir[6] = 0x0;
spir[7] = 0x0;

uint8_t pec = 0x41;
for (int i=2; i<=7; i++) {
pec = calc_pec(spir[i], pec);
}
spir[8] = pec;
LPC_GPIO->CLR[1] = (1 << 23);
//delay(2);
ssp1Send(spir, 9);
//delay(2);
LPC_GPIO->SET[1] = (1 << 23);

// 0x10: STCVDC, start adc
spir[0] = 0x60;
spir[1] = 0xe7;
LPC_GPIO->CLR[1] = (1 << 23);
//delay(2);
ssp1Send(spir, 2);
//delay(2);
LPC_GPIO->SET[1] = (1 << 23);

delay(50); // FIXME tunable

// 0x4: PLADC, poll adc converter status
/*spir[0] = 0x40;
spir[1] = 0x07;
LPC_GPIO->CLR[1] = (1 << 23);
delay(2);
ssp1Send(spir, 2);
memset(spir, 0, 32);
ssp1Receive(spir, 2);
delay(2);
LPC_GPIO->SET[1] = (1 << 23);
sprintf(uartBuffer,"PLADC %02x %02x\r\n",
spir[0], spir[1]);
uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));*/

//delay(20);
// 0x4: RDCV, read all cell voltages
spir[0] = 0x4;
spir[1] = 0xdc;
LPC_GPIO->CLR[1] = (1 << 23);
//delay(2);
ssp1Send(spir, 2);
memset(spir, 0, 32);
ssp1Receive(spir, 19);
//delay(2);
LPC_GPIO->SET[1] = (1 << 23);

/*sprintf(uartBuffer,"CVR00 %02x %02x %02x %02x %02x %02x %02x %02x\r\n",
spir[0], spir[1], spir[2], spir[3], spir[4], spir[5], spir[6], spir[7]);
uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));
sprintf(uartBuffer,"CVR08 %02x %02x %02x %02x %02x %02x %02x %02x\r\n",
spir[8], spir[9], spir[10], spir[11], spir[12], spir[13], spir[14], spir[15]);
uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));
sprintf(uartBuffer,"CVR10 %02x %02x %02x %02x %02x %02x %02x %02x\r\n\r\n",
spir[16], spir[17], spir[18], spir[19], spir[20], spir[21], spir[22], spir[23]);
uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));*/

int j=0;
for (int i=0; i<8; i+=2) {
cells_v[i] = ((float)((spir[j]|((spir[j+1]&0xf)<<8))-512)) * 1.5 / 1000.0;
cells_v[i+1] = ((float)((spir[j+1]&0xf0)>>4|(spir[j+2]<<4))-512) * 1.5 / 1000.0;
j+=3;

#ifdef REF2_DEBUG
sprintf(uartBuffer,"cell %d: %fV cell %d: %fV\r\n",
i, cells_v[i], i+1, cells_v[i+1]);
uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));
#endif
}

num_missing_cells = 0;
num_undervolted_cells = 0;
num_overvolted_cells = 0;
missing_bits = 0;
undervoltage_bits = 0;
overvoltage_bits = 0;
for (int i=0; i<8; i++) {
if (cells_v[i] >= MISSING_VALUE || cells_v[i]<0.5) {
missing_bits |= (1<<i);
num_missing_cells++;
}
else if (cells_v[i] >= OVERVOLTAGE_VALUE) {
overvoltage_bits |= (1<<i);
num_overvolted_cells++;
}
else if (cells_v[i] <= UNDERVOLTAGE_VALUE) {
undervoltage_bits |= (1<<i);
num_undervolted_cells++;
}
}
// 0x2: RDCFG, read config registers
/*spir[0] = 0x2;
spir[1] = 0xce;
LPC_GPIO->CLR[1] = (1 << 23);
delay(2);
ssp1Send(spir, 2);
memset(spir, 0, 32);
ssp1Receive(spir, 7);
delay(2);
LPC_GPIO->SET[1] = (1 << 23);

sprintf(uartBuffer,"CFG00 %02x %02x %02x %02x %02x %02x %02x %02x\r\n",
spir[0], spir[1], spir[2], spir[3], spir[4], spir[5], spir[6], spir[7]);
uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));*/
}

void discharge_overvolted_cells() {
discharge_bits = overvoltage_bits;
}

void reset_discharge_bits() {
discharge_bits = 0;
}

// using INA260 current monitor, count amp-secs going in and out of battery
// (battery gauge)
void measure_and_accumulate_current() {
float raw_volts = (float)i2c_read16_le(INA260_ADDRESS, 0x2);
float raw_current = (float)i2c_read16_le(INA260_ADDRESS, 0x1);

volts = raw_volts * 0.00125;
current = raw_current * 0.001;

if (current>-0.02 && current<0.02) current = 0; // clamp to zero

unsigned long thisTime = delayGetSecondsActive();
if (lastTime>0 && thisTime>lastTime) {
unsigned long secondsPassed = thisTime - lastTime;

if (secondsPassed >= 1) {
lastTime = thisTime;
// decrease estimated battery capacity
ampSecs -= current*(secondsPassed);
}
} else {
// timer uninitialized or timer wrap
lastTime = thisTime;
}

#ifdef CFG_CMSIS_RTOS
#include "RTX_CM_lib.h"
#ifdef REF2_DEBUG
sprintf(uartBuffer,"\033[H\033[2JINA Ah: %f V: %f A: %f\r\n",ampSecs/3600,volts,current);
uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));
#endif
}

uint16_t charger_state;
uint16_t charge_status;
uint16_t system_status;

void configure_charger(int charge_current) {
// disable jeita
i2c_write16_be(LTC4162F_ADDRESS, 0x29, 0);

// set charge current
i2c_write16_be(LTC4162F_ADDRESS, 0x1a, charge_current);
// set charge voltage
i2c_write16_be(LTC4162F_ADDRESS, 0x1b, 31); // default

if (charge_current > 0) {
// config_bits_reg: unsuspend charger
i2c_write16_be(LTC4162F_ADDRESS, 0x14, 0);
} else {
// config_bits_reg: suspend charger
i2c_write16_be(LTC4162F_ADDRESS, 0x14, 1<<5);
}
// set recharge threshold
i2c_write16_be(LTC4162F_ADDRESS, 0x2e, 40000);
// read some charger regs
uint16_t vcharge_setting = i2c_read16_be(LTC4162F_ADDRESS, 0x1b) & 0x1f; // [4:0]
charger_state = i2c_read16_be(LTC4162F_ADDRESS, 0x34);
charge_status = i2c_read16_be(LTC4162F_ADDRESS, 0x35);
system_status = i2c_read16_be(LTC4162F_ADDRESS, 0x39);
uint16_t charger_config = i2c_read16_be(LTC4162F_ADDRESS, 0x29);
float chg_vbat = i2c_read16_be(LTC4162F_ADDRESS, 0x3a);
float chg_vin = i2c_read16_be(LTC4162F_ADDRESS, 0x3b);
float chg_vout = i2c_read16_be(LTC4162F_ADDRESS, 0x3c);
float chg_ibat = i2c_read16_be(LTC4162F_ADDRESS, 0x3d);
float chg_iin = i2c_read16_be(LTC4162F_ADDRESS, 0x3e);
int16_t chg_chem = (i2c_read16_be(LTC4162F_ADDRESS, 0x43) >> 8) & 0xf;
int16_t chg_cell_count = (i2c_read16_be(LTC4162F_ADDRESS, 0x43)) & 0xf;

uint16_t tchargetimer = i2c_read16_be(LTC4162F_ADDRESS, 0x30);
uint16_t tcvtimer = i2c_read16_be(LTC4162F_ADDRESS, 0x31);
uint16_t tabsorbtimer = i2c_read16_be(LTC4162F_ADDRESS, 0x32);
chg_vin*=1.649;
chg_vbat*=(8.0*0.1924);
chg_vout*=1.653;

#ifdef CFG_CC3000
#include "drivers/rf/wifi/cc3000/wifi.h"
#ifdef REF2_DEBUG
sprintf(uartBuffer,"ltc state: %x status: %x system: %x\r\n",charger_state,charge_status,system_status);
uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));
#endif
sprintf(uartBuffer,"ltc vbat: %f vin: %f vout: %f\r\n",chg_vbat,chg_vin,chg_vout);
//uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));
sprintf(uartBuffer,"ltc chemistry: %d cell count: %d\r\n",chg_chem,chg_cell_count);
//uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));

sprintf(uartBuffer,"config: jeita: %d thermal_reg: %d ilim_reg: %d vin_uvcl: %d iin_limit: %d\r\n",charger_config&1, !!(charge_status&16), !!(charge_status&32), !!(charge_status&8), !!(charge_status&4));
//uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));
sprintf(uartBuffer,"tchgt: %d tcvt: %d tabsorbt: %d\r\n\r\n",tchargetimer,tcvtimer,tabsorbtimer);
//uartSend((uint8_t *)uartBuffer, strlen(uartBuffer));
}

void turn_som_power_on(void) {
LPC_GPIO->SET[1] = (1 << 15); // high = on
LPC_GPIO->CLR[0] = (1 << 20); // low = on
}

void turn_som_power_off(void) {
LPC_GPIO->CLR[1] = (1 << 15); // high = on
LPC_GPIO->SET[0] = (1 << 20); // low = on
}

void watchdog_feed(void) {
LPC_WWDT->FEED = 0xAA;
LPC_WWDT->FEED = 0x55;
}

void watchdog_setup(void) {
LPC_SYSCON->WDTOSCCTRL =
(0x1<<5) | // FREQSEL 0.6MHz
0; // DIVSEL 2 (0+1)*2
LPC_WWDT->CLKSEL = 1; // WDOSC
LPC_SYSCON->PDRUNCFG &= ~(6<<15); // WDTOSC_PD disable

LPC_SYSCON->SYSAHBCLKCTRL |= (1<<15); // WWDT enable
LPC_WWDT->COUNT = 0xffff; // timeout counter
LPC_WWDT->MOD |= (1<<1); // enable WDRESET (watchdog resets system)
LPC_WWDT->MOD |= 1; // watchdog enable
}

/**************************************************************************/
/*!
@brief Board-specific initialisation function
*/
/**************************************************************************/
void boardInit(void)
{
SystemCoreClockUpdate();
delayInit();
GPIOInit();

#ifdef CFG_PRINTF_UART
uartInit(CFG_UART_BAUDRATE);
#endif
uartInit(CFG_UART_BAUDRATE);
i2cInit(I2CMASTER);
ssp1Init();
ssp1ClockSlow();
LPC_GPIO->DIR[1] |= (1 << 31);
LPC_GPIO->DIR[1] |= (1 << 25);

/* Set user LED pin to output and disable it */
//LPC_GPIO->DIR[CFG_LED_PORT] |= (1 << CFG_LED_PIN);
//boardLED(CFG_LED_OFF);
// SPI chip select
LPC_GPIO->DIR[1] |= (1 << 23);
LPC_GPIO->SET[1] = (1 << 23); // active low

/* Start the command line interface */
#ifdef CFG_INTERFACE
cliInit();
#endif
// 5V regulator on/off
LPC_GPIO->DIR[1] |= (1 << 15);
turn_som_power_on(); // FIXME
//LPC_GPIO->SET[1] = (1 << 15); // high = on

// FIXME
// 3V3 rail transistor on/off
LPC_GPIO->DIR[1] |= (1 << 16);
LPC_GPIO->CLR[1] = (1 << 16); // low = on

// PCIe 1 power supply transistor
LPC_GPIO->DIR[0] |= (1 << 20);
LPC_GPIO->CLR[0] = (1 << 20); // low = on

sprintf(uartBuffer, "\r\nMNT Reform 2.0 MCU initialized.\r\n");
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}

#ifndef _TEST_
/**************************************************************************/
/*!
@brief Primary (non-RTOS!) entry point for this project.
*/
/**************************************************************************/
volatile uint32_t test[8] = { 0 };
int main(void)
{
uint32_t currentSecond, lastSecond;
currentSecond = lastSecond = 0;
char remote_cmd = 0;
uint8_t remote_arg = 0;
unsigned char cmd_state = ST_EXPECT_DIGIT_0;
unsigned int cmd_number = 0;
int cmd_echo = 0;

/* Configure the HW */
boardInit();
void handle_commands() {
if (!uartRxBufferDataPending()) return;

while (1)
{
/* Blinky (1Hz) */
currentSecond = delayGetSecondsActive();
if (currentSecond != lastSecond)
{
lastSecond = currentSecond;
//boardLED(lastSecond % 2);
}
char chr = uartRxBufferRead();

/* Check for binary protocol input if CFG_PROTOCOL is enabled */
//#ifdef CFG_PROTOCOL
// prot_task(NULL);
//#endif

/* Poll for CLI input if CFG_INTERFACE is enabled */
#ifdef CFG_INTERFACE
cliPoll();
#endif

// if ( usb_custom_is_ready_to_send() )
// {
// static uint32_t magic_number = 0;
// uint32_t buffer[2][16]; // 2x64 byte in size
// buffer[0][0] = magic_number++;
// buffer[1][0] = magic_number++;
// usb_custom_send(buffer, 64*2);
// }
//
// if (custom_recv_magic_number != 0)
// {
// printf("%d\n", custom_recv_magic_number);
// custom_recv_magic_number = 0;
// }

/* Optionally enter high level sleep mode here via WFI */
if (cmd_echo) {
sprintf(uartBuffer, "%c", chr);
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
}
#endif /* !_TEST_ */

/**************************************************************************/
/*!
@brief Turns the LED(s) on or off
*/
/**************************************************************************/
void boardLED(uint8_t state)
{
if (state)
{
LPC_GPIO->SET[CFG_LED_PORT] = (1 << CFG_LED_PIN);
// states:
// 0-3 digits of optional command argument
// 4 command letter expected
// 5 syntax error (unexpected character)
// 6 command letter entered

// TODO get cell voltage
// TODO get charger state
// TODO get charger status / mode
if (cmd_state>=ST_EXPECT_DIGIT_0 && cmd_state<=ST_EXPECT_DIGIT_3) {
// read number or command
if (chr >= '0' && chr <= '9') {
cmd_number*=10;
cmd_number+=(chr-'0');
cmd_state++;
} else if ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z')) {
// command entered instead of digit
remote_cmd = chr;
cmd_state = ST_EXPECT_RETURN;
} else if (chr == '\n' || chr == ' ') {
// ignore newlines or spaces
} else if (chr == '\r') {
sprintf(uartBuffer, "error:syntax\r\n");
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
cmd_state = ST_EXPECT_DIGIT_0;
cmd_number = 0;
} else {
// syntax error
cmd_state = ST_SYNTAX_ERROR;
}
}
else
{
LPC_GPIO->CLR[CFG_LED_PORT] = (1 << CFG_LED_PIN);
else if (cmd_state == ST_EXPECT_CMD) {
// read command
if ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z')) {
remote_cmd = chr;
cmd_state = ST_EXPECT_RETURN;
} else {
cmd_state = ST_SYNTAX_ERROR;
}
}
else if (cmd_state == ST_SYNTAX_ERROR) {
// syntax error
if (chr == '\r') {
sprintf(uartBuffer, "error:syntax\r\n");
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
cmd_state = ST_EXPECT_DIGIT_0;
cmd_number = 0;
}
}
else if (cmd_state == ST_EXPECT_RETURN) {
if (chr == '\n' || chr == ' ') {
// ignore newlines or spaces
}
else if (chr == '\r') {
if (cmd_echo) {
// FIXME
sprintf(uartBuffer,"\n");
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
// execute
if (remote_cmd == 'p') {
// toggle system 5V power
if (cmd_number == 0) {
turn_som_power_off();
} else {
turn_som_power_on();
}
}
else if (remote_cmd == 'a') {
// get system current (mA)
sprintf(uartBuffer,"%d\r\n",(int)(current*1000.0));
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
else if (remote_cmd == 'v' && cmd_number>=0 && cmd_number<=7) {
// get cell voltage
sprintf(uartBuffer,"%d\r\n",(int)(cells_v[cmd_number]*1000.0));
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
else if (remote_cmd == 'V') {
// get system voltage
sprintf(uartBuffer,"%d\r\n",(int)(volts*1000.0));
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
else if (remote_cmd == 's') {
// get charger system state
if (state == ST_CHARGE) {
sprintf(uartBuffer,"idle: %x st: %x sy: %x [%d]\r",charger_state,charge_status,system_status,cycles_in_state);
} else if (state == ST_OVERVOLTED) {
sprintf(uartBuffer,"overvolt [%d]\r",cycles_in_state);
} else if (state == ST_UNDERVOLTED) {
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);
}
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
else if (remote_cmd == 'c' && cmd_number>=0 && cmd_number<=7) {
// get cell status
int n = cmd_number;
sprintf(uartBuffer,"%c%c%c%c\r\n",
missing_bits &(1<<n)?'m':'.',
undervoltage_bits &(1<<n)?'u':'.',
overvoltage_bits &(1<<n)?'o':'.',
discharge_bits &(1<<n)?'d':'.');
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
else if (remote_cmd == 'S') {
// get charger system cycles in current state
sprintf(uartBuffer, "%d\r\n", cycles_in_state);
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));
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
else if (remote_cmd == 'e') {
// toggle serial echo
cmd_echo = cmd_number?1:0;
}
else {
sprintf(uartBuffer, "error:command\r\n");
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
cmd_state = ST_EXPECT_DIGIT_0;
cmd_number = 0;
} else {
cmd_state = ST_SYNTAX_ERROR;
}
}
}

/**************************************************************************/
/*!
@brief Configure the board for low power and enter sleep mode
*/
/**************************************************************************/
void boardSleep(void)
int main(void)
{
// ToDo!
}
boardInit();
reset_discharge_bits();
state = ST_CHARGE;
cycles_in_state = 0;

/**************************************************************************/
/*!
@brief Restores parts and system peripherals to an appropriate
state after waking up from sleep mode
*/
/**************************************************************************/
void boardWakeup(void)
{
// ToDo!
delay(500);

last_second = delayGetSecondsActive();

// WIP, not yet tested
//watchdog_setup();
while (1)
{
// algorithm idea:
// - check all cell voltages
// - charging state: if a cell voltage is higher than nominal, do not charge, enter balancing state
// - option 1: suspend_charger (CONFIG_BITS_REG, 0x14, bit [5])
// - option 2: charge_current_setting (0x1A) to a low level
// - charging state: if cell voltages are on average lower than nominal, enter charging state
// - balancing state: discharge the cell with highest voltage (enable discharge switch)
// - idle state: if one cell voltages is below undervoltage threshold, enter alert state
// - alert state: constantly signal undervoltage alert to host. after timeout, switch off 5v rail and 3v3 rail

// charge current 2: ~0.2A

//watchdog_feed();

measure_and_accumulate_current();
measure_cell_voltages_and_control_discharge();

if (state == ST_CHARGE) {
configure_charger(charge_current);
if (cycles_in_state > 5) {
// some cool-off time
if (num_missing_cells > 0) {
//state = ST_MISSING;
//cycles_in_state = 0;
}
else if (num_undervolted_cells > 0) {
state = ST_UNDERVOLTED;
cycles_in_state = 0;
}
else if (num_overvolted_cells > 0) {
state = ST_OVERVOLTED;
cycles_in_state = 0;
}
}
}
else if (state == ST_UNDERVOLTED) {
// TODO: issue alert -- switch off system if critical
// TODO: how to get out of this state?
configure_charger(charge_current);

if (cycles_in_state > 5) {
state = ST_CHARGE;
cycles_in_state = 0;
}
}
else if (state == ST_OVERVOLTED) {
// suspend charger
configure_charger(0);
discharge_overvolted_cells();

// FIXME optimize cycles
if (cycles_in_state > 5 && (num_undervolted_cells==0 || num_undervolted_cells>0)) {
reset_discharge_bits();
state = ST_CHARGE;
cycles_in_state = 0;
}
}
else if (state == ST_MISSING) {
configure_charger(0);
reset_discharge_bits();
if (cycles_in_state > 5) {
if (num_missing_cells < 1) {
state = ST_CHARGE;
cycles_in_state = 0;
}
}
}
handle_commands();
cur_second = delayGetSecondsActive();

if (last_second != cur_second) {
if (cur_second-last_second<10) {
// prevent rollovers
cycles_in_state += cur_second-last_second;
}
last_second = cur_second;
}
}
}

#endif /* CFG_BRD_LPCXPRESSO_LPC1347 */
#endif

Loading…
Cancel
Save