Skip to content

Commit 78d4383

Browse files
committed
pbio/drv/charger: Add charge pause after timeout.
After charging for a long time, we disable charging for some time. This matches observed behavior with the LEGO Education SPIKE V3.x firmware.
1 parent f49d482 commit 78d4383

File tree

1 file changed

+34
-4
lines changed

1 file changed

+34
-4
lines changed

lib/pbio/drv/charger/charger_mp2639a.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,24 @@ static bool read_chg(void) {
164164
#endif
165165
}
166166

167+
// Sample CHG signal at 4Hz to capture transitions to detect fault condition.
168+
#define PBDRV_CHARGER_MP2639A_STATUS_SAMPLE_TIME (250)
169+
170+
// After charging for a long time, we disable charging for some time. This
171+
// matches observed behavior with the LEGO Education SPIKE V3.x firmware.
172+
//
173+
// Why? Due to the way the hardware works, the hub cannot be truly turned off
174+
// while USB is plugged in. As a result, the charger is always on. For some
175+
// battery-charger pairs, this causes the battery to stop charging normally in
176+
// hardware when full as intended, automatically starting a new cycle after
177+
// some time. But in some battery-charger pairs, the charger will reach a
178+
// timeout state and not restart charging. When leaving such a combination
179+
// plugged in overnight, it will time out and not be charged in the morning,
180+
// which is not desirable. For this reason, we pause and restart charging
181+
// manually if it has been plugged in for a long time.
182+
#define PBDRV_CHARGER_MP2639A_CHARGE_TIMEOUT_MS (60 * 60 * 1000)
183+
#define PBDRV_CHARGER_MP2639A_CHARGE_PAUSE_MS (30 * 1000)
184+
167185
PROCESS_THREAD(pbdrv_charger_mp2639a_process, ev, data) {
168186
PROCESS_BEGIN();
169187

@@ -195,13 +213,11 @@ PROCESS_THREAD(pbdrv_charger_mp2639a_process, ev, data) {
195213
static bool chg_samples[7];
196214
static uint8_t chg_index = 0;
197215
static struct etimer timer;
198-
199-
// sample at 4Hz
200-
etimer_set(&timer, 250);
216+
static uint32_t charge_count = 0;
201217

202218
for (;;) {
219+
etimer_set(&timer, PBDRV_CHARGER_MP2639A_STATUS_SAMPLE_TIME);
203220
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER && etimer_expired(&timer));
204-
etimer_restart(&timer);
205221

206222
// Enable charger chip based on USB state. We don't need to disable it
207223
// on charger fault since the chip will automatically disable itself.
@@ -211,6 +227,10 @@ PROCESS_THREAD(pbdrv_charger_mp2639a_process, ev, data) {
211227
chg_samples[chg_index] = read_chg();
212228

213229
if (mode_pin_is_low) {
230+
231+
// Keep track of how long we have been charging.
232+
charge_count++;
233+
214234
// Count number of transitions seen during sampling window.
215235
int transitions = chg_samples[0] != chg_samples[PBIO_ARRAY_SIZE(chg_samples) - 1];
216236
for (size_t i = 1; i < PBIO_ARRAY_SIZE(chg_samples); i++) {
@@ -239,12 +259,22 @@ PROCESS_THREAD(pbdrv_charger_mp2639a_process, ev, data) {
239259
// devices) requires a momentary pulse on the /PB pin, which is
240260
// not wired up.
241261
pbdrv_charger_status = PBDRV_CHARGER_STATUS_DISCHARGE;
262+
charge_count = 0;
242263
}
243264

244265
// Increment sampling index with wrap around.
245266
if (++chg_index >= PBIO_ARRAY_SIZE(chg_samples)) {
246267
chg_index = 0;
247268
}
269+
270+
// If we have been charging for a long time, pause charging for a while.
271+
if (charge_count > (PBDRV_CHARGER_MP2639A_CHARGE_TIMEOUT_MS / PBDRV_CHARGER_MP2639A_STATUS_SAMPLE_TIME)) {
272+
pbdrv_charger_status = PBDRV_CHARGER_STATUS_DISCHARGE;
273+
pbdrv_charger_enable(false, PBDRV_CHARGER_LIMIT_NONE);
274+
etimer_set(&timer, PBDRV_CHARGER_MP2639A_CHARGE_PAUSE_MS);
275+
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer));
276+
charge_count = 0;
277+
}
248278
}
249279

250280
PROCESS_END();

0 commit comments

Comments
 (0)