Defer regulator ADC and flash work during Morse timing
This commit is contained in:
1 parent
c1e7ce0959
commit
71372f686f
3 files changed
+76
-4
No files matched your search
@@ -38,6 +38,7 @@ void markRegulatorCleanReset() {}
|
||||
void resumeRegulatorRecovery() {}
|
||||
}} // namespace mesh::power
|
||||
#else
|
||||
#include "MorseLoopPolicy.h"
|
||||
namespace mesh { namespace power {
|
||||
namespace {
|
||||
MainBoard* main_board = nullptr;
|
||||
@@ -231,14 +232,18 @@ void serviceRegulatorSettings() {
|
||||
(activeRegulatorMode(true) == RegulatorMode::Dcdc ? RECOVERY_MCU : 0)
|
||||
| (activeRegulatorMode(false) == RegulatorMode::Dcdc ? RECOVERY_RADIO : 0);
|
||||
recovery().updateActiveDomains(active_dcdc);
|
||||
if (uint32_t(now - last_battery) >= 30000) {
|
||||
// ADC sampling deliberately spaces three readings, and persistence can
|
||||
// block on flash. Keep both out of keyed Morse TX/RX timing; heartbeats
|
||||
// remain live without refreshing a deferred sample or retry deadline.
|
||||
const bool defer_io = mesh::morseTimingActive(main_radio);
|
||||
if (!defer_io && uint32_t(now - last_battery) >= 30000) {
|
||||
bool valid = false;
|
||||
const uint8_t percent = readBatteryPercent(valid);
|
||||
last_battery = millis();
|
||||
recovery().updateBattery(last_battery, percent, valid);
|
||||
}
|
||||
recovery().heartbeat(millis());
|
||||
if (!fallback_saved && uint32_t(now - last_save) >= 30000) {
|
||||
if (!defer_io && !fallback_saved && uint32_t(now - last_save) >= 30000) {
|
||||
last_save = now;
|
||||
fallback_saved = save(requested[0], requested[1], recovery().fallbackMask());
|
||||
}
|
||||
|
||||
+53
@@ -269,6 +269,59 @@ int main(int argc,char** argv) {
|
||||
fresh(board,radio,fs,ResetKind::Cold,true);advance(1000);fresh(board,radio,fs,ResetKind::Crash);counts(1,1);
|
||||
for(unsigned n=0;n<301;++n)advance(1000);
|
||||
fresh(board,radio,fs,ResetKind::Crash);counts(1,1);
|
||||
} else if(test=="morse_battery") {
|
||||
for(bool receive:{false,true}) {
|
||||
fs=MockFS();fresh(board,radio,fs,ResetKind::Cold,true);
|
||||
radio.morse_tx=!receive;radio.morse_rx=receive;
|
||||
const auto measured=last_battery;
|
||||
const unsigned reads=board.reads;
|
||||
advance(30000);
|
||||
assert(mock_now==measured+30000 && board.reads==reads);
|
||||
assert(last_battery==measured);
|
||||
assert(platformRecoveryRecord().heartbeatAt==mock_now);
|
||||
assert(platformRecoveryRecord().activeDcdcMask==RECOVERY_DOMAINS);
|
||||
radio.active=RegulatorMode::Ldo;
|
||||
advance(1000);
|
||||
assert(platformRecoveryRecord().activeDcdcMask==RECOVERY_MCU);
|
||||
radio.active=RegulatorMode::Dcdc;
|
||||
advance(30000);
|
||||
assert(mock_now==measured+61000 && board.reads==reads);
|
||||
assert(last_battery==measured);
|
||||
assert(platformRecoveryRecord().heartbeatAt==mock_now);
|
||||
assert(platformRecoveryRecord().activeDcdcMask==RECOVERY_DOMAINS);
|
||||
fresh(board,radio,fs,ResetKind::Crash);counts(0,0);
|
||||
|
||||
radio.morse_tx=!receive;radio.morse_rx=receive;
|
||||
const unsigned resumed_reads=board.reads;
|
||||
advance(30000);
|
||||
radio.morse_tx=radio.morse_rx=false;
|
||||
// An unkeyed carrier does not defer optional work.
|
||||
radio.carrier_wave=true;
|
||||
const uint32_t before=mock_now;
|
||||
advance(1000);
|
||||
assert(mock_now==before+1010 && board.reads==resumed_reads+3);
|
||||
assert(last_battery==mock_now);
|
||||
}
|
||||
} else if(test=="morse_save") {
|
||||
for(bool receive:{false,true}) {
|
||||
fs=MockFS();fresh(board,radio,fs,ResetKind::Cold,true);
|
||||
for(unsigned n=1;n<=2;++n){advance(1000);fresh(board,radio,fs,ResetKind::Crash);counts(n,n);}
|
||||
fs.fail_write=true;advance(1000);fresh(board,radio,fs,ResetKind::Crash);
|
||||
assert(!fallback_saved);
|
||||
const auto measured=last_battery,save_deadline=last_save;
|
||||
const unsigned reads=board.reads,writes=fs.writes;
|
||||
fs.fail_write=false;radio.morse_tx=!receive;radio.morse_rx=receive;
|
||||
advance(30000);
|
||||
assert(mock_now==measured+30000 && board.reads==reads && fs.writes==writes);
|
||||
assert(last_battery==measured && last_save==save_deadline && !fallback_saved);
|
||||
assert(platformRecoveryRecord().heartbeatAt==mock_now);
|
||||
radio.morse_tx=radio.morse_rx=false;
|
||||
advance(1000);
|
||||
assert(board.reads==reads+3 && fs.writes>writes && fallback_saved);
|
||||
assert(last_battery==mock_now && last_save==mock_now-10);
|
||||
fresh(board,radio,fs,ResetKind::Cold,true);
|
||||
assert(board.active==RegulatorMode::Ldo && radio.active==RegulatorMode::Ldo);
|
||||
}
|
||||
} else if(test=="retry") {
|
||||
fresh(board,radio,fs,ResetKind::Cold,true);
|
||||
for(unsigned n=1;n<=2;++n){advance(1000);fresh(board,radio,fs,ResetKind::Crash);counts(n,n);}
|
||||
|
||||
@@ -86,7 +86,7 @@ struct MainBoard {
|
||||
uint8_t modes=3;power::RegulatorMode default_mode=power::RegulatorMode::Dcdc;
|
||||
power::RegulatorMode active=power::RegulatorMode::Default;
|
||||
unsigned applies=0;bool dcdc_applied=false;uint16_t mv=3600;
|
||||
std::vector<uint16_t> samples;unsigned sample_index=0;
|
||||
std::vector<uint16_t> samples;unsigned sample_index=0,reads=0;
|
||||
uint8_t mcuRegulatorModes() const { return modes; }
|
||||
power::RegulatorMode defaultMcuRegulatorMode() const { return default_mode; }
|
||||
power::RegulatorMode activeMcuRegulatorMode() const { return active; }
|
||||
@@ -96,6 +96,7 @@ struct MainBoard {
|
||||
dcdc_applied|=active==power::RegulatorMode::Dcdc;return true;
|
||||
}
|
||||
uint16_t getBattMilliVolts() {
|
||||
++reads;
|
||||
return samples.empty()?mv:samples[sample_index++%samples.size()];
|
||||
}
|
||||
void resetHardware() { active=power::RegulatorMode::Default;sample_index=0; }
|
||||
@@ -104,6 +105,10 @@ struct Radio {
|
||||
uint8_t modes=3;power::RegulatorMode default_mode=power::RegulatorMode::Dcdc;
|
||||
power::RegulatorMode active=power::RegulatorMode::Default,boot=power::RegulatorMode::Default;
|
||||
unsigned configures=0,inits=0;bool locked=false,dcdc_applied=false;
|
||||
bool morse_tx=false,morse_rx=false,carrier_wave=false;
|
||||
bool isMorseTxActive() const { return morse_tx; }
|
||||
bool isMorseRxActive() const { return morse_rx; }
|
||||
bool isCarrierWaveActive() const { return carrier_wave; }
|
||||
uint8_t regulatorModes() const { return modes; }
|
||||
power::RegulatorMode defaultRegulatorMode() const { return default_mode; }
|
||||
power::RegulatorMode activeRegulatorMode() const { return active; }
|
||||
@@ -115,7 +120,10 @@ struct Radio {
|
||||
++inits;locked=true;active=boot==power::RegulatorMode::Default?default_mode:boot;
|
||||
dcdc_applied|=active==power::RegulatorMode::Dcdc;
|
||||
}
|
||||
void resetHardware() { active=boot=power::RegulatorMode::Default;locked=false; }
|
||||
void resetHardware() {
|
||||
active=boot=power::RegulatorMode::Default;locked=false;
|
||||
morse_tx=morse_rx=carrier_wave=false;
|
||||
}
|
||||
};
|
||||
}
|
||||
'''
|
||||
@@ -239,6 +247,12 @@ class RegulatorSettingsTests(unittest.TestCase):
|
||||
def test_failed_fallback_save_retries_and_then_survives_power_loss(self):
|
||||
self.scenario('retry')
|
||||
|
||||
def test_morse_defers_battery_io_without_refreshing_stale_samples_or_stopping_heartbeats(self):
|
||||
self.scenario('morse_battery')
|
||||
|
||||
def test_morse_defers_flash_retry_and_resumes_overdue_io_after_timing_ends(self):
|
||||
self.scenario('morse_save')
|
||||
|
||||
def test_missing_primary_store_rejects_persistence_without_live_mode_changes(self):
|
||||
self.scenario('no_store')
|
||||
|
||||
|
||||
Reference in new issue
Block a user