feat(global-time): Добавь общие часы и порт TMS320F28335

Вынеси программные часы, коррекцию по ProtoCAN RTCSETUP и атомарную метку логгера в переносимое ядро без глобального состояния.

Подключи eCAN-A и CPU Timer2 отдельным портом F28335; зафиксируй календарные границы, переполнение, задержку RX и защиту IRQ в хостовых тестах.
This commit is contained in:
2026-10-23 14:16:21 +03:00
parent 62c0492bfd
commit 2da7e7b525
13 changed files with 992 additions and 0 deletions
+167
View File
@@ -0,0 +1,167 @@
# Глобальное время
Программные часы с тактом 1 мс, синхронизацией даты и времени через ProtoCAN
и атомарным получением метки для логгера. Ядро C99 не требует аппаратного RTC,
операционной системы или динамической памяти. Порт TMS320F28335 включает
eCAN-A и необязательный CPU Timer2. Все текстовые файлы — UTF-8.
## Обмен при запуске
1. Приложение инициализирует часы до разрешения IRQ и запускает источник 1 мс.
2. После завершения инициализации **всей платы** вызывает `gt_ready()` /
`gt_f28335_ready()`.
3. Из main библиотека отправляет `DISCRETE/FLAGS`, `MsgBody=0x3000`,
`Route=1`, адрес самой платы, `DLC=1`, `DATA=01`.
4. Верхний уровень распознаёт пакет через `gt_parse_ready()`, берёт свою
текущую дату и посылает **один broadcast всем платам**, используя `gt_make_rtc()`.
5. Каждый узел принимает штатный `RTCSETUP`, сравнивает время со своим,
сохраняет величину коррекции и немедленно обновляет календарные часы.
При отсутствии ответа узел повторяет готовность не чаще раза в секунду.
`RTCSETUP` полностью сохраняет действующий формат ProtoCAN.
Универсальное сообщение готовности здесь определено как новый профиль
`DISCRETE/FLAGS`; ранее такого общего пакета в базовом протоколе не было.
Подробный [контракт обмена](../../doc/setcan/protocan/GLOBAL_TIME.md).
Код верхнего уровня для автоматической отправки времени подключается в его
приложении; библиотека предоставляет кодировщик и распознавание события старта.
## Метка логгера
`gt_get()` одним атомарным чтением возвращает:
| Поле | Значение |
|---|---|
| `seconds`, `millisecond` | секунды от 2000-01-01 и доля 0..999 мс |
| `synchronized` | 0 до первого корректного RTCSETUP, 1 после |
| `uptime_ms` | время работы modulo 2^32, независимое от переводов часов |
| `sync_uptime_ms` | момент приёма последнего опорного пакета |
| `generation` | номер принятой коррекции |
| `correction_ms` | последнее смещение, знаковое 64-битное значение в мс |
| `weekday` | фаза недели 0..6, продвигаемая при смене суток |
`gt_timestamp_ms()` формирует 64-битную метку от 2000 года.
`gt_calendar()` преобразует снимок в календарь и возвращает 0, пока дата неизвестна.
Для записей, которые должны различать переводы часов, сохраняйте также
`generation`, `synchronized` и/или `uptime_ms`. Календарное время может идти назад
после коррекции. Тайм-ауты следует считать по разности `uptime_ms`.
Без новых broadcast часы продолжают ход по локальному генератору;
возраст синхронизации равен `(uint32_t)(uptime_ms - sync_uptime_ms)`.
```c
#include "global_time_f28335.h" /* Подключает общую временную базу F28335. */
gt_stamp stamp; /* Содержит согласованную дату, миллисекунды и качество времени. */
gt_get(gt_f28335_clock(), &stamp); /* Допускается из main и ISR, включая ISR логгера. */
uint64_t time_ms = gt_timestamp_ms(&stamp); /* Перед сохранением календарной метки проверяется stamp.synchronized. */
```
Это фрагмент для функции потребителя. Формат хранения существующего логгера
библиотека не меняет. В частности, старое 32-битное поле `time_ms` в GAS logger
и v4 УМП не вмещает календарные миллисекунды: потребителю нужен собственный
формат абсолютной метки либо отдельная временная база и номер поколения.
## Подключение TMS320F28335
Добавьте в сборку `src/global_time.c` и
`ports/tms320f28335/global_time_f28335.c`. Include-пути: `include`,
`ports/tms320f28335`, штатные TI `DSP2833x_headers/include`,
`DSP2833x_common/include` и include компилятора.
Ресурсы порта:
| Ресурс | Назначение |
|---|---|
| eCAN-A MBOX7 | RX точного RTCSETUP, любой приоритет, IRQ line0 |
| eCAN-A MBOX8 | TX готовности, ACK проверяется в main |
| CPU Timer2 / INT14 | необязательный собственный такт 1 мс |
Mailbox 0..6, скорость CAN, GPIO и глобальные маршруты PIE/CANGIM настраивает
приложение. `DBO=0` обязателен; порт отвергает другую раскладку октетов.
MBOX7 использует CANOPC: кадр защищён от аппаратной перезаписи до копирования ISR.
В программной очереди хранится последний broadcast, поскольку более новый
опорный пакет заменяет предыдущий.
При запрещённых IRQ, **после** `InitECan` и `InitPieVectTable`:
```c
int ok = gt_f28335_init_periph(5U); /* PERIPH5=Type1/ID5, то есть УМП1; номер берётся из конфигурации платы. */
if (ok) ok = gt_f28335_timer2_start(150000000UL); /* Частота CPU задаётся проектом в Гц; пример соответствует 150 МГц. */
```
`gt_f28335_init()` позволяет передать Type и ID непосредственно.
`init_periph()` поддерживает физические PERIPH 0..31: Type1/2 и ID0..15;
5 — УМП1, 6 — УМП2, 8 — задатчик. Порт инициализируется до подключения hooks.
Повторный init разрешён только при остановленном собственном Timer2,
отсутствующем TX и запрещённых глобальных IRQ.
Если Timer2 занят RTOS или приложением, **не вызывайте** `timer2_start()`;
в существующем ISR с периодом ровно 1 мс вызывайте `gt_f28335_tick_1ms()`.
Используйте один источник такта. Отдельный Timer2 продолжает отсчёт и при
медленном main, но длительная блокировка IRQ на несколько миллисекунд приводит
к потере тиков: аппаратный флаг прерывания не считает пропущенные периоды.
В существующем `ECAN0INTA` ISR перед очисткой общих CAN-флагов и PIEACK:
```c
gt_f28335_rx_isr(); /* Копирует только MBOX7 и запоминает uptime при приёме, календарь проверяется позднее. */
```
Приложение должно разрешить соответствующий CAN IRQ: `CANGIM.I0EN`,
PIE group9 `INTx5`, CPU `M_INT9`; оставить свой обработчик остальных mailbox
и штатные подтверждения CANGIF/PIEACK. Порт не заменяет общий CAN ISR.
Глобальный `EINT` выполняет приложение после полной настройки.
После прикладной инициализации и в каждом проходе главного цикла соответственно:
```c
gt_f28335_ready(); /* Выполняется один раз после загрузки параметров и запуска прикладных подсистем. */
gt_f28335_service(); /* Выполняется регулярно в main; обработка календаря и CAN не ждёт аппаратного ACK. */
```
Все атомарные чтения и обновления используют TI
`__disable_interrupts()` / `__restore_interrupts()`, сохраняя прежний INTM.
Транспорт ждёт подтверждение CANTA асинхронно и не перезаписывает занятый MBOX8.
При отсутствии других CAN-узлов передача остаётся аппаратному контроллеру
до восстановления шины. CAN ACK не подтверждает обработку пакета мастером;
приход корректного RTCSETUP прекращает повторные объявления готовности.
## Точность и календарь
Разрешение локальных часов — 1 мс. Действующий RTCSETUP передаёт только целые
секунды, поэтому сам формат **не гарантирует синхронизацию плат с точностью 1 мс**.
Мастер должен посылать актуальное значение около секундной границы.
Арбитраж CAN и задержка до RX ISR остаются погрешностью; задержка после RX ISR
до обработки в main компенсируется по монотонному счётчику. Для точности между
платами до 1 мс понадобится согласованное расширение проводного протокола.
Одна шкала времени выбирается верхним уровнем (рекомендуется UTC); кадр не
содержит часовой пояс. Входные годы 2000..2099, дата проверяется по григорианскому
календарю. Формат дня недели 0..6 соответствует проверке существующего
STM32-порта; профиль обозначает 0 как воскресенье. Для совместимости переданная
фаза недели сохраняется без дополнительной проверки соответствия дате.
Некоторые старые клиенты отображают неделю как 1..7; перед отправкой им нужен
перевод воскресенья 7 → 0.
Секундная шкала uint32_t от 2000 года переполнится в феврале 2136 года.
Период между захватом RX uptime и `gt_receive()` должен быть меньше 2^32 мс.
## Проверки
Из корня templates:
```text
python c/global-time/tests/run_tests.py
```
`--cc` задаёт GCC/Clang. Для отдельной TI C28x проверки передайте
`--ti-cgt` (каталог компилятора), `--ti-headers` (DSP2833x_headers/include)
и при необходимости `--ti-common` (DSP2833x_common/include).
Объекты и EXE создаются во временном каталоге. CMake также предоставляет
`global_time`, два CTest и опции `GLOBAL_TIME_BUILD_F28335` /
`GLOBAL_TIME_BUILD_TESTS`; MCU проект задаёт свои TI include-пути.
Проверяются ошибки даты и кадра, високосный февраль, смена года, коррекции
в обе стороны, компенсация очереди через переполнение uptime, ранний broadcast,
повторы готовности, сохранение IRQ, изоляция eCAN mailbox, аппаратный ACK,
значения периода Timer2 и его реальный ISR через назначенный вектор.
TI-проверка компилирует исходники с large/COFF ABI и настоящими заголовками
проекта 28335_periph. Полная линковка конкретной прошивки и проверка на плате
в этот набор не входят.