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

battery - fix BATTERY_NOT_PRESENT detection, detection logic change #13599

Merged
merged 3 commits into from
May 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 11 additions & 15 deletions src/main/sensors/battery.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
*
*/

#define VBAT_STABLE_MAX_DELTA 10 // 100mV
#define LVC_AFFECT_TIME 10000000 //10 secs for the LVC to slowly kick in

// Battery monitoring stuff
Expand Down Expand Up @@ -159,20 +158,14 @@ void batteryUpdateVoltage(timeUs_t currentTimeUs)
break;
}

if (!voltageMeter.isVoltageStable && cmp32(millis(), voltageMeter.prevDisplayFilteredTime) >= PREV_DISPLAY_FILTERED_TIME_DIFF) {
if ((voltageState == BATTERY_NOT_PRESENT || voltageState == BATTERY_INIT) && isVoltageFromBattery()) {
voltageMeter.isVoltageStable = abs(voltageMeter.prevDisplayFiltered - voltageMeter.displayFiltered) <= VBAT_STABLE_MAX_DELTA;
}

voltageMeter.prevDisplayFiltered = voltageMeter.displayFiltered;
voltageMeter.prevDisplayFilteredTime = millis();
}
voltageStableUpdate(&voltageMeter);

DEBUG_SET(DEBUG_BATTERY, 0, voltageMeter.unfiltered);
DEBUG_SET(DEBUG_BATTERY, 1, voltageMeter.displayFiltered);
DEBUG_SET(DEBUG_BATTERY, 4, voltageMeter.isVoltageStable ? 1 : 0);
DEBUG_SET(DEBUG_BATTERY, 3, voltageMeter.voltageStableBits);
DEBUG_SET(DEBUG_BATTERY, 4, voltageIsStable(&voltageMeter) ? 1 : 0);
DEBUG_SET(DEBUG_BATTERY, 5, isVoltageFromBattery() ? 1 : 0);
DEBUG_SET(DEBUG_BATTERY, 6, voltageMeter.prevDisplayFiltered);
DEBUG_SET(DEBUG_BATTERY, 6, voltageMeter.voltageStablePrevFiltered);
DEBUG_SET(DEBUG_BATTERY, 7, voltageState);
}

Expand Down Expand Up @@ -205,7 +198,9 @@ bool isVoltageFromBattery(void)

