265 lines
25 KiB
Markdown
265 lines
25 KiB
Markdown
# Optical Channel Tester — ESP32-C3 / ESP32-S3
|
||
|
||
Полностью рабочий Arduino IDE-проект для строгой проверки оптического цифрового канала. Устройство формирует PWM на выбранной частоте, последовательно уменьшает длительность импульса по заданному списку и проверяет ответ канала: частоту и длительность импульса. Поддерживаются роли `SOLO`, `MASTER` и `SLAVE`; роли выбираются только вручную.
|
||
|
||
## Что реализовано
|
||
|
||
- неблокирующий конечный автомат без `pulseIn()` и длинных `delay()`;
|
||
- аппаратные LEDC (C3) и MCPWM (S3) с расчётом реально получившихся частоты и длительности импульса;
|
||
- Arduino-ESP32 3.3.10: на S3 два аппаратных канала MCPWM Capture из отдельной группы фиксируют RISING и FALLING в независимых регистрах общего 32-битного таймера 80 МГц;
|
||
- безопасный fallback для старых Arduino-ESP32 3.x через GPIO edge ISR и аппаратный CPU cycle counter (верхний предел при этом автоматически ограничен 100 кГц);
|
||
- отбрасывание ровно `PWM_SETTLE_CYCLES` полных периодов;
|
||
- потоковая статистика без хранения всех периодов;
|
||
- немедленный FAIL по первому отдельному импульсу или периоду вне допуска;
|
||
- десять измерительных участков на каждой длительности импульса с промежуточным отображением результата;
|
||
- ESP-NOW discovery, handshake, CRC, session/stage/sequence, ACK, повторы и защита от старых пакетов;
|
||
- SSD1306 128×32: каждый экран всегда состоит ровно из двух строк;
|
||
- две кнопки, debounce, long press и repeat; после long press ложный short click не создаётся;
|
||
- Preferences/NVS с версией структуры, checksum, проверкой индексов и восстановлением defaults;
|
||
- работа через Serial при отсутствующем OLED;
|
||
- безопасное выключение PWM при PASS, FAIL, ABORT и потере связи.
|
||
- полный журнал действий в Serial с временными метками; OLED необязателен.
|
||
|
||
## Файлы
|
||
|
||
Arduino sketch находится в каталоге `OpticalChannelTester`:
|
||
|
||
- `OpticalChannelTester.ino` — стандартная точка входа Arduino IDE;
|
||
- `Config.h` — GPIO, тайм-ауты, пределы и все пользовательские массивы;
|
||
- `Core.*` — последовательность длительностей, ALL, допуски, статистика;
|
||
- `Buttons.*` — автомат двух кнопок;
|
||
- `SettingsStore.*` — NVS;
|
||
- `Display.*` — OLED и компактное форматирование;
|
||
- `Log.*` — журнал действий и Serial-зеркало интерфейса;
|
||
- `Pwm.*` — LEDC / MCPWM;
|
||
- `Receiver.*` — MCPWM Capture на S3 / совместимый GPIO fallback;
|
||
- `Measurement.*` — строгая проверка периодов;
|
||
- `Protocol.*`, `Radio.*` — ESP-NOW;
|
||
- `App.*` — общий конечный автомат SOLO/MASTER/SLAVE.
|
||
|
||
## Требования Arduino IDE
|
||
|
||
Проверенная конфигурация:
|
||
|
||
- Arduino IDE 2.x;
|
||
- пакет плат **esp32 by Espressif Systems 3.3.10**;
|
||
- **Adafruit GFX Library 1.12.1**;
|
||
- **Adafruit SSD1306 2.5.15**;
|
||
- Adafruit BusIO устанавливается Library Manager как зависимость.
|
||
|
||
В Arduino IDE откройте `OpticalChannelTester/OpticalChannelTester.ino`. Не переносите один `.ino` отдельно: остальные вкладки являются частью скетча.
|
||
|
||
Для C3 выберите подходящую плату ESP32-C3, например `ESP32C3 Dev Module`. Для S3 — `ESP32S3 Dev Module`. Затем выберите порт и нажмите Verify/Upload.
|
||
|
||
## 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 |
|
||
| Optical RX input | GPIO 4 | GPIO 13 |
|
||
| START | GPIO 10 | GPIO 4 |
|
||
| MODE | GPIO 20 | GPIO 5 |
|
||
| VBAT | GPIO 2 | GPIO 11 |
|
||
| Analog RX | GPIO 0 | GPIO 9 |
|
||
| OLED SDA | GPIO 6 | GPIO 44 |
|
||
| OLED SCL | GPIO 7 | GPIO 1 |
|
||
|
||
Оба профиля рассчитаны на установку S3 с совмещением контактов `5V` и `GND` с модулем C3. Номера GPIO S3 выбраны по тому же физическому ряду разъёма, а не по совпадению номера GPIO.
|
||
|
||
На ESP32-C3 PWM формируется через LEDC с аппаратным дробным делителем Q10.8. На ESP32-S3 используется отдельный MCPWM со счётчиком 20 МГц: это позволяет формировать 500 Гц в пределах 16-битного счётчика и сохраняет шаг импульса 50 нс. `PWM_ACTIVE_LEVEL` задаёт электрический уровень активной части тестового импульса на обеих платах; остальная часть работающего PWM имеет противоположный уровень. `PWM_SAFE_LEVEL` применяется только при остановленном PWM и во сне. Перед началом точки прошивка проверяет, что фактические частота и длительность импульса укладываются в выбранную точность.
|
||
|
||
### OLED
|
||
|
||
Подключите SSD1306 128×32: `VCC → 3.3 V`, `GND → GND`, `SDA/SCL` по таблице. Адрес по умолчанию `0x3C`. Если OLED не отвечает, тест продолжает работать и пишет диагностику в Serial 115200.
|
||
|
||
### Работа вообще без OLED
|
||
|
||
OLED можно не подключать. Откройте Serial Monitor на **115200 baud**: каждый экран всегда дублируется одной строкой вида:
|
||
|
||
```text
|
||
[ 1250][UI ] MODE: SOLO | START=RUN
|
||
```
|
||
|
||
Полоса прогресса учитывает номер длительности импульса и десять измерительных участков внутри неё.
|
||
|
||
В Serial также выводятся:
|
||
|
||
- каждое распознанное нажатие START/MODE и текущее состояние автомата;
|
||
- вход, изменение и сохранение каждого пункта меню;
|
||
- загрузка, проверка и сохранение NVS;
|
||
- запуск/остановка PWM и реальные параметры LEDC;
|
||
- начало этапа, число отбрасываемых периодов и параметры измерительного окна;
|
||
- все действия ESP-NOW, peer, session/stage/sequence, ACK и повторы;
|
||
- статистика этапа, первый плохой период и итоговая причина завершения;
|
||
- все строки, которые были бы показаны на OLED.
|
||
|
||
Во время строгого измерительного окна отдельные импульсы намеренно не печатаются: они проверяются потоково, а итоговая статистика выводится после окна. Это предотвращает влияние Serial на точность и переполнение очереди аппаратных фронтов. Журнал действий включён параметрами в `Config.h`:
|
||
|
||
```cpp
|
||
constexpr bool SERIAL_ACTION_LOG = true;
|
||
constexpr bool SERIAL_LOG_TIMESTAMPS = true;
|
||
```
|
||
|
||
### Кнопки
|
||
|
||
По умолчанию задано:
|
||
|
||
```cpp
|
||
#define BUTTON_ACTIVE_LEVEL LOW
|
||
```
|
||
|
||
Поэтому каждая кнопка подключается между своим GPIO и GND, а прошивка автоматически включает `INPUT_PULLUP`.
|
||
|
||
Если установить `BUTTON_ACTIVE_LEVEL HIGH`, подключайте кнопку между GPIO и 3.3 V; автоматически будет использован `INPUT_PULLDOWN`. Других изменений логики не требуется.
|
||
|
||
## Подключение сигнала
|
||
|
||
### SOLO
|
||
|
||
```text
|
||
ESP GPIO_PWM -> вход передатчика оптического канала
|
||
выход приёмника оптического канала -> ESP GPIO_RX
|
||
GND ESP -> GND входной/выходной электроники (если канал не гальванически развязан)
|
||
```
|
||
|
||
### MASTER / SLAVE
|
||
|
||
```text
|
||
MASTER GPIO_PWM -> вход передатчика проверяемого канала
|
||
выход приёмника проверяемого канала -> SLAVE GPIO_RX
|
||
MASTER <~~~~ ESP-NOW Wi-Fi channel 6 ~~~~> SLAVE
|
||
```
|
||
|
||
На обеих ESP должны совпадать `ESPNOW_WIFI_CHANNEL` и версия прошивки. Slave сначала переводится в `SLAVE READY` кнопкой START, затем START нажимается на Master.
|
||
|
||
Пока бодрствующий Slave находится в `SLAVE READY`, радиоприём работает периодически: окно 20 мс каждые 100 мс, а собственный оптический передатчик Slave отключён уровнем `PWM_SAFE_LEVEL`. При переходе Slave в light sleep ESP-NOW и Wi-Fi полностью отключаются, поэтому радиопакет не может его разбудить. Master во время поиска посылает серию `DISCOVER` каждые 20 мс и одновременно переключает оптический выход между `PWM_ACTIVE_LEVEL` и `PWM_SAFE_LEVEL` каждые `OPTICAL_WAKE_HALF_PERIOD_MS`. Изменение уровня на оптическом входе пробуждает Slave через GPIO; после этого Slave запускает ESP-NOW и принимает повторяемый `DISCOVER`. Постоянный HIGH или LOW сам по себе повторных пробуждений не вызывает. После обнаружения Master прекращает wake-сигнал, а Slave включает непрерывный радиоприём на всю тестовую сессию. Параметры задаются в `Config.h`.
|
||
|
||
**GPIO ESP32 допускают только логические уровни 0…3.3 В.** Не подавайте 5 В на `GPIO_RX`; применяйте согласование уровня. Полярность выхода оптического приёмника задаётся однозначно его активным уровнем:
|
||
|
||
Базовый активный уровень входа задаёт `RX_ACTIVE_LEVEL`, но во время теста полярность определяется автоматически по первому полному периоду и фиксируется до следующего запуска приёмника. Прошивка сравнивает длительности обоих уровней с заданным импульсом и использует более близкую. Например, для 2 кГц и 200 мкс автоматически выбирается 200 мкс, а не дополнение 300 мкс. При заполнении 50% обе полярности эквивалентны.
|
||
|
||
## Управление
|
||
|
||
В ожидании:
|
||
|
||
- MODE short: `SOLO → MASTER → SLAVE → SOLO`, выбранная роль сохраняется;
|
||
- MODE long: открыть настройки;
|
||
- START short: начать тест;
|
||
- START long во время теста: ABORT, PWM немедленно выключается.
|
||
|
||
В настройках:
|
||
|
||
- MODE short: следующий параметр;
|
||
- MODE long: проверить диапазон, сохранить один раз в NVS и выйти;
|
||
- START short: следующее значение;
|
||
- START long: предыдущее значение, затем autorepeat при удержании.
|
||
|
||
Удержание обеих кнопок минимум 1.5 с при включении восстанавливает defaults.
|
||
|
||
### Энергосбережение
|
||
|
||
В ожидании процессор работает на 80 МГц, во время теста и связанной MASTER/SLAVE-сессии — на 160 МГц. Пока Solo или Master бодрствует и тест не идёт, выход передатчика удерживается на `PWM_ACTIVE_LEVEL`; в режиме Slave передатчик всегда отключён уровнем `PWM_SAFE_LEVEL`. При запуске теста, остановленном PWM и после тайм-аута сна также используется `PWM_SAFE_LEVEL` — уровень полного выключения с минимальным потреблением тока. После тайм-аута бездействия в `IDLE`, `FINISHED` или `SLAVE READY` OLED выключается, а ESP переходит в непрерывный light sleep без периодических таймерных пробуждений. Пока открыто меню настроек, light sleep запрещён, поэтому кнопки остаются отзывчивыми независимо от времени настройки. Экран и текущее состояние сохраняются. START и MODE будят любую роль, а фронт на оптическом входе дополнительно будит Slave. После пробуждения прошивка полностью перезапускает драйверы USB Serial и I²C, а в режиме Slave также восстанавливает ESP-NOW.
|
||
|
||
START и MODE будят устройство. Первое, пробуждающее нажатие не выполняет действие и полностью подавляется до отпускания кнопки; действие выполняет только следующее нажатие. Оптический wake-сигнал также немедленно выводит Slave из энергосбережения. Тайм-аут задаётся `IDLE_POWER_SAVE_TIMEOUT_MS` в `Config.h`.
|
||
|
||
## Настройки теста
|
||
|
||
Меню содержит пять параметров:
|
||
|
||
1. частота ШИМ из `PWM_FREQUENCY_OPTIONS_HZ`;
|
||
2. максимальная длительность импульса из отдельного `MAX_PULSE_OPTIONS_NS`;
|
||
3. минимальная длительность импульса из отдельного `MIN_PULSE_OPTIONS_NS`;
|
||
4. точность;
|
||
5. время выборки.
|
||
|
||
Тест работает на одной выбранной частоте и проходит длительности из списка от максимальной к минимальной. Значения, равные периоду или превышающие его, автоматически исключаются при выборе частоты. На S3 минимальная длительность ограничивается расчётной точностью генератора и MCPWM Capture 80 МГц: длинная пауза не снижает точность короткого импульса. Меню не позволяет задать точку, которой аппаратно не хватает выбранного допуска. Сохранённые индексы и их сочетания проверяются и приводятся к допустимому диапазону.
|
||
|
||
MAX и MIN имеют независимые массивы и независимые индексы. Сам тест проходит отдельный полный список `TEST_PULSE_WIDTHS_NS`. Все пункты меню зациклены: после последнего допустимого значения выбирается первое, а при движении назад перед первым выбирается последнее.
|
||
|
||
`ALL` пересчитывается после изменения частоты, границ импульса или времени выборки и отображается с точностью до целой секунды. Для каждой точки учитывается:
|
||
|
||
```text
|
||
max(TEST_TIME, periods / RX_PROCESSING_PERIODS_PER_SECOND)
|
||
+ PWM_SETTLE_CYCLES / actualFrequency × 10
|
||
+ OLED_PROGRESS_UPDATE_MS × 11
|
||
```
|
||
|
||
`TEST TIME` — это чистое время выборки сигнала, а не полная длительность этапа. На высоких частотах полная длительность заметно возрастает из-за проверки каждого периода. Расчёт использует консервативную производительность 300 тысяч периодов в секунду.
|
||
|
||
## Строгая проверка
|
||
|
||
После каждой перенастройки PWM приёмник:
|
||
|
||
1. автоматически определяет полярность Rx; отдельные каналы MCPWM Capture S3 фиксируют RISING и FALLING в независимых регистрах общего 32-битного таймера 80 МГц, чтобы близкий второй фронт не перезаписал первый;
|
||
2. отбрасывает ровно `PWM_SETTLE_CYCLES` полных периодов;
|
||
3. очищает статистику;
|
||
4. непрерывно держит MCPWM Capture включённым на всём измерительном этапе, проверяет каждый полный импульс ровно один раз и на десяти границах TEST TIME показывает промежуточный результат; границы прогресса не останавливают capture и не выбрасывают пересекающий их импульс. Между длительностями сначала останавливается PWM, затем capture, поэтому очистка очереди не пересекается с ISR.
|
||
|
||
Каждый полный период и каждый активный импульс проверяются отдельно с выбранным относительным допуском 1%, 2%, 5% или 10%. Поэтому единичное искажение не растворяется в среднем остальных импульсов. После трёх начальных фронтов приёмник работает как простой автомат чередующихся интервалов «импульс — пауза»: тип последующих фронтов не используется, а лишний или потерянный фронт превращается в конкретный `PULSE OUT` или `PERIOD OUT`, не в абстрактный `GLITCH`. Для одного измерения допускается только коррекция на один такт приёмного таймера в сторону заданного значения — это неизбежная неопределённость фиксации фронта. Если после неё конкретная частота или длительность остаётся вне допуска, тест немедленно завершается ошибкой. Накопленное среднее используется только для строк `F:..., P:...` и итогового журнала. Переполнение очереди фронтов немедленно даёт `DATA LOSS ERROR`.
|
||
|
||
Во время этапа OLED показывает заданную точку и измеренный ответ, например:
|
||
|
||
```text
|
||
TEST: 2kHz, 2us
|
||
F:2.000k, P:2.00u
|
||
```
|
||
|
||
При ошибке сохраняется прежний двухстрочный стиль с конкретным значением,
|
||
вышедшим за допуск:
|
||
|
||
```text
|
||
FAIL AT 2kHz, 200us
|
||
PULSE OUT 300.01u
|
||
```
|
||
|
||
В MASTER/SLAVE передаются выбранная и фактическая длительность импульса и измеренный результат. На принимающей стороне полярность Rx определяется автоматически.
|
||
|
||
Незавершённый период в начале и конце временного участка не учитывается. Общая статистика и отображаемое среднее по всем принятым полным периодам этапа сохраняются, но решение PASS/FAIL принимается по каждому импульсу отдельно.
|
||
|
||
Причины: `NO SIGNAL`, `PERIOD OUT`, `DUTY OUT` (в интерфейсе — ошибка длительности импульса), `EXTRA EDGE`, `GLITCH`, `LOST EDGE`, `DATA LOSS ERROR`, `LINK LOST`, `UNSUPPORTED`, `RESOLUTION`, `ABORTED`.
|
||
|
||
### Пределы
|
||
|
||
- C3 и S3: настраиваемый строгий предел до 1 МГц;
|
||
- выше строгого аппаратного предела выдаётся `UNSUPPORTED`, ослабленной проверки нет;
|
||
- комбинации, для которых PWM или аппаратный capture не дают выбранную точность, отсекаются ещё в меню; повторная проверка перед стартом остаётся защитой от аппаратного сбоя.
|
||
|
||
Реальный предел зависит от конкретной платы, разводки, формы фронтов, Wi-Fi нагрузки и оптического оборудования. Начинайте проверку с 2 кГц и импульсов 200…2 мкс.
|
||
|
||
## Первый запуск SOLO
|
||
|
||
1. Соберите схему SOLO и ещё раз убедитесь, что на `GPIO_RX` не бывает напряжения выше 3.3 В.
|
||
2. Откройте Serial Monitor на 115200 baud.
|
||
3. Включите плату. На OLED должно быть `MODE: SOLO` / `START=RUN`.
|
||
4. Если выбран другой режим, коротко нажимайте MODE до SOLO.
|
||
5. Для первого опыта оставьте defaults: 2 кГц, импульсы 200…2 мкс, accuracy 5%. Полярность Rx определяется автоматически.
|
||
6. Коротко нажмите START.
|
||
7. Serial покажет запрошенные и фактические параметры PWM, список длительностей, ALL и статистику каждой точки.
|
||
8. Успех: `PASS ...` / `START=AGAIN`. Ошибка: `FAIL AT ...` и точная причина.
|
||
|
||
## Проверка проекта
|
||
|
||
Целевая версия проекта — Arduino-ESP32 3.3.10. В доступной среде исходный код собран Arduino CLI с этой версией:
|
||
|
||
| Target | Arduino-ESP32 | Flash | RAM | Результат |
|
||
|---|---:|---:|---:|---|
|
||
| ESP32-C3 | 3.3.10 | 1,045,075 B (79%) | 41,012 B (12%) | PASS |
|
||
| ESP32-S3 | 3.3.10 | 976,233 B (74%) | 49,812 B (15%) | PASS |
|
||
|
||
Физическое оборудование в этой среде недоступно, поэтому реальные оптические фронты, MCPWM Capture под длительной нагрузкой, дальность ESP-NOW и электрическая совместимость должны быть проверены на ваших платах. Сборка и программные тесты не выдаются за аппаратный тест.
|