1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-24 15:38:22 +00:00

feat(core): fuel gauge compensation in hibernation mode.

[no changelog]
This commit is contained in:
kopecdav 2025-06-30 15:50:29 +02:00 committed by kopecdav
parent 513fbcb039
commit c9acae7b97
4 changed files with 112 additions and 17 deletions

View File

@ -125,6 +125,10 @@ static secbool boot_sequence(secbool manufacturing_mode) {
haptic_init();
#endif
#ifdef USE_RTC
rtc_init();
#endif
#ifdef USE_POWER_MANAGER
pm_init(false);
@ -245,10 +249,6 @@ static void drivers_init(secbool manufacturing_mode,
tamper_init();
#endif
#ifdef USE_RTC
rtc_init();
#endif
#ifndef LAZY_DISPLAY_INIT
display_touch_init(manufacturing_mode, touch_initialized);
#endif

View File

@ -107,6 +107,27 @@ pm_status_t pm_init(bool inherit_state) {
irq_key_t irq_key = irq_lock();
if (recovery_ok) {
#ifdef USE_RTC
// RTC compensation should happen only during initialization in bootloader
if (!inherit_state) {
// Get RTC timestamp and compare it with the timestamp from recovery data
// to estimate time off and compensate self-discharge of the battery.
uint32_t rtc_timestamp;
if (recovery.last_capture_timestamp != 0 &&
rtc_get_timestamp(&rtc_timestamp)) {
// If the RTC timestamp is older than the last captured timestamp,
// we will not use it.
if (rtc_timestamp >= recovery.last_capture_timestamp) {
pm_compensate_fuel_gauge(
&recovery.soc, rtc_timestamp - recovery.last_capture_timestamp,
PM_SELF_DISG_RATE_HIBERNATION_MA, 25.0f);
}
}
}
#endif
fuel_gauge_set_soc(&drv->fuel_gauge, recovery.soc, recovery.P);
} else {
pm_battery_initial_soc_guess();
@ -256,6 +277,13 @@ pm_status_t pm_suspend(wakeup_flags_t* wakeup_reason) {
wakeup_flags_t wakeup_flags = system_suspend();
// Wait for pmic measurements to stabilize the fuel gauge estimation.
pm_status_t status = pm_wait_to_stabilize(drv, PM_STABILIZATION_TIMEOUT_MS);
if (status != PM_OK) {
// timeout during state machine stabilization
return false;
}
// TODO: Handle wake-up flags
// UNUSED(wakeup_flags);
@ -443,6 +471,13 @@ pm_status_t pm_store_data_to_backup_ram() {
recovery.bat_critical = drv->battery_critical;
recovery.bootloader_exit_state = drv->state;
#ifdef USE_RTC
if (!rtc_get_timestamp(&recovery.last_capture_timestamp)) {
// If RTC timestamp cannot be obtained, set it to 0
recovery.last_capture_timestamp = 0;
}
#endif
irq_unlock(irq_key);
bool write_ok =
@ -532,6 +567,33 @@ bool pm_driver_suspend(void) {
return false;
}
irq_key_t irq_key = irq_lock();
if (drv->woke_up_from_suspend) {
// Driver just woke up from suspend and have no data available yet.
// Request the suspend but wait for the next pmic_meausrement
drv->suspending = true;
} else {
pm_schedule_rtc_wakeup();
drv->suspended = true;
}
// Delete the monitoring timer to stop the periodic sampling
systimer_delete(drv->monitoring_timer);
irq_unlock(irq_key);
return true;
}
bool pm_schedule_rtc_wakeup(void) {
pm_driver_t* drv = &g_pm;
if (!drv->initialized) {
return false;
}
#ifdef USE_RTC
// Capture the timestamp when device was active for the last time.
@ -570,6 +632,7 @@ bool pm_driver_suspend(void) {
systimer_delete(drv->monitoring_timer);
irq_unlock(irq_key);
drv->suspended = true;
return true;
}
@ -580,6 +643,12 @@ bool pm_driver_resume(void) {
return false;
}
if (!drv->suspended) {
// Already resumed, nothing to do
return true;
}
drv->suspended = false;
drv->woke_up_from_suspend = true;
#ifdef USE_RTC
@ -603,18 +672,18 @@ bool pm_driver_resume(void) {
// Set the periodic sampling period
systimer_set_periodic(drv->monitoring_timer, PM_BATTERY_SAMPLING_PERIOD_MS);
// Wait for pmic measurements to stabilize the fuel gauge estimation.
pm_status_t status = pm_wait_to_stabilize(drv, PM_STABILIZATION_TIMEOUT_MS);
if (status != PM_OK) {
// timeout during state machine stabilization
return false;
}
return true;
}
bool pm_driver_is_suspended(void) {
// No specific pending tasks, just return true
return true;
pm_driver_t* drv = &g_pm;
bool suspended;
irq_key_t irq_key = irq_lock();
suspended = drv->suspended;
irq_unlock(irq_key);
return suspended;
}
void pm_compensate_fuel_gauge(float* soc, uint32_t elapsed_s,
@ -623,7 +692,6 @@ void pm_compensate_fuel_gauge(float* soc, uint32_t elapsed_s,
bool discharging_mode = battery_current_ma >= 0.0f;
*soc -=
(compensation_mah / battery_total_capacity(bat_temp_c, discharging_mode));
return;
}
static pm_status_t pm_wait_to_stabilize(pm_driver_t* drv, uint32_t timeout_ms) {

View File

@ -40,8 +40,8 @@
#define PM_BATTERY_CHARGING_CURRENT_MIN PMIC_CHARGING_LIMIT_MIN
#define PM_BATTERY_SAMPLING_BUF_SIZE 10
#define PM_SELF_DISG_RATE_HIBERATION_MAH 0.004f
#define PM_SELF_DISG_RATE_SUSPEND_MAH 0.032f
#define PM_SELF_DISG_RATE_HIBERNATION_MA 0.004f
#define PM_SELF_DISG_RATE_SUSPEND_MA 0.032f
// Fuel gauge extended kalman filter parameters
#define PM_FUEL_GAUGE_R 2000.0f
@ -70,6 +70,14 @@ typedef struct {
bool state_machine_stabilized;
pm_power_status_t state;
// Set if the driver was requested to suspend background operations.
// IF so, the driver waits until the last operation is finished,
// then enters suspended mode.
bool suspending;
// Set if the driver's background operations are suspended.
bool suspended;
// Fuel gauge
fuel_gauge_state_t fuel_gauge;
bool fuel_gauge_initialized;
@ -156,3 +164,8 @@ pm_status_t pm_store_data_to_backup_ram(void);
// suspend or hibernation.
void pm_compensate_fuel_gauge(float* soc, uint32_t elapsed_s,
float battery_current_mah, float bat_temp_c);
// Schedule the RTC wakeup when going into suspend mode.
// Return false if the driver was not initialized or the RTC timestamp is
// not available.
bool pm_schedule_rtc_wakeup(void);

View File

@ -78,10 +78,17 @@ void pm_pmic_data_ready(void* context, pmic_report_t* report) {
} else {
// Use known battery self-discharge rate to compensate the fuel gauge
// estimation during the suspend period. Since this period may be very
// long and the batery temperature may vary, use the average ambient
// long and the battery temperature may vary, use the average ambient
// temperature.
pm_compensate_fuel_gauge(&drv->fuel_gauge.soc, drv->time_in_suspend_s,
PM_SELF_DISG_RATE_SUSPEND_MAH, 25.0f);
PM_SELF_DISG_RATE_SUSPEND_MA, 25.0f);
// TODO: Currently in suspend mode we use single self-discharge rate
// but in practive the discharge rate may change in case the BLE chip
// remains active. Since the device is very likely to stay in suspend
// mode for limited time, for now we decided to neglect this. but in
// the future we may want to distinguish between suspend mode
// with/without BLE and use different self-discharge rates.
}
fuel_gauge_set_soc(&drv->fuel_gauge, drv->fuel_gauge.soc,
@ -117,6 +124,13 @@ void pm_pmic_data_ready(void* context, pmic_report_t* report) {
pm_process_state_machine();
pm_store_data_to_backup_ram();
if (drv->suspending) {
pm_schedule_rtc_wakeup();
drv->suspending = false;
drv->suspended = true;
}
drv->state_machine_stabilized = true;
}
}