Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Electron] Power management #1412

Merged
merged 9 commits into from
Nov 6, 2017
10 changes: 7 additions & 3 deletions hal/src/stm32f2xx/concurrent_hal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,14 @@ static_assert(portMAX_DELAY==CONCURRENT_WAIT_FOREVER, "expected portMAX_DELAY==C

int os_queue_put(os_queue_t queue, const void* item, system_tick_t delay, void*)
{
if (HAL_IsISR())
return xQueueSendFromISR(queue, item, nullptr)!=pdTRUE;
else
if (HAL_IsISR()) {
BaseType_t woken = pdFALSE;
int res = xQueueSendFromISR(queue, item, &woken) != pdTRUE;
portYIELD_FROM_ISR(woken);
return res;
} else {
return xQueueSend(queue, item, delay)!=pdTRUE;
}
}

int os_queue_take(os_queue_t queue, void* item, system_tick_t delay, void*)
Expand Down
2 changes: 2 additions & 0 deletions services/inc/diagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ constexpr const char* DIAG_NAME_SYSTEM_SYSTEM_LOOPS = "sys:loop";
constexpr const char* DIAG_NAME_SYSTEM_APPLICATION_LOOPS = "app:loop";
constexpr const char* DIAG_NAME_SYSTEM_UPTIME = "sys:uptime";
constexpr const char* DIAG_NAME_SYSTEM_BATTERY_STATE = "batt:state";
constexpr const char* DIAG_NAME_SYSTEM_POWER_SOURCE = "pwr:src";
constexpr const char* DIAG_NAME_NETWORK_CONNECTION_STATUS = "net:stat";
constexpr const char* DIAG_NAME_NETWORK_CONNECTION_ERROR_CODE = "net:err";
constexpr const char* DIAG_NAME_NETWORK_DISCONNECTS = "net:dx";
Expand Down Expand Up @@ -58,6 +59,7 @@ typedef enum diag_id {
DIAG_ID_SYSTEM_APPLICATION_LOOPS = 5, // app:loops
DIAG_ID_SYSTEM_UPTIME = 6, // sys:uptime
DIAG_ID_SYSTEM_BATTERY_STATE = 7, // batt:state
DIAG_ID_SYSTEM_POWER_SOURCE = 24, // pwr::src
DIAG_ID_NETWORK_CONNECTION_STATUS = 8, // net:stat
DIAG_ID_NETWORK_CONNECTION_ERROR_CODE = 9, // net:error
DIAG_ID_NETWORK_DISCONNECTS = 12, // net:disconn
Expand Down
3 changes: 3 additions & 0 deletions system/inc/system_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ enum SystemEvents {
button_final_click = 1<<13, // generated for last click in series - data is the number of clicks in the lower 4 bits.
time_changed = 1<<14,
low_battery = 1<<15, // generated when low battery condition is detected
battery_state = 1<<16,
power_source = 1<<17,


all_events = 0xFFFFFFFFFFFFFFFF
};
Expand Down
18 changes: 14 additions & 4 deletions system/inc/system_power.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,24 @@ typedef enum {
BATTERY_STATE_CHARGING = 2,
BATTERY_STATE_CHARGED = 3,
BATTERY_STATE_DISCHARGING = 4,
BATTERY_STATE_FAULT = 5
} BatteryState;
BATTERY_STATE_FAULT = 5,
BATTERY_STATE_DISCONNECTED = 6
} battery_state_t;

typedef enum {
POWER_SOURCE_UNKNOWN = 0,
POWER_SOURCE_VIN = 1,
POWER_SOURCE_USB_HOST = 2,
POWER_SOURCE_USB_ADAPTER = 3,
POWER_SOURCE_USB_OTG = 4,
POWER_SOURCE_BATTERY = 5
} power_source_t;

} } // particle::power

