Доработки по тесту драйвера

This commit is contained in:
2026-08-14 14:39:39 +03:00
parent 037bb37e62
commit 035792aedd
10 changed files with 307 additions and 140 deletions

View File

@@ -38,6 +38,16 @@ float dutyFromPulse(uint32_t hz, uint32_t pulseNs) {
return static_cast<float>(static_cast<double>(hz) * pulseNs / 10000000.0);
}
bool configuredTxPulseLightOn(const Settings &settings) {
return static_cast<TestKind>(settings.testKind) == TestKind::DRIVER
? txActiveLightOn(settings) : true;
}
const char *configuredLevelName(const Settings &settings) {
return static_cast<TestKind>(settings.testKind) == TestKind::DRIVER
? lightCodeName(static_cast<LightCode>(settings.lightCode)) : "AUTO";
}
void formatTarget(uint32_t hz, uint32_t pulseNs, char *out, size_t size) {
char frequency[16], pulse[12];
Display::formatPwmFrequency(hz, frequency, sizeof(frequency));
@@ -67,7 +77,7 @@ void formatFailure(FailReason reason, uint32_t hz, uint32_t pulseNs,
}
void formatElapsedNs(uint64_t ns, char *out, size_t size) {
// Compact form keeps `T:... D:... P:...` within 21 OLED columns.
// Compact form keeps error timing within 21 OLED columns.
// Exact nanoseconds remain available in the Serial diagnostic.
if (ns < 1000ULL) snprintf(out, size, "%llun", ns);
else if (ns < 1000000ULL) snprintf(out, size, "%.1fu", ns / 1000.0);
@@ -269,7 +279,7 @@ void App::finishInitialization(bool factoryReset) {
}
sanitizeRange();
params_ = store_.params(settings_);
pwm_.configureActiveLight(txActiveLightOn(settings_));
pwm_.configureActiveLight(configuredTxPulseLightOn(settings_));
if (!display_.begin()) Log::event("BOOT", "OLED unavailable; Serial UI remains fully operational");
initialized_ = true;
if (!receiver_.begin()) { Log::event("BOOT", "FATAL: capture peripheral init failed"); finish(false, FailReason::UNSUPPORTED); return; }
@@ -458,7 +468,7 @@ void App::printSerialHelp() {
Serial.println(" set min 250|500|1000|2000|5000|10000|50000");
Serial.println(" set accuracy 1|2|5|10");
Serial.println(" set time 100|250|500|1000|2000|5000 (ms)");
Serial.println(" set light HH|HL|LH|LL");
Serial.println(" set light HH|HL|LH|LL (DRIVER only)");
}
void App::printSerialStatus() {
@@ -471,7 +481,7 @@ void App::printSerialStatus() {
appStateName(state_), roleName(static_cast<Role>(settings_.role)),
testKindName(static_cast<TestKind>(settings_.testKind)), params_.frequencyHz,
params_.maxPulseNs, params_.minPulseNs, params_.accuracyPct, params_.testTimeMs,
lightCodeName(static_cast<LightCode>(settings_.lightCode)),
configuredLevelName(settings_),
usbHostPresent() ? "connected" : "disconnected");
}
@@ -485,7 +495,7 @@ void App::finishSerialSettingsChange() {
state_ = AppState::IDLE;
sanitizeRange();
params_ = store_.params(settings_);
pwm_.configureActiveLight(txActiveLightOn(settings_));
pwm_.configureActiveLight(configuredTxPulseLightOn(settings_));
const bool saved = store_.save(settings_);
Serial.printf("OK settings saved=%s\n", saved ? "yes" : "no");
if (static_cast<Role>(settings_.role) == Role::SLAVE) armSlave();
@@ -577,6 +587,10 @@ void App::handleSerialCommand(char *line) {
const int index = optionIndex(TEST_TIME_OPTIONS_MS, numeric);
if (index >= 0) { settings_.timeIndex = static_cast<uint8_t>(index); accepted = true; }
} else if (!strcmp(name, "light")) {
if (static_cast<TestKind>(settings_.testKind) != TestKind::DRIVER) {
Serial.println("ERR level setting is available only in DRIVER test");
return;
}
if (!strcmp(value, "hh")) { settings_.lightCode = static_cast<uint8_t>(LightCode::HH); accepted = true; }
else if (!strcmp(value, "hl")) { settings_.lightCode = static_cast<uint8_t>(LightCode::HL); accepted = true; }
else if (!strcmp(value, "lh")) { settings_.lightCode = static_cast<uint8_t>(LightCode::LH); accepted = true; }
@@ -634,7 +648,6 @@ void App::sanitizeRange() {
DRIVER_MIN_INPUT_PULSE_NS, driverLastMin);
if (settings_.minPulseIndex < firstDriverMin)
settings_.minPulseIndex = firstDriverMin;
settings_.lightCode = static_cast<uint8_t>(LightCode::HL);
}
}
@@ -672,17 +685,22 @@ void App::changeMenu(int d) {
case 0: value = &settings_.frequencyIndex; count = countOf(PWM_FREQUENCY_OPTIONS_HZ); break;
case 3: value = &settings_.accuracyIndex; count = countOf(ACCURACY_OPTIONS_PCT); break;
case 4: value = &settings_.timeIndex; count = countOf(TEST_TIME_OPTIONS_MS); break;
case 5: value = &settings_.lightCode; count = 4; break;
case 5:
if (static_cast<TestKind>(settings_.testKind) != TestKind::DRIVER) {
showMenu();
return;
}
value = &settings_.lightCode; count = 4; break;
default: return;
}
*value = cycleIndex(*value, 0, static_cast<uint8_t>(count - 1U), d);
}
sanitizeRange(); params_ = store_.params(settings_);
pwm_.configureActiveLight(txActiveLightOn(settings_));
pwm_.configureActiveLight(configuredTxPulseLightOn(settings_));
Log::printf("ACTION", "menu item=%u changed direction=%+d frequency=%u max-pulse=%u min-pulse=%u accuracy=%u time=%u light=%s",
menuItem_, d, settings_.frequencyIndex, settings_.maxPulseIndex,
settings_.minPulseIndex, settings_.accuracyIndex, settings_.timeIndex,
lightCodeName(static_cast<LightCode>(settings_.lightCode)));
configuredLevelName(settings_));
showMenu();
}
@@ -712,6 +730,13 @@ void App::showMenu() {
label = UiText::MENU_TEST_TIME;
break;
case 5: {
if (static_cast<TestKind>(settings_.testKind) != TestKind::DRIVER) {
snprintf(value, sizeof(value), "%s", UiText::LIGHT_AUTO);
label = UiText::MENU_LIGHT_CODE;
formatMenuLine(label, value, one, sizeof(one));
display_.show(one, UiText::LIGHT_AUTO_FORMAT);
return;
}
const char *code = lightCodeName(static_cast<LightCode>(settings_.lightCode));
snprintf(value, sizeof(value), "%s", code);
label = UiText::MENU_LIGHT_CODE;
@@ -739,13 +764,13 @@ void App::startTest() {
Log::printf("TEST", "starting role=%s test=%s light=%s stages=%lu",
roleName(static_cast<Role>(settings_.role)),
testKindName(static_cast<TestKind>(settings_.testKind)),
lightCodeName(static_cast<LightCode>(settings_.lightCode)), stageCount_);
configuredLevelName(settings_), stageCount_);
if (SERIAL_MINIMAL_LOG) {
Log::printf("CONFIG", "mode=%s/%s frequency=%luHz pulse=%lu..%luns accuracy=%.2f%% time=%lums LIGHT=%s stages=%lu",
roleName(static_cast<Role>(settings_.role)),
testKindName(static_cast<TestKind>(settings_.testKind)), params_.frequencyHz,
params_.maxPulseNs, params_.minPulseNs, params_.accuracyPct, params_.testTimeMs,
lightCodeName(static_cast<LightCode>(settings_.lightCode)),
configuredLevelName(settings_),
stageCount_);
}
printConfiguration();
@@ -836,7 +861,7 @@ bool App::startLocalMeasurement(float hz, float duty) {
receiver_.plannedPulseTickHz(static_cast<uint32_t>(hz + 0.5f), duty),
PWM_SETTLE_CYCLES, params_.testTimeMs);
const bool ok = measurement_.start(hz, duty, params_.accuracyPct, params_.testTimeMs,
MEASUREMENT_AVERAGING_PERIODS, PWM_SETTLE_CYCLES, rxActiveLightOn(settings_));
MEASUREMENT_AVERAGING_PERIODS, PWM_SETTLE_CYCLES, true);
const uint32_t nominalMs = stageWallTimeMs(params_.testTimeMs,
static_cast<uint32_t>(hz + 0.5f));
const uint64_t watchdogMs = static_cast<uint64_t>(nominalMs) * 2ULL + 2000ULL;
@@ -1381,15 +1406,20 @@ void App::printConfiguration() {
Serial.printf("\nOptical Channel Tester | %s | mode=%s/%s | light=%s\n", board,
roleName(static_cast<Role>(settings_.role)),
testKindName(static_cast<TestKind>(settings_.testKind)),
lightCodeName(static_cast<LightCode>(settings_.lightCode)));
configuredLevelName(settings_));
Serial.printf("MAC=%02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
Serial.printf("GPIO PWM=%u RX=%u START=%u MODE=%u SDA=%u SCL=%u\n", GPIO_PWM, GPIO_RX,
GPIO_BUTTON_START, GPIO_BUTTON_MODE, GPIO_SDA, GPIO_SCL);
Serial.printf("Test %lu Hz, pulse %lu..%lu ns, accuracy %.2f%%, %lums, TX light=%c RX active light=%c\n",
params_.frequencyHz, params_.maxPulseNs, params_.minPulseNs,
params_.accuracyPct, params_.testTimeMs,
lightCodeName(static_cast<LightCode>(settings_.lightCode))[0],
lightCodeName(static_cast<LightCode>(settings_.lightCode))[1]);
if (static_cast<TestKind>(settings_.testKind) == TestKind::DRIVER) {
const char *code = lightCodeName(static_cast<LightCode>(settings_.lightCode));
Serial.printf("Test %lu Hz, pulse %lu..%lu ns, accuracy %.2f%%, %lums, TX light=%c RX active light=%c\n",
params_.frequencyHz, params_.maxPulseNs, params_.minPulseNs,
params_.accuracyPct, params_.testTimeMs, code[0], code[1]);
} else {
Serial.printf("Test %lu Hz, pulse %lu..%lu ns, accuracy %.2f%%, %lums, optical polarity=AUTO\n",
params_.frequencyHz, params_.maxPulseNs, params_.minPulseNs,
params_.accuracyPct, params_.testTimeMs);
}
stageCount_ = pulseWidthPointCount(params_.maxPulseNs, params_.minPulseNs);
Serial.printf("Pulse widths descending (%lu): ", stageCount_);
for (uint32_t i = 0; i < stageCount_; ++i)
@@ -1484,27 +1514,23 @@ void App::showDriverResult(const DriverStats &s, bool testPassed) {
delay, response);
} else if (s.reason != FailReason::NONE) {
snprintf(one, sizeof(one), "%s", uiFailName(s.reason));
char elapsed[12] = "---", errorDelay[12] = "---", errorPulse[12] = "---";
if (s.errorElapsedTicks) {
char elapsed[12] = "---", errorPulse[12] = "---";
if (s.errorTriggerValid) {
const uint64_t elapsedNs =
(s.errorElapsedTicks * 1000000000ULL + driverTest_.tickHz() / 2U) /
(static_cast<uint64_t>(s.errorTriggerTicks) * 1000000000ULL +
driverTest_.tickHz() / 2U) /
driverTest_.tickHz();
formatElapsedNs(elapsedNs, elapsed, sizeof(elapsed));
}
if (s.errorDelayValid) {
const uint64_t errorDelayNs =
(static_cast<uint64_t>(s.errorDelayTicks) * 1000000000ULL +
driverTest_.tickHz() / 2U) / driverTest_.tickHz();
formatElapsedNs(errorDelayNs, errorDelay, sizeof(errorDelay));
}
if (s.errorPulseValid) {
const uint64_t errorPulseNs =
(static_cast<uint64_t>(s.errorPulseTicks) * 1000000000ULL +
driverTest_.tickHz() / 2U) / driverTest_.tickHz();
formatElapsedNs(errorPulseNs, errorPulse, sizeof(errorPulse));
}
snprintf(two, sizeof(two), "T:%s D:%s P:%s",
elapsed, errorDelay, errorPulse);
if (s.errorPulseValid)
snprintf(two, sizeof(two), "T:%s P:%s", elapsed, errorPulse);
else snprintf(two, sizeof(two), "T:%s", elapsed);
} else {
formatTestTarget(requestedHz_, requestedPulseNs_, one, sizeof(one));
snprintf(two, sizeof(two), UiText::DRIVER_MEASUREMENT_FORMAT,