From f56595f767682187891f0747cfca1989e2660e86 Mon Sep 17 00:00:00 2001 From: Razvalyaev Date: Tue, 11 Aug 2026 21:43:43 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=B4=D0=B5=D1=84=D0=B0=D0=B9=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=B2=D0=BE=D0=B4=D0=BA=D0=B8=20=D0=BE=D0=BF=D1=82?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit запущено все на s3 скорректированны пины и шим на s3 --- OpticalChannelTester/App.cpp | 14 ++++++ OpticalChannelTester/App.h | 2 + OpticalChannelTester/Config.h | 34 +++++++++---- OpticalChannelTester/OpticalChannelTester.ino | 50 +++++++++++++++++++ OpticalChannelTester/Pwm.cpp | 5 +- README.md | 17 ++++++- 6 files changed, 109 insertions(+), 13 deletions(-) diff --git a/OpticalChannelTester/App.cpp b/OpticalChannelTester/App.cpp index 558e72a..3752112 100644 --- a/OpticalChannelTester/App.cpp +++ b/OpticalChannelTester/App.cpp @@ -127,6 +127,7 @@ void App::update() { else if (now - bootCheckStartedMs_ >= FACTORY_RESET_HOLD_MS) finishInitialization(true); return; } + serviceRxPinStateLog(); if (state_ != AppState::IDLE && state_ != AppState::MENU && state_ != AppState::FINISHED && startEvent == ButtonEvent::LONG) { abortTest(); return; } if (state_ != AppState::IDLE && state_ != AppState::MENU && state_ != AppState::FINISHED && @@ -208,6 +209,19 @@ void App::sanitizeRange() { settings_.endIndex %= countOf(END_FREQ_OPTIONS_HZ); } +void App::serviceRxPinStateLog() { +#ifdef RX_PIN_CHANGE_TEST + const bool level = digitalRead(GPIO_RX) == HIGH; + const bool outsideTest = state_ == AppState::IDLE || state_ == AppState::MENU || + state_ == AppState::SLAVE_READY || state_ == AppState::FINISHED; + if (rxPinStateKnown_ && level != rxPinState_ && outsideTest) + Log::printf("RX TEST", "GPIO=%u state=%s (%u)", GPIO_RX, + level ? "HIGH" : "LOW", level ? 1U : 0U); + rxPinState_ = level; + rxPinStateKnown_ = true; +#endif +} + void App::changeMenu(int d) { sanitizeRange(); uint8_t *value = nullptr; size_t count = 0; diff --git a/OpticalChannelTester/App.h b/OpticalChannelTester/App.h index 01f5395..76f2c9a 100644 --- a/OpticalChannelTester/App.h +++ b/OpticalChannelTester/App.h @@ -48,6 +48,7 @@ class App { void updateHeartbeat(); bool packetForCurrent(const ProtocolPacket &p) const; void serviceIdlePowerSave(); + void serviceRxPinStateLog(); void leaveIdlePowerSave(bool wakeDisplay = true); bool idlePowerSaveAllowed() const; void setActivePerformance(bool active); @@ -81,4 +82,5 @@ class App { bool stageStartConfirmed_ = false; uint32_t lastUserActivityMs_ = 0; bool idlePowerSave_ = false; + bool rxPinStateKnown_ = false, rxPinState_ = false; }; diff --git a/OpticalChannelTester/Config.h b/OpticalChannelTester/Config.h index d6aa8f3..4d25217 100644 --- a/OpticalChannelTester/Config.h +++ b/OpticalChannelTester/Config.h @@ -3,9 +3,22 @@ #include // ------------------------- Hardware configuration ------------------------- -// Uncomment for the hand-wired prototype. The production PCB assignments -// below follow the physical header positions shown in the schematic. -// #define MAKETKA +// 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 + +// Additionally log raw GPIO_RX level changes while no test is running. +//#define RX_PIN_CHANGE_TEST + +// Standalone PWM output check. While enabled, the normal application is not +// started: GPIO_PWM continuously outputs the frequency and duty below. +// Comment this define out after the hardware check. +//#define PWM_OUTPUT_TEST +constexpr uint32_t PWM_OUTPUT_TEST_FREQUENCY_HZ = 1000; +constexpr uint32_t PWM_OUTPUT_TEST_SWEEP_PERIOD_MS = 2000; +constexpr uint32_t PWM_OUTPUT_TEST_UPDATE_MS = 10; +constexpr uint8_t PWM_OUTPUT_TEST_MIN_DUTY_PCT = 5; +constexpr uint8_t PWM_OUTPUT_TEST_MAX_DUTY_PCT = 95; #if CONFIG_IDF_TARGET_ESP32C3 constexpr bool TARGET_IS_C3 = true; @@ -25,12 +38,13 @@ constexpr uint8_t GPIO_SCL = 7; #elif CONFIG_IDF_TARGET_ESP32S3 constexpr bool TARGET_IS_C3 = false; #ifdef MAKETKA -constexpr uint8_t GPIO_PWM = 4; -constexpr uint8_t GPIO_RX = 5; -constexpr uint8_t GPIO_BUTTON_MODE = 0; -constexpr uint8_t GPIO_BUTTON_START = 1; -constexpr uint8_t GPIO_SDA = 8; -constexpr uint8_t GPIO_SCL = 9; +// Same physical header contacts as the C3 MAKETKA profile when 5V/GND align. +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_SDA = 44; +constexpr uint8_t GPIO_SCL = 1; #else // The S3 SuperMini is fitted so its 5V and GND pins occupy the same PCB // contacts as on the C3 SuperMini. Signals therefore follow header position. @@ -58,7 +72,7 @@ constexpr bool SERIAL_MINIMAL_LOG = true; #define BUTTON_ACTIVE_LEVEL LOW #define RX_SIGNAL_INVERTED false -#define PWM_SAFE_LEVEL LOW +#define PWM_SAFE_LEVEL HIGH #define PWM_SETTLE_CYCLES 5U constexpr uint32_t BUTTON_DEBOUNCE_MS = 30; diff --git a/OpticalChannelTester/OpticalChannelTester.ino b/OpticalChannelTester/OpticalChannelTester.ino index 1f8a4c9..97a1d6d 100644 --- a/OpticalChannelTester/OpticalChannelTester.ino +++ b/OpticalChannelTester/OpticalChannelTester.ino @@ -1,7 +1,57 @@ #include "App.h" +#include "Config.h" +#include +#ifdef PWM_OUTPUT_TEST +PwmGenerator pwmOutputTest; +uint8_t pwmOutputTestDuty = 50; +uint32_t pwmOutputTestUpdatedMs = 0; + +void setup() { + Serial.begin(SERIAL_BAUD); + delay(200); + Serial.printf("\nPWM OUTPUT TEST: GPIO=%u requested=%luHz duty=%u..%u%% sine=%lums safe=%s\n", + GPIO_PWM, PWM_OUTPUT_TEST_FREQUENCY_HZ, + PWM_OUTPUT_TEST_MIN_DUTY_PCT, PWM_OUTPUT_TEST_MAX_DUTY_PCT, + PWM_OUTPUT_TEST_SWEEP_PERIOD_MS, + PWM_SAFE_LEVEL == HIGH ? "HIGH" : "LOW"); + + pwmOutputTest.begin(); + ActualPwm actual = {}; + if (pwmOutputTest.start(PWM_OUTPUT_TEST_FREQUENCY_HZ, + pwmOutputTestDuty, actual)) { + Serial.printf("PWM OUTPUT TEST STARTED: actual=%luHz duty=%.2f%% bits=%u\n", + actual.actualHz, actual.actualDutyPct, actual.bits); + } else { + Serial.println("PWM OUTPUT TEST FAILED"); + } +} + +void loop() { + const uint32_t now = millis(); + if (now - pwmOutputTestUpdatedMs < PWM_OUTPUT_TEST_UPDATE_MS) return; + pwmOutputTestUpdatedMs = now; + + constexpr float PWM_TWO_PI = 6.28318530718f; + const float phase = PWM_TWO_PI * (now % PWM_OUTPUT_TEST_SWEEP_PERIOD_MS) / + PWM_OUTPUT_TEST_SWEEP_PERIOD_MS; + const float center = (PWM_OUTPUT_TEST_MIN_DUTY_PCT + PWM_OUTPUT_TEST_MAX_DUTY_PCT) * 0.5f; + const float amplitude = (PWM_OUTPUT_TEST_MAX_DUTY_PCT - PWM_OUTPUT_TEST_MIN_DUTY_PCT) * 0.5f; + const uint8_t duty = static_cast(center + amplitude * sinf(phase) + 0.5f); + if (duty == pwmOutputTestDuty) return; + + ActualPwm actual = {}; + if (pwmOutputTest.start(PWM_OUTPUT_TEST_FREQUENCY_HZ, duty, actual)) { + pwmOutputTestDuty = duty; + } else { + Serial.printf("PWM OUTPUT TEST UPDATE FAILED: duty=%u%%\n", duty); + delay(100); + } +} +#else App app; void setup() { app.begin(); } void loop() { app.update(); } +#endif diff --git a/OpticalChannelTester/Pwm.cpp b/OpticalChannelTester/Pwm.cpp index 9406d42..530110c 100644 --- a/OpticalChannelTester/Pwm.cpp +++ b/OpticalChannelTester/Pwm.cpp @@ -155,7 +155,10 @@ bool PwmGenerator::start(uint32_t hz, uint8_t dutyPct, ActualPwm &a) { stop(); bool ok = mcpwm_timer_set_period(mcpwmTimer, periodTicks) == ESP_OK; ok = ok && mcpwm_comparator_set_compare_value(mcpwmComparator, activeTicks) == ESP_OK; - ok = ok && mcpwm_generator_set_force_level(mcpwmGenerator, -1, false) == ESP_OK; + // stop() applies a continuous force level (hold_on=true). Remove that same + // continuous-force action; hold_on=false addresses a different, one-shot + // force mechanism and would leave the safe level permanently active. + ok = ok && mcpwm_generator_set_force_level(mcpwmGenerator, -1, true) == ESP_OK; ok = ok && mcpwm_timer_start_stop(mcpwmTimer, MCPWM_TIMER_START_NO_STOP) == ESP_OK; if (!ok) { mcpwm_generator_set_force_level(mcpwmGenerator, PWM_SAFE_LEVEL, true); diff --git a/README.md b/README.md index 5761509..b1ad999 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,23 @@ Arduino sketch находится в каталоге `OpticalChannelTester`: Для C3 выберите подходящую плату ESP32-C3, например `ESP32C3 Dev Module`. Для S3 — `ESP32S3 Dev Module`. Затем выберите порт и нажмите Verify/Upload. -## GPIO по умолчанию +## GPIO Все назначения находятся в начале `Config.h` и могут быть изменены до сборки. +Сейчас в `Config.h` включён `#define MAKETKA`. В этом профиле используются следующие назначения: + +| Сигнал | ESP32-C3 | ESP32-S3 | +|---|---:|---:| +| PWM output | GPIO 3 | GPIO 12 | +| Optical RX input | GPIO 4 | GPIO 13 | +| START | GPIO 1 | GPIO 10 | +| MODE | GPIO 0 | GPIO 9 | +| OLED SDA | GPIO 6 | GPIO 44 | +| OLED SCL | GPIO 7 | GPIO 1 | + +Если закомментировать `#define MAKETKA`, включается профиль производственной PCB: + | Сигнал | ESP32-C3 | ESP32-S3 | |---|---:|---:| | PWM output | GPIO 3 | GPIO 12 | @@ -68,7 +81,7 @@ Arduino sketch находится в каталоге `OpticalChannelTester`: | OLED SDA | GPIO 6 | GPIO 44 | | OLED SCL | GPIO 7 | GPIO 1 | -Эти назначения рассчитаны на установку S3 в плату с совмещением контактов `5V` и `GND` с прежним модулем C3. Для проводной макетки раскомментируйте `#define MAKETKA` в начале `Config.h`: кнопки будут назначены как `MODE = GPIO 0`, `START = GPIO 1`, а остальные GPIO S3 вернутся к макетным назначениям `PWM=4`, `RX=5`, `SDA=8`, `SCL=9`. +Оба профиля рассчитаны на установку S3 с совмещением контактов `5V` и `GND` с модулем C3. Номера GPIO S3 выбраны по тому же физическому ряду разъёма, а не по совпадению номера GPIO. На ESP32-C3 PWM формируется через LEDC с целым делителем. На ESP32-S3 используется отдельный аппаратный MCPWM со счётчиком 40 МГц и произвольным целым периодом.