feat(global-time): Добавь общие часы и порт TMS320F28335
Вынеси программные часы, коррекцию по ProtoCAN RTCSETUP и атомарную метку логгера в переносимое ядро без глобального состояния. Подключи eCAN-A и CPU Timer2 отдельным портом F28335; зафиксируй календарные границы, переполнение, задержку RX и защиту IRQ в хостовых тестах.
This commit is contained in:
@@ -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. Полная линковка конкретной прошивки и проверка на плате
|
||||
в этот набор не входят.
|
||||
Reference in New Issue
Block a user