void batteryUpdatePresence(void)
{
if ((voltageState == BATTERY_NOT_PRESENT || voltageState == BATTERY_INIT) && isVoltageFromBattery() && voltageMeter.isVoltageStable) {
if ((voltageState == BATTERY_NOT_PRESENT || voltageState == BATTERY_INIT)
&& isVoltageFromBattery()
&& voltageIsStable(&voltageMeter)) {
// Battery has just been connected - calculate cells, warning voltages and reset state

consumptionState = voltageState = BATTERY_OK;
Expand All @@ -232,11 +227,12 @@ void batteryUpdatePresence(void)
batteryCriticalHysteresisVoltage = (batteryCriticalVoltage > batteryConfig()->vbathysteresis) ? batteryCriticalVoltage - batteryConfig()->vbathysteresis : 0;
lowVoltageCutoff.percentage = 100;
lowVoltageCutoff.startTime = 0;
} else if (voltageState != BATTERY_NOT_PRESENT && voltageMeter.isVoltageStable && !isVoltageFromBattery()) {
} else if (voltageState != BATTERY_NOT_PRESENT
&& voltageIsStable(&voltageMeter)
&& !isVoltageFromBattery()) {
/* battery has been disconnected - can take a while for filter cap to disharge so we use a threshold of batteryConfig()->vbatnotpresentcellvoltage */
consumptionState = voltageState = BATTERY_NOT_PRESENT;

voltageMeter.isVoltageStable = false;

batteryCellCount = 0;
batteryWarningVoltage = 0;
batteryCriticalVoltage = 0;
Expand Down
32 changes: 29 additions & 3 deletions src/main/sensors/voltage.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

#include "platform.h"

Expand Down Expand Up @@ -89,10 +90,10 @@ const uint8_t supportedVoltageMeterCount = ARRAYLEN(voltageMeterIds);
void voltageMeterReset(voltageMeter_t *meter)
{
meter->displayFiltered = 0;
meter->prevDisplayFiltered = 0;
meter->prevDisplayFilteredTime = 0;
meter->voltageStablePrevFiltered = 0;
meter->voltageStableLastUpdate = 0;
meter->voltageStableBits = 0;
meter->unfiltered = 0;
meter->isVoltageStable = false;
#if defined(USE_BATTERY_VOLTAGE_SAG_COMPENSATION)
meter->sagFiltered = 0;
#endif
Expand Down Expand Up @@ -362,3 +363,28 @@ void voltageMeterRead(voltageMeterId_e id, voltageMeter_t *meter)
voltageMeterReset(meter);
}
}

// update voltageStableBits
// new 1 bit (= stable) is shifted in every VOLTAGE_STABLE_UPDATE_MS
// when difference is larger than VOLTAGE_STABLE_MAX_DELTA, LSB of shift register is
// reset to 0 (logical AND) and voltageStablePrevFiltered is updated with new voltage value
void voltageStableUpdate(voltageMeter_t* vm)
{
const uint32_t now = millis();
// test voltage on each call
if (abs(vm->voltageStablePrevFiltered - vm->displayFiltered) > VOLTAGE_STABLE_MAX_DELTA) {
// reset stable voltage reference
vm->voltageStablePrevFiltered = vm->displayFiltered;
vm->voltageStableBits &= ~BIT(0); // voltage threshold exceeded in this period, clear LSB/BIT(0)
}
if (cmp32(now, vm->voltageStableLastUpdate) >= VOLTAGE_STABLE_TICK_MS) {
vm->voltageStableBits = (vm->voltageStableBits << 1) | BIT(0); // start with 'stable' state
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is | BIT(0) really necessary as << 1 shifts a 0 already?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It it bit zero (LSB), not bit with value of 0. New 1 bit is shifted in on each update and possibly cleared above.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense!

vm->voltageStableLastUpdate = now;
}
}

// voltage is stable when it was within VOLTAGE_STABLE_MAX_DELTA for 10 update periods
bool voltageIsStable(voltageMeter_t* vm)
{
return __builtin_popcount(vm->voltageStableBits & (BIT(VOLTAGE_STABLE_BITS_TOTAL + 1) - 1)) >= VOLTAGE_STABLE_BITS_THRESHOLD;
}
23 changes: 17 additions & 6 deletions src/main/sensors/voltage.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@
#define SLOW_VOLTAGE_TASK_FREQ_HZ 50
#define FAST_VOLTAGE_TASK_FREQ_HZ 200

#define PREV_DISPLAY_FILTERED_TIME_DIFF 500 // ms
#define VOLTAGE_STABLE_TICK_MS 100
#define VOLTAGE_STABLE_BITS_TOTAL 11 // number of samples to test
#define VOLTAGE_STABLE_BITS_THRESHOLD 10 // number of samples tham must be within delta (~1s)
#define VOLTAGE_STABLE_MAX_DELTA 10 // 100mV

STATIC_ASSERT(VOLTAGE_STABLE_BITS_TOTAL < sizeof(uint16_t) * 8, "not enough voltageStableBits");

//
// meters
Expand All @@ -43,11 +48,11 @@ extern const char * const voltageMeterSourceNames[VOLTAGE_METER_COUNT];
// WARNING - do not mix usage of VOLTAGE_METER_* and VOLTAGE_SENSOR_*, they are separate concerns.

typedef struct voltageMeter_s {
uint16_t displayFiltered; // voltage in 0.01V steps
uint16_t prevDisplayFiltered; // voltage in 0.01V steps
timeMs_t prevDisplayFilteredTime;
bool isVoltageStable;
uint16_t unfiltered; // voltage in 0.01V steps
uint16_t displayFiltered; // voltage in 0.01V steps
uint16_t voltageStablePrevFiltered; // last filtered voltage sample
timeMs_t voltageStableLastUpdate;
uint16_t voltageStableBits; // rolling bitmask, bit 1 if battery difference is within threshold, shifted left
uint16_t unfiltered; // voltage in 0.01V steps
#if defined(USE_BATTERY_VOLTAGE_SAG_COMPENSATION)
uint16_t sagFiltered; // voltage in 0.01V steps
#endif
Expand Down Expand Up @@ -116,6 +121,12 @@ void voltageMeterESCRefresh(void);
void voltageMeterESCReadCombined(voltageMeter_t *voltageMeter);
void voltageMeterESCReadMotor(uint8_t motor, voltageMeter_t *voltageMeter);

//
// api for battery stable voltage detection
//

void voltageStableUpdate(voltageMeter_t* vm);
bool voltageIsStable(voltageMeter_t* vm);

//
// API for reading/configuring current meters by id.
Expand Down