extern particle::power::BatteryChargeDiagnosticData g_batteryCharge;
extern particle::SimpleEnumDiagnosticData<particle::power::BatteryState> g_batteryState;
extern particle::SimpleEnumDiagnosticData<particle::power::battery_state_t> g_batteryState;
extern particle::SimpleEnumDiagnosticData<particle::power::power_source_t> g_powerSource;

void system_power_management_init();
void system_power_management_update();
void system_power_management_sleep(bool sleep = true);
3 changes: 0 additions & 3 deletions system/src/system_network_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,6 @@ class ManagedNetworkInterface : public NetworkInterface
}
console.loop();
}
#if Wiring_Cellular == 1
system_power_management_update();
#endif
#if PLATFORM_THREADING
SystemISRTaskQueue.process();
if (!APPLICATION_THREAD_CURRENT()) {
Expand Down
254 changes: 10 additions & 244 deletions system/src/system_power.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
#include "system_power.h"
#include "spark_wiring_platform.h"
#include "spark_wiring_power.h"
#include "spark_wiring_fuel.h"
#include "spark_wiring_diagnostics.h"
#include "spark_wiring_fixed_point.h"
#include "debug.h"

#if Wiring_Cellular == 1
/* flag used to initiate system_power_management_update() from main thread */
static volatile bool SYSTEM_POWER_MGMT_UPDATE = true;

#include "system_power_manager.h"

namespace particle { namespace power {

Expand All @@ -18,7 +17,7 @@ BatteryChargeDiagnosticData::BatteryChargeDiagnosticData(uint16_t id, const char

int BatteryChargeDiagnosticData::get(IntType& val) {
FuelGauge fuel(true);
float soc = fuel.getSoC();
float soc = fuel.getNormalizedSoC();
val = particle::FixedPointUQ<8, 8>(soc);
return SYSTEM_ERROR_NONE;
}
Expand All @@ -29,254 +28,21 @@ using namespace particle;
using namespace particle::power;

BatteryChargeDiagnosticData g_batteryCharge(DIAG_ID_SYSTEM_BATTERY_CHARGE, DIAG_NAME_SYSTEM_BATTERY_CHARGE);
SimpleEnumDiagnosticData<BatteryState> g_batteryState(DIAG_ID_SYSTEM_BATTERY_STATE, DIAG_NAME_SYSTEM_BATTERY_STATE, BATTERY_STATE_UNKNOWN);

static bool s_lowBattEventEnabled = true;
static system_tick_t s_possibleFaultTimestamp = 0;
static uint32_t s_possibleFaultCounter = 0;

void system_power_log_stat(uint8_t stat, uint8_t fault);
void system_power_handle_state_change(BatteryState from, BatteryState to, bool low);

/*******************************************************************************
* Function Name : Power_Management_Handler
* Description : Sets default power management IC charging rate when USB
input power source changes or low battery indicated by
fuel gauge IC.
* Output : SYSTEM_POWER_MGMT_UPDATE is set to true.
*******************************************************************************/
extern "C" void Power_Management_Handler(void)
{
SYSTEM_POWER_MGMT_UPDATE = true;
}

void system_power_init_default_params() {
PMIC power(true);
power.begin();
power.disableWatchdog();
power.disableDPDM();
// power.setInputVoltageLimit(4360); // default
power.setInputCurrentLimit(900); // 900mA
power.setChargeCurrent(0,0,0,0,0,0); // 512mA
power.setChargeVoltage(4112); // 4.112V termination voltage
power.enableCharging();
}
SimpleEnumDiagnosticData<battery_state_t> g_batteryState(DIAG_ID_SYSTEM_BATTERY_STATE, DIAG_NAME_SYSTEM_BATTERY_STATE, BATTERY_STATE_UNKNOWN);
SimpleEnumDiagnosticData<power_source_t> g_powerSource(DIAG_ID_SYSTEM_POWER_SOURCE, DIAG_NAME_SYSTEM_POWER_SOURCE, POWER_SOURCE_UNKNOWN);

void system_power_management_init()
{
INFO("Power Management Initializing.");
system_power_init_default_params();
FuelGauge fuel(true);
fuel.wakeup();
fuel.setAlertThreshold(10); // Low Battery alert at 10% (about 3.6V)
fuel.clearAlert(); // Ensure this is cleared, or interrupts will never occur
INFO("State of Charge: %-6.2f%%", fuel.getSoC());
INFO("Battery Voltage: %-4.2fV", fuel.getVCell());
attachInterrupt(LOW_BAT_UC, Power_Management_Handler, FALLING);

system_power_management_update();
PowerManager::instance()->init();
}

void system_power_management_sleep(bool sleep) {
// When going into sleep we do not want to exceed the default charging parameters set
// by system_power_init_default_params(), which will be reset in case we are in a fault mode with
// PMIC watchdog enabled. Reset to the defaults and disable watchdog before going into sleep.
if (sleep && g_batteryState == BATTERY_STATE_FAULT) {
system_power_init_default_params();
SYSTEM_POWER_MGMT_UPDATE = true;
}
PowerManager::instance()->sleep(sleep);
}

void system_power_management_update()
{
if (!SYSTEM_POWER_MGMT_UPDATE) {
return;
}

SYSTEM_POWER_MGMT_UPDATE = false;

PMIC power(true);
FuelGauge fuel(true);
power.begin();

// In order to read the current fault status, the host has to read REG09 two times
// consecutively. The 1st reads fault register status from the last read and the 2nd
// reads the current fault register status.
const uint8_t lastFault = power.getFault();
const uint8_t curFault = power.getFault();

const uint8_t status = power.getSystemStatus();

// Watchdog fault
if ((curFault | lastFault) & 0x80) {
// Restore parameters
system_power_init_default_params();
}

BatteryState state = BATTERY_STATE_UNKNOWN;
#else /* Wiring_Cellular != 1 */

// Deduce current battery state
const uint8_t chrg_stat = (status >> 4) & 0b11;
if (chrg_stat) {
// Charging or charged
if (chrg_stat == 0b11) {
state = BATTERY_STATE_CHARGED;
} else {
state = BATTERY_STATE_CHARGING;
}
} else {
// For now we only know that the battery is not charging
state = BATTERY_STATE_NOT_CHARGING;
// Now we need to deduce whether it is NOT_CHARGING, DISCHARGING, or in a FAULT state
// const uint8_t chrg_fault = (curFault >> 4) & 0b11;
const uint8_t bat_fault = (curFault >> 3) & 0b01;
// const uint8_t ntc_fault = curFault & 0b111;
const uint8_t pwr_good = (status >> 2) & 0b01;
if (bat_fault) {
state = BATTERY_STATE_FAULT;
} else if (!pwr_good) {
state = BATTERY_STATE_DISCHARGING;
}
}

const bool lowBat = fuel.getAlert();
system_power_handle_state_change(g_batteryState, state, lowBat);

if (lowBat) {
fuel.clearAlert();
if (s_lowBattEventEnabled) {
s_lowBattEventEnabled = false;
system_notify_event(low_battery);
}
}

system_power_log_stat(status, curFault);
}

BatteryState system_power_handle_possible_fault(BatteryState from, BatteryState to) {
if (from == BATTERY_STATE_CHARGED || from == BATTERY_STATE_CHARGING) {
system_tick_t m = millis();
if (m - s_possibleFaultTimestamp > 1000) {
s_possibleFaultTimestamp = m;
s_possibleFaultCounter = 0;
} else {
s_possibleFaultCounter++;
if (s_possibleFaultCounter > 5) {
return BATTERY_STATE_FAULT;
}
}
}
return to;
}

void system_power_handle_state_change(BatteryState from, BatteryState to, bool low) {
BatteryState toOriginal = to;

switch (from) {
case BATTERY_STATE_CHARGING:
case BATTERY_STATE_CHARGED: {
if (!low) {
to = system_power_handle_possible_fault(from, to);
}
break;
}
default: {
if (from == to) {
// No state change occured
return;
}
}
}

switch (from) {
case BATTERY_STATE_UNKNOWN:
break;
case BATTERY_STATE_NOT_CHARGING: {
if (to == BATTERY_STATE_CHARGING) {
// Reconfigure the PMIC current limits due to possible input source change
system_power_init_default_params();
}
break;
}
case BATTERY_STATE_CHARGING:
break;
case BATTERY_STATE_CHARGED:
break;
case BATTERY_STATE_DISCHARGING:
break;
case BATTERY_STATE_FAULT: {
// When going from FAULT state to any other state, reset FuelGauge
FuelGauge fuel;
fuel.quickStart();

system_power_init_default_params();
break;
}
}

switch (to) {
case BATTERY_STATE_CHARGING: {
// When going into CHARGING state, enable low battery event
s_lowBattEventEnabled = true;
break;
}
case BATTERY_STATE_UNKNOWN:
break;
case BATTERY_STATE_NOT_CHARGING:
break;
case BATTERY_STATE_CHARGED:
break;
case BATTERY_STATE_DISCHARGING:
break;
case BATTERY_STATE_FAULT: {
if (from != to && to != toOriginal) {
// A fault condition was detected by system_power_handle_possible_fault()
PMIC power;
// Enable watchdog that should re-enable charging in 40 seconds
power.setWatchdog(0b01);
// Disable charging
power.disableCharging();
}
break;
}
}

g_batteryState = to;

#if defined(DEBUG_BUILD) || 1
static const char* states[] = {
"UNKNOWN",
"NOT_CHARGING",
"CHARGING",
"CHARGED",
"DISCHARGING",
"FAULT"
};
LOG(TRACE, "Battery state %s -> %s", states[from], states[to]);
#endif // defined(DEBUG_BUILD)
}

void system_power_log_stat(uint8_t stat, uint8_t fault) {
#if defined(DEBUG_BUILD) && 0
if (LOG_ENABLED(TRACE)) {
uint8_t vbus_stat = stat >> 6; // 0 – Unknown (no input, or DPDM detection incomplete), 1 – USB host, 2 – Adapter port, 3 – OTG
uint8_t chrg_stat = (stat >> 4) & 0x03; // 0 – Not Charging, 1 – Pre-charge (<VBATLOWV), 2 – Fast Charging, 3 – Charge Termination Done
bool dpm_stat = stat & 0x08; // 0 – Not DPM, 1 – VINDPM or IINDPM
bool pg_stat = stat & 0x04; // 0 – Not Power Good, 1 – Power Good
bool therm_stat = stat & 0x02; // 0 – Normal, 1 – In Thermal Regulation
bool vsys_stat = stat & 0x01; // 0 – Not in VSYSMIN regulation (BAT > VSYSMIN), 1 – In VSYSMIN regulation (BAT < VSYSMIN)
bool wd_fault = fault & 0x80; // 0 – Normal, 1- Watchdog timer expiration
uint8_t chrg_fault = (fault >> 4) & 0x03; // 0 – Normal, 1 – Input fault (VBUS OVP or VBAT < VBUS < 3.8 V),
// 2 - Thermal shutdown, 3 – Charge Safety Timer Expiration
bool bat_fault = fault & 0x08; // 0 – Normal, 1 – BATOVP
uint8_t ntc_fault = fault & 0x07; // 0 – Normal, 5 – Cold, 6 – Hot
LOG(TRACE, "[ PMIC STAT ] VBUS:%d CHRG:%d DPM:%d PG:%d THERM:%d VSYS:%d", vbus_stat, chrg_stat, dpm_stat, pg_stat, therm_stat, vsys_stat);
LOG(TRACE, "[ PMIC FAULT ] WATCHDOG:%d CHRG:%d BAT:%d NTC:%d", wd_fault, chrg_fault, bat_fault, ntc_fault);
delay(50);
}
#endif
}
#else
void system_power_management_sleep(bool sleep) {
}
#endif

#endif /* Wiring_Cellular == 1 */
Loading