/* Portable continuous recorder. All addresses/counts are 16-bit WORDS, * including on C28x (CHAR_BIT=16). No heap, UART, HAL or global state. */ #ifndef GAS_LOGGER_H #define GAS_LOGGER_H #include #ifdef __cplusplus extern "C" { #endif #define GL_MAGIC 0x474CU #define GL_VERSION 1U #define GL_HEADER_WORDS 5U /* time_ms:u32, sequence:u32, event:u16, values... */ #define GL_DATA_OFFSET 0x100U #define GL_MAX_CHANNELS 64U #define GL_COMMAND 18U #define GL_RELEASE_LO 19U #define GL_RELEASE_HI 20U #define GL_START 1U #define GL_SNAPSHOT 2U #define GL_RELEASE 3U typedef enum { GL_OK=0, GL_ADDRESS, GL_ARGUMENT, GL_BUSY, GL_EMPTY, GL_STALE, GL_SOURCE_ERROR } gl_result; typedef struct { uint16_t gas, modbus, can; } gl_channel; typedef struct { uint16_t base, channels, capacity; uint32_t schema_id; const gl_channel *map; } gl_config; /* enter returns the previous IRQ state; leave MUST restore it, not enable * IRQs unconditionally. source executes under this lock: bounded RAM reads * only, never I/O/waits. Cache remote CAN values before sampling. */ typedef struct { void *user; uint32_t (*enter)(void *user); void (*leave)(void *user, uint32_t state); int (*source)(void *user, uint16_t gas, uint16_t *value); } gl_port; typedef struct { gl_config config; gl_port port; uint16_t *banks[2]; uint16_t active, count, next, recording, pinned, snap_count, snap_next; uint32_t generation, release_generation, sequence, missed, source_errors; } gas_logger; /* Each distinct bank must have >=capacity*(channels+5) uint16_t elements. * All callbacks and a nonoverlapping register map are required. */ gl_result gl_init(gas_logger *log, const gl_config *config, const gl_port *port, uint16_t *bank0, uint16_t *bank1, uint32_t bank_words); /* Called by the project timer/event hook. A failed source discards the whole * sample. Sequence/time remain uint32, wrapping modulo 2^32. */ gl_result gl_capture(gas_logger *log, uint32_t time_ms, uint16_t event); /* Snapshot swaps banks, pins the old one, and NEVER stops acquisition. * Another snapshot while pinned returns BUSY and increments missed. */ gl_result gl_snapshot(gas_logger *log); /* Atomic, all-or-nothing block read. Snapshot records are chronological; * reads past snap_count are rejected, not zero-filled. */ gl_result gl_read(gas_logger *log, uint16_t address, uint16_t *out, uint16_t count); /* No STOP/reset command. START is idempotent. RELEASE requires the generation * staged in base+19/20. One controlling client per device. */ gl_result gl_write(gas_logger *log, uint16_t address, uint16_t value); /* Register aliases for FC03/06 and ProtoCAN GAS. Transports keep their own * framing/CRC; addresses are zero-based and words preserve their values. */ gl_result gl_modbus_read(gas_logger *log, uint16_t address, uint16_t *out, uint16_t count); gl_result gl_modbus_write(gas_logger *log, uint16_t address, uint16_t value); gl_result gl_can_read(gas_logger *log, uint16_t address, uint16_t *out, uint16_t count); #ifdef __cplusplus } #endif #endif