13 KiB
GAS Logger — экспериментальная версия 1
Непрерывный регистратор с двумя кольцевыми банками, неизменяемым снимком и
общей картой GAS / ProtoCAN GAS / Modbus FC03/FC06. Весь код и проектные/MCU
адаптеры находятся в templates; приложения подключают ревизию сабмодулем.
Ядро C99 не содержит HAL, malloc, глобальных переменных, драйвера связи или
прерываний. Хранение — RAM, после отключения питания данные исчезают.
Приложение → проектный источник сигналов → gas_logger → MCU-порт блокировки.
GAS/Modbus/CAN диспетчеры → тот же экземпляр gas_logger.
Состав
| Файл | Назначение / зависимости |
|---|---|
include/gas_logger.h, src/gas_logger.c |
переносимое ядро, только стандартный C99 |
ports/pm35/ |
источники ПМ35 и кэш напряжений ПМ67, сгенерированная карта |
ports/c28x/ |
сохранение/восстановление IRQ для TI F2812/F28335 |
ports/stm32/ |
PRIMASK для STM32 F1/F4/G4, CMSIS проекта |
src/gas_logger_pcan.c |
регион сервиса pcan_gas, только MCU с поддержкой uint8_t |
../../python/set_devices/gas_logger.py |
валидация карты, генератор C, клиент снимков; stdlib |
../../python/set_devices/gas_logger_maps/pm35.json |
имена, адреса GAS/CAN/Modbus, источники и регистры сервиса |
tests/ |
ядро и сквозной тест клиента с настоящим C-регистратором |
JSON и адреса
JSON — исходное описание. ports/pm35/pm35_map.h — автоматически созданная
копия для прошивки. tools/generate_map.py --check проверяет их соответствие.
Изменения выполняются в JSON, затем запускается генератор. CRC32 канонического
JSON публикуется платой: клиент откажется читать/записывать при несовпадении
карты. Это идентификатор совместимости, не механизм аутентификации.
Все адреса — нулевые индексы 16-битных регистров, без префикса 4xxxx.
В поле can.address указан MsgBody для ProtoCAN GAS, а не полный CAN ID.
DeviceType/DeviceID/Route выбирает транспорт проекта. Идентификаторы legacy
CAN ПМ35 и ПМ67 не переименовываются в GAS: проект сначала обновляет их кэш,
после чего регистратор читает его через source.
Экспериментальный профиль ПМ35:
| GAS | Modbus FC03 | ProtoCAN GAS | Имя | Источник |
|---|---|---|---|---|
| 0x5000 | 0x5000 | 0x5000 | Команды УМП | modbus[127] |
| 0x5001 | 0x5001 | 0x5001 | Ток, 0.1 мА | modbus[28] |
| 0x5002 | 0x5002 | 0x5002 | Код ЦАП | modbus[123] |
| 0x5003 | 0x5003 | 0x5003 | Начальная уставка | modbus[80] |
| 0x5004 | 0x5004 | 0x5004 | Конечная уставка | modbus[81] |
| 0x5005 | 0x5005 | 0x5005 | ЗПТ1 | pm67_voltage[0], ранее принятый CAN адрес 305 |
| 0x5006 | 0x5006 | 0x5006 | ЗПТ2 | pm67_voltage[1], ранее принятый CAN адрес 8 |
Адреса 0x5000/0x6000 — выделение для тестового профиля, не утверждение о поддержке этих окон существующей прошивкой. Перед включением в другую карту проект обязан проверить отсутствие пересечений со своими регионами.
Карта сервиса
База одинакова для всех трёх транспортов: 0x6000; имена и смещения также
перечислены в JSON. Многословные числа: сначала младшее 16-битное слово.
Endian байтов задаёт транспорт: Modbus BE, ProtoCAN GAS LE.
| Смещение | Доступ | Значение |
|---|---|---|
| 0, 1 | R | сигнатура 0x474C, версия 1 |
| 2 | R | бит 0 запись, бит 1 снимок закреплён |
| 3..5 | R | каналов, слов в записи, ёмкость банка |
| 6, 7 | R | записей в текущем банке / снимке |
| 8..9 | R | поколение снимка u32 |
| 10..11 | R | CRC32 JSON-карты |
| 12..13 | R | следующий номер записи u32 |
| 14..15 | R | пропущенные снимки: запрос во время закреплённого снимка |
| 16..17 | R | ошибки источников при записи |
| 18 | W | 1 START, 2 SNAPSHOT, 3 RELEASE |
| 19..20 | W | поколение, разрешённое к освобождению |
| 0x100.. | R | записи снимка по прямому адресу, без команд курсора |
Запись: time_ms:u32, sequence:u32, event:u16, channels[N]:u16.
В профиле ПМ35 — 12 слов, 760 записей на банк, всего 18240 слов RAM для двух
банков. Блочное чтение разрешено по любой границе, максимум 125 слов.
Клиент собирает записи из блоков, в том числе неполных по размеру записи.
Классический CAN использует до 4 слов в кадре. Для UART подходит 120/125 слов.
Для 760 записей этого профиля требуется 76 чтений по 120 слов без записи
курсора; состав полей отличается от старого 42-словного УМП, поэтому это не
прямое измерение ускорения прежнего лога.
Непрерывная запись и снимки
START идемпотентен: включает запись, не очищает историю. STOP-команды нет.
SNAPSHOT кратко блокирует IRQ, переключает активный банк и закрепляет старый.
Во время загрузки новые записи продолжают поступать в другой кольцевой банк.
Снимок не перезаписывается, пока клиент не передаст совпадающее поколение и
RELEASE. Одновременные управляющие клиенты не поддерживаются.
Повторный SNAPSHOT при занятом снимке возвращает BUSY и увеличивает счётчик.
Пустой банк возвращает EMPTY. Долгий обрыв связи не останавливает запись, но
закреплённый снимок остаётся занят: нужно явно продолжить чтение (resume=True)
либо освободить его. Автоматического таймаута освобождения нет.
После переключения новый банк начинает историю с нуля; непрерывность означает
продолжение сбора, а не дублирование предыстории между снимками.
Триггер/постинтервал определяет проект. Например, ПМ35 вызывает gl_snapshot()
по завершении своей секунды после отключения, а не при начале события.
Этот модуль не заменяет автоматически старую логику УМП.
Контракт порта
uint32_t enter(void *user); /* сохранить и запретить IRQ */
void leave(void *user, uint32_t previous); /* восстановить ровно previous */
int source(void *user, uint16_t gas, uint16_t *value);
source выполняется под блокировкой: только быстрые чтения RAM. Ни запросов
CAN/Modbus, ни ожиданий периферии. Ошибка любого канала отбрасывает всю запись.
Кэш CAN обновляется под той же блокировкой. Для RTOS/многоядерных платформ
нужен порт с подходящей межпоточной синхронизацией; PRIMASK — для одного ядра.
На стеке блочного чтения до 125 слов; при захвате до 64 слов каналов.
Массивы банков не должны перекрываться друг с другом и контекстом.
Подключение ПМ35 / F28335
#include "gas_logger_pm35.h"
#include "gas_logger_c28x.h"
static gas_logger logger;
static uint16_t bank0[9120], bank1[9120]; /* разместить в RAM по linker map */
static gl_pm35_context source;
void logger_init(volatile uint16_t *modbus, volatile uint16_t *pm67) {
source.modbus = modbus; source.pm67_voltage = pm67;
source.enter = gl_c28x_enter; source.leave = gl_c28x_leave;
source.irq_user = 0;
if (gl_pm35_init(&logger, &source, bank0, bank1, 9120) == GL_OK)
gl_write(&logger, 0x6012, GL_START);
}
/* Из существующего таймера: gl_capture(&logger, time_ms, event); */
Включить src/gas_logger.c, ports/pm35/gas_logger_pm35.c и
ports/c28x/gas_logger_c28x.c. Include-пути — include, ports/pm35,
ports/c28x. F2812/F28335 используют одни intrinsics TI.
Для STM32 подключить ports/stm32/gas_logger_stm32.c, выбрать
GL_STM32_DEVICE_HEADER (stm32f1xx.h/stm32f4xx.h/stm32g4xx.h) и передать
gl_stm32_enter/leave. Проектный PM35-адаптер применим к STM-эмулятору только
если его массивы соответствуют указанному контракту.
FC03 диспетчер вызывает gl_modbus_read(), FC06 — gl_modbus_write().
GL_ADDRESS → exception 2, GL_ARGUMENT → 3, GL_BUSY → 6, остальные ошибки → 4.
Ответ на запись отправляется лишь после GL_OK. CRC/кадры остаются в транспорте.
Для pcan_gas добавляется регион gl_pcan_region_init(); сигнальные адреса
маршрутизируются отдельно в gl_can_read() (или уже существующие GAS-регионы).
Стандартные GAS-записи CAN не имеют ACK: CAN-адаптер клиента должен подтвердить
результат чтением статуса; просто отправить CAN-кадр недостаточно.
Передача UART/CAN выполняется после снятия IRQ-блокировки.
Клиент и проверка
from set_devices.gas_logger import GasLoggerClient
client = GasLoggerClient(read_registers, write_register, block_words=120)
client.start() # после проверки сигнатуры и JSON CRC
records = client.download() # фиксирует, читает, проверяет, освобождает
Вызовы транспорта синхронные, подтверждённые; для GUI запускать в его рабочем потоке. Исключение/отмена оставляет снимок закреплённым для явного продолжения. Записи с таймаутом автоматически не повторяются.
python c/gas-logger/tools/generate_map.py --check
python c/gas-logger/tests/run_tests.py --cc C:/msys64/mingw64/bin/gcc.exe
Либо cmake -S c/gas-logger -B <build>, сборка и ctest --test-dir <build>.
Ветка codex/test-gas-logger: потребитель SETGUI импортирует модуль из
third_party/templates и включает JSON в пакет. Производственные прошивки
ПМ35/166 пока не переключены; адреса не включены в их текущие диспетчеры.
Нужны подключение к таймеру/карте конкретной платы, проверка linker RAM и
испытание на устройстве. Проверки на хосте не измеряют аппаратные тайминги.