Files
templates/c/global-time/README.md
T
Andrey 2da7e7b525 feat(global-time): Добавь общие часы и порт TMS320F28335
Вынеси программные часы, коррекцию по ProtoCAN RTCSETUP и атомарную метку логгера в переносимое ядро без глобального состояния.

Подключи eCAN-A и CPU Timer2 отдельным портом F28335; зафиксируй календарные границы, переполнение, задержку RX и защиту IRQ в хостовых тестах.
2026-10-23 14:16:21 +03:00

13 KiB
Raw Blame History

Глобальное время

Программные часы с тактом 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; ранее такого общего пакета в базовом протоколе не было. Подробный контракт обмена. Код верхнего уровня для автоматической отправки времени подключается в его приложении; библиотека предоставляет кодировщик и распознавание события старта.

Метка логгера

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).

#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:

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:

gt_f28335_rx_isr(); /* Копирует только MBOX7 и запоминает uptime при приёме, календарь проверяется позднее. */

Приложение должно разрешить соответствующий CAN IRQ: CANGIM.I0EN, PIE group9 INTx5, CPU M_INT9; оставить свой обработчик остальных mailbox и штатные подтверждения CANGIF/PIEACK. Порт не заменяет общий CAN ISR. Глобальный EINT выполняет приложение после полной настройки.

После прикладной инициализации и в каждом проходе главного цикла соответственно:

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:

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. Полная линковка конкретной прошивки и проверка на плате в этот набор не входят.