Сделана настройка мощности передающей оптики

This commit is contained in:
2026-08-23 18:32:47 +03:00
parent 3907cbcf77
commit 8699f8e4eb
7 changed files with 224 additions and 12 deletions

View File

@@ -14,6 +14,8 @@
#include <string.h>
namespace {
constexpr uint8_t MENU_OPTICAL_CALIBRATION_ITEM = 7;
const char *uiFailName(FailReason reason);
const char *appStateName(AppState state) {
@@ -248,6 +250,13 @@ uint8_t cycleIndex(uint8_t value, uint8_t first, uint8_t last, int direction) {
return value <= first ? last : static_cast<uint8_t>(value - 1U);
}
uint8_t nextMenuItem(uint8_t current, TestGroup group) {
if (group == TestGroup::BOARD)
return current == 0U ? MENU_OPTICAL_CALIBRATION_ITEM : 0U;
return current >= MENU_OPTICAL_CALIBRATION_ITEM ? 0U
: static_cast<uint8_t>(current + 1U);
}
bool parseUnsigned(const char *text, uint32_t &value) {
if (!text || !*text || *text == '-') return false;
char *end = nullptr;
@@ -388,18 +397,25 @@ void App::update() {
}
if (state_ == AppState::MENU) {
if (modeEvent == ButtonEvent::SHORT) {
const uint8_t menuCount = static_cast<TestGroup>(settings_.testGroup) == TestGroup::BOARD ? 1U : 7U;
menuItem_ = (menuItem_ + 1U) % menuCount; Log::printf("ACTION", "menu item selected index=%u", menuItem_); showMenu();
leaveOpticalCalibration();
menuItem_ = nextMenuItem(menuItem_,
static_cast<TestGroup>(settings_.testGroup));
Log::printf("ACTION", "menu item selected index=%u", menuItem_);
showMenu();
}
else if (modeEvent == ButtonEvent::LONG) {
leaveOpticalCalibration();
sanitizeRange(); const bool saved = store_.save(settings_); params_ = store_.params(settings_);
Log::printf("ACTION", "settings menu saved and closed, NVS=%s", saved ? "OK" : "FAILED");
state_ = AppState::IDLE; printConfiguration();
if (static_cast<TestGroup>(settings_.testGroup) == TestGroup::OPTICS &&
static_cast<Role>(settings_.role) == Role::SLAVE) armSlave();
else showIdle();
} else if (startEvent == ButtonEvent::SHORT) changeMenu(+1);
else if (startEvent == ButtonEvent::LONG || startEvent == ButtonEvent::REPEAT) changeMenu(-1);
} else if (menuItem_ != MENU_OPTICAL_CALIBRATION_ITEM &&
startEvent == ButtonEvent::SHORT) changeMenu(+1);
else if (menuItem_ != MENU_OPTICAL_CALIBRATION_ITEM &&
(startEvent == ButtonEvent::LONG || startEvent == ButtonEvent::REPEAT)) changeMenu(-1);
updateOpticalCalibration(now);
return;
}
if (state_ == AppState::SOLO_MEASURE) {
@@ -544,6 +560,7 @@ bool App::serialSettingsMutable() const {
void App::finishSerialSettingsChange() {
if (state_ == AppState::SLAVE_READY) radio_.end();
leaveOpticalCalibration();
state_ = AppState::IDLE;
sanitizeRange();
params_ = store_.params(settings_);
@@ -589,6 +606,7 @@ void App::handleSerialCommand(char *line) {
Serial.println("OK board test stopped");
stopBoardTest();
} else if (state_ == AppState::MENU) {
leaveOpticalCalibration();
state_ = AppState::IDLE; showIdle(); Serial.println("OK menu closed");
} else if (state_ == AppState::SLAVE_READY) Serial.println("OK slave is armed; no test is running");
else {
@@ -823,6 +841,9 @@ void App::showMenu() {
display_.show(one, total);
return;
}
case MENU_OPTICAL_CALIBRATION_ITEM:
enterOpticalCalibration(millis());
return;
default: return;
}
formatMenuLine(label, value, one, sizeof(one));
@@ -833,6 +854,43 @@ void App::showMenu() {
display_.show(one, total);
}
void App::enterOpticalCalibration(uint32_t now) {
if (opticalCalibrationActive_) return;
optical_current_begin();
pwm_.lightOn();
opticalCalibrationActive_ = true;
opticalCalibrationStartedMs_ = now;
opticalCalibrationUpdatedMs_ = 0;
display_.show("Idiode: --.- mA", "Vcc: --.-- V");
Log::printf("CALIB", "optical LED ON; sense GPIO=%u, VCC GPIO=%u, settling %lums",
GPIO_OPTICAL_CURRENT, GPIO_OPTICAL_VCC, OPTICAL_CURRENT_SETTLE_MS);
}
void App::updateOpticalCalibration(uint32_t now, bool force) {
if (!opticalCalibrationActive_ ||
now - opticalCalibrationStartedMs_ < OPTICAL_CURRENT_SETTLE_MS) return;
(void)force;
OpticalCurrentMeasurement reading = {};
if (!optical_current_poll(reading)) return;
opticalCalibrationUpdatedMs_ = now;
char current[32], vcc[24];
snprintf(current, sizeof(current), "Idiode: %.1f mA", reading.currentMa);
snprintf(vcc, sizeof(vcc), "Vcc: %.2f V", reading.vccVoltage);
display_.show(current, vcc);
Log::printf("CALIB",
"raw sense=%u vcc=%u | ADC sense=%.3fV vcc=%.3fV | Usense=%.3fV Vcc=%.3fV | Idiode=%.1fmA",
reading.senseRaw, reading.vccRaw, reading.senseAdcVoltage,
reading.vccAdcVoltage, reading.senseVoltage, reading.vccVoltage,
reading.currentMa);
}
void App::leaveOpticalCalibration() {
if (!opticalCalibrationActive_) return;
pwm_.stop();
opticalCalibrationActive_ = false;
Log::event("CALIB", "optical LED OFF; calibration menu left");
}
void App::startTest() {
leaveIdlePowerSave();
pwm_.stop();
@@ -1463,12 +1521,9 @@ bool App::usbHostPresent() const {
}
void App::setStandbyOpticalOutput() {
// A gate driver must never be held enabled while the tester is idle or
// showing a result. DRIVER is SOLO-only, so force real light OFF here.
if (static_cast<TestGroup>(settings_.testGroup) == TestGroup::BOARD ||
static_cast<Role>(settings_.role) == Role::SLAVE ||
static_cast<TestKind>(settings_.testKind) == TestKind::DRIVER) pwm_.stop();
else pwm_.active();
// Calibration and tests may hold the transmitter active. Idle/result
// screens must always detach PWM and restore the physical light-OFF level.
pwm_.stop();
}
void App::setActivePerformance(bool active) {

View File

@@ -3,6 +3,7 @@
#include "Display.h"
#include "DriverTest.h"
#include "Measurement.h"
#include "OpticalCurrent.h"
#include "Pwm.h"
#include "Radio.h"
#include "SettingsStore.h"
@@ -23,6 +24,9 @@ class App {
void finishInitialization(bool factoryReset);
void showMenu();
void changeMenu(int direction);
void enterOpticalCalibration(uint32_t now);
void updateOpticalCalibration(uint32_t now, bool force = false);
void leaveOpticalCalibration();
void cycleRunMode();
void sanitizeRange();
void startTest();
@@ -105,6 +109,9 @@ class App {
uint32_t boardTestUpdatedMs_ = 0;
uint32_t boardDisplayUpdatedMs_ = 0;
bool boardPwmLightOn_ = false;
bool opticalCalibrationActive_ = false;
uint32_t opticalCalibrationStartedMs_ = 0;
uint32_t opticalCalibrationUpdatedMs_ = 0;
mutable uint32_t lastUsbHostSeenMs_ = 0;
char serialLine_[96] = {};
uint8_t serialLineLength_ = 0;

View File

@@ -5,13 +5,15 @@
// ------------------------- Hardware configuration -------------------------
// Enabled for the hand-wired prototype. Comment out for the production PCB.
// Both profiles map S3 signals by physical header position with 5V/GND aligned.
//#define MAKETKA
#define MAKETKA
// Select exactly one populated receiver circuit. Use
// BOARD_RX_INTERFACE_DIGITAL for MAKETKA and boards fitted with GPIO_RX.
#define BOARD_RX_INTERFACE_ADC 1
#define BOARD_RX_INTERFACE_DIGITAL 2
#define BOARD_RX_INTERFACE BOARD_RX_INTERFACE_ADC
#ifndef BOARD_RX_INTERFACE
#define BOARD_RX_INTERFACE BOARD_RX_INTERFACE_DIGITAL
#endif
#if BOARD_RX_INTERFACE != BOARD_RX_INTERFACE_ADC && \
BOARD_RX_INTERFACE != BOARD_RX_INTERFACE_DIGITAL
@@ -38,12 +40,16 @@ constexpr uint8_t GPIO_RX = 4;
constexpr bool BOARD_ADC_AVAILABLE = false;
constexpr uint8_t GPIO_BUTTON_MODE = 0;
constexpr uint8_t GPIO_BUTTON_START = 1;
constexpr uint8_t GPIO_OPTICAL_CURRENT = 2;
constexpr uint8_t GPIO_OPTICAL_VCC = 5;
constexpr uint8_t GPIO_VBAT = UINT8_MAX;
constexpr uint8_t GPIO_ANALOG_RX = UINT8_MAX;
#else
constexpr bool BOARD_ADC_AVAILABLE = true;
constexpr uint8_t GPIO_BUTTON_MODE = 20;
constexpr uint8_t GPIO_BUTTON_START = 10;
constexpr uint8_t GPIO_OPTICAL_CURRENT = 1;
constexpr uint8_t GPIO_OPTICAL_VCC = 5;
constexpr uint8_t GPIO_VBAT = 2;
constexpr uint8_t GPIO_ANALOG_RX = 0;
#endif
@@ -58,6 +64,8 @@ constexpr uint8_t GPIO_PWM = 12;
constexpr uint8_t GPIO_RX = 13;
constexpr uint8_t GPIO_BUTTON_MODE = 9;
constexpr uint8_t GPIO_BUTTON_START = 10;
constexpr uint8_t GPIO_OPTICAL_CURRENT = 2;
constexpr uint8_t GPIO_OPTICAL_VCC = 6;
constexpr uint8_t GPIO_SDA = 44;
constexpr uint8_t GPIO_SCL = 1;
constexpr uint8_t GPIO_VBAT = UINT8_MAX;
@@ -70,6 +78,8 @@ constexpr uint8_t GPIO_PWM = 12;
constexpr uint8_t GPIO_RX = 13;
constexpr uint8_t GPIO_BUTTON_MODE = 5;
constexpr uint8_t GPIO_BUTTON_START = 4;
constexpr uint8_t GPIO_OPTICAL_CURRENT = 10;
constexpr uint8_t GPIO_OPTICAL_VCC = 6;
constexpr uint8_t GPIO_SDA = 44;
constexpr uint8_t GPIO_SCL = 1;
constexpr uint8_t GPIO_VBAT = 11;
@@ -79,6 +89,22 @@ constexpr uint8_t GPIO_ANALOG_RX = 9;
#error "Only ESP32-C3 and ESP32-S3 are supported"
#endif
static_assert(GPIO_OPTICAL_CURRENT != GPIO_PWM &&
GPIO_OPTICAL_CURRENT != GPIO_RX &&
GPIO_OPTICAL_CURRENT != GPIO_BUTTON_MODE &&
GPIO_OPTICAL_CURRENT != GPIO_BUTTON_START &&
GPIO_OPTICAL_CURRENT != GPIO_SDA &&
GPIO_OPTICAL_CURRENT != GPIO_SCL,
"Optical-current ADC GPIO conflicts with another board signal");
static_assert(GPIO_OPTICAL_VCC != GPIO_OPTICAL_CURRENT &&
GPIO_OPTICAL_VCC != GPIO_PWM &&
GPIO_OPTICAL_VCC != GPIO_RX &&
GPIO_OPTICAL_VCC != GPIO_BUTTON_MODE &&
GPIO_OPTICAL_VCC != GPIO_BUTTON_START &&
GPIO_OPTICAL_VCC != GPIO_SDA &&
GPIO_OPTICAL_VCC != GPIO_SCL,
"Optical-VCC ADC GPIO conflicts with another board signal");
constexpr uint8_t OLED_ROTATION = 0;
constexpr uint8_t OLED_ADDRESS = 0x3C;
@@ -89,6 +115,11 @@ constexpr uint32_t SERIAL_BAUD = 115200;
constexpr uint32_t SERIAL_TX_TIMEOUT_MS = 2;
constexpr uint32_t BOARD_ADC_PRINT_INTERVAL_MS = 100;
constexpr uint32_t BOARD_TEST_DISPLAY_INTERVAL_MS = 250;
constexpr float OPTICAL_VCC = 5.1f;
constexpr float OPTICAL_SENSE_R = 47.0f;
constexpr float OPTICAL_DIVIDER_RATIO = 2.0f;
constexpr uint32_t OPTICAL_CURRENT_SETTLE_MS = 20;
constexpr uint32_t OPTICAL_CURRENT_AVERAGING_MS = 200;
constexpr bool SERIAL_ACTION_LOG = true;
constexpr bool SERIAL_LOG_TIMESTAMPS = true;
constexpr bool SERIAL_MINIMAL_LOG = true;

View File

@@ -58,6 +58,7 @@ constexpr const char *MENU_MIN_PULSE = "МИН. ИМПУЛЬС:";
constexpr const char *MENU_ACCURACY = "ТОЧНОСТЬ:";
constexpr const char *MENU_TEST_TIME = "ВРЕМЯ ВЫБОРКИ:";
constexpr const char *MENU_LIGHT_CODE = "АКТ. УРОВЕНЬ:";
constexpr const char *MENU_OPTICAL_CALIBRATION = "КАЛИБР. ОПТИКИ";
constexpr const char *LIGHT_CODE_FORMAT = "TX:%c, RX:%c";
constexpr const char *LIGHT_AUTO = "АВТО";
constexpr const char *LIGHT_AUTO_FORMAT = "TX/RX: АВТО";
@@ -136,6 +137,7 @@ constexpr const char *MENU_MIN_PULSE = "MIN PULSE:";
constexpr const char *MENU_ACCURACY = "ACCURACY:";
constexpr const char *MENU_TEST_TIME = "TEST TIME:";
constexpr const char *MENU_LIGHT_CODE = "ACTIVE LEVEL:";
constexpr const char *MENU_OPTICAL_CALIBRATION = "OPTICAL CALIBRATION";
constexpr const char *LIGHT_CODE_FORMAT = "TX:%c, RX:%c";
constexpr const char *LIGHT_AUTO = "AUTO";
constexpr const char *LIGHT_AUTO_FORMAT = "TX/RX: AUTO";

View File

@@ -0,0 +1,74 @@
#include "OpticalCurrent.h"
#include "Config.h"
namespace {
struct AdcAccumulator {
uint64_t senseRaw = 0;
uint64_t vccRaw = 0;
uint64_t senseMillivolts = 0;
uint64_t vccMillivolts = 0;
uint32_t samples = 0;
uint32_t startedMs = 0;
};
AdcAccumulator accumulator;
void resetAccumulator(uint32_t now) {
accumulator = {};
accumulator.startedMs = now;
}
OpticalCurrentMeasurement finishMeasurement() {
const float senseAdcVoltage =
accumulator.senseMillivolts / (1000.0f * accumulator.samples);
const float vccAdcVoltage =
accumulator.vccMillivolts / (1000.0f * accumulator.samples);
const float senseVoltage = senseAdcVoltage * OPTICAL_DIVIDER_RATIO;
const float vccVoltage = vccAdcVoltage * OPTICAL_DIVIDER_RATIO;
const float resistorVoltage = vccVoltage - senseVoltage;
const float currentMa = resistorVoltage * 1000.0f / OPTICAL_SENSE_R;
return {
static_cast<uint16_t>(accumulator.senseRaw / accumulator.samples),
static_cast<uint16_t>(accumulator.vccRaw / accumulator.samples),
senseAdcVoltage, vccAdcVoltage, senseVoltage, vccVoltage,
currentMa > 0.0f ? currentMa : 0.0f
};
}
}
void optical_current_begin(void) {
analogReadResolution(12);
pinMode(GPIO_OPTICAL_CURRENT, INPUT);
pinMode(GPIO_OPTICAL_VCC, INPUT);
// Both 10k/10k dividers can present about 2.5 V to their ADC inputs.
analogSetPinAttenuation(GPIO_OPTICAL_CURRENT, ADC_11db);
analogSetPinAttenuation(GPIO_OPTICAL_VCC, ADC_11db);
resetAccumulator(0);
}
bool optical_current_poll(OpticalCurrentMeasurement &measurement) {
const uint32_t now = millis();
if (!accumulator.startedMs) resetAccumulator(now ? now : 1U);
accumulator.senseRaw += analogRead(GPIO_OPTICAL_CURRENT);
accumulator.senseMillivolts += analogReadMilliVolts(GPIO_OPTICAL_CURRENT);
accumulator.vccRaw += analogRead(GPIO_OPTICAL_VCC);
accumulator.vccMillivolts += analogReadMilliVolts(GPIO_OPTICAL_VCC);
++accumulator.samples;
if (now - accumulator.startedMs < OPTICAL_CURRENT_AVERAGING_MS) return false;
measurement = finishMeasurement();
resetAccumulator(now);
return true;
}
OpticalCurrentMeasurement optical_get_led_measurement(void) {
OpticalCurrentMeasurement measurement = {};
resetAccumulator(millis());
while (!optical_current_poll(measurement)) delay(1);
return measurement;
}
float optical_get_led_current_ma(void) {
return optical_get_led_measurement().currentMa;
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <stdint.h>
struct OpticalCurrentMeasurement {
uint16_t senseRaw;
uint16_t vccRaw;
float senseAdcVoltage;
float vccAdcVoltage;
float senseVoltage;
float vccVoltage;
float currentMa;
};
void optical_current_begin(void);
bool optical_current_poll(OpticalCurrentMeasurement &measurement);
OpticalCurrentMeasurement optical_get_led_measurement(void);
float optical_get_led_current_ma(void);

File diff suppressed because one or more lines are too long