Chapter 3b — CAN-Specific Types and Constants
This chapter covers types defined in can_types.h that are specific to the CAN transport layer. These supplement the transport-independent types from Chapter 3.
3b.1 The can_msg_t Structure
A complete CAN 2.0B extended frame:
typedef struct can_msg_struct {
can_msg_state_t state; // Allocation flag (1-bit bitfield)
uint32_t identifier; // 29-bit extended CAN identifier
uint8_t payload_count; // Number of valid data bytes (0-8)
payload_bytes_can_t payload; // 8-byte data array
} can_msg_t;
The can_msg_state_t contains a single allocated bit, used by the CAN buffer store to track ownership:
typedef struct can_msg_state_struct {
uint8_t allocated : 1;
} can_msg_state_t;
3b.2 CAN Identifier Bit Layout
OpenLCB uses the CAN 2.0B 29-bit extended identifier. The bits are divided into fields depending on whether the frame is a CAN control frame or an OpenLCB message frame:
29-bit CAN Extended Identifier Layout:
+----+-----+--------+--------------+------------------+
| 28 | 27 | 26-24 | 23-12 | 11-0 |
+----+-----+--------+--------------+------------------+
| R | Msg | Frame | Variable | Source Alias |
| | Ind | Type / | Field / | (12 bits) |
| 0 | | Seq # | MTI field | |
+----+-----+--------+--------------+------------------+
Bit 28: Reserved (always 0)
Bit 27: 1 = OpenLCB message frame, 0 = CAN control frame
Bits 26-24: Frame Type (OpenLCB) or Sequence Number (CAN control)
Bits 23-12: Variable Field (MTI bits in messages, command in control frames)
Bits 11-0: Source Alias (12-bit CAN alias of the sending node)
Reserved
(0)"] B27["Bit 27
Msg Indicator
1=OpenLCB"] B26_24["Bits 26-24
Frame Type /
Seq Number"] B23_12["Bits 23-12
Variable Field
(MTI / Cmd)"] B11_0["Bits 11-0
Source Alias
(12 bits)"] end style B28 fill:#ffcdd2,stroke:#c62828 style B27 fill:#c8e6c9,stroke:#2e7d32 style B26_24 fill:#bbdefb,stroke:#1565c0 style B23_12 fill:#fff9c4,stroke:#f57f17 style B11_0 fill:#e1bee7,stroke:#7b1fa2
3b.3 Frame Type Constants
When bit 27 is set (OpenLCB message), bits 26-24 identify the frame type:
| Constant | Value | Bits 26-24 | Description |
|---|---|---|---|
OPENLCB_MESSAGE_STANDARD_FRAME_TYPE | 0x01000000 | 001 | Global or addressed OpenLCB message |
CAN_FRAME_TYPE_DATAGRAM_ONLY | 0x02000000 | 010 | Datagram complete in single frame |
CAN_FRAME_TYPE_DATAGRAM_FIRST | 0x03000000 | 011 | First frame of multi-frame datagram |
CAN_FRAME_TYPE_DATAGRAM_MIDDLE | 0x04000000 | 100 | Middle frame of multi-frame datagram |
CAN_FRAME_TYPE_DATAGRAM_FINAL | 0x05000000 | 101 | Final frame of multi-frame datagram |
CAN_FRAME_TYPE_RESERVED | 0x06000000 | 110 | Reserved for future use |
CAN_FRAME_TYPE_STREAM | 0x07000000 | 111 | Stream data frame |
3b.4 CAN Control Frame Constants
When bit 27 is clear (CAN control frame), bits 26-24 carry a sequence number for CID frames, or the variable field encodes the control command:
| Constant | Value | Description |
|---|---|---|
CAN_CONTROL_FRAME_CID7 | 0x07000000 | Check ID 7 -- first 12 bits of Node ID |
CAN_CONTROL_FRAME_CID6 | 0x06000000 | Check ID 6 -- 2nd 12 bits of Node ID |
CAN_CONTROL_FRAME_CID5 | 0x05000000 | Check ID 5 -- 3rd 12 bits of Node ID |
CAN_CONTROL_FRAME_CID4 | 0x04000000 | Check ID 4 -- last 12 bits of Node ID |
CAN_CONTROL_FRAME_RID | 0x00700000 | Reserve ID -- claims the alias |
CAN_CONTROL_FRAME_AMD | 0x00701000 | Alias Map Definition -- maps alias to Node ID |
CAN_CONTROL_FRAME_AME | 0x00702000 | Alias Mapping Enquiry -- query alias ownership |
CAN_CONTROL_FRAME_AMR | 0x00703000 | Alias Map Reset -- node releasing alias |
3b.5 Multi-Frame Indicator Bits
For addressed messages that span multiple CAN frames, bits 5-4 of the first data byte indicate the frame position in the sequence:
| Constant | Value | Bits 5-4 | Description |
|---|---|---|---|
MULTIFRAME_ONLY | 0x00 | 00 | Single frame (complete message) |
MULTIFRAME_FIRST | 0x10 | 01 | First frame of multi-frame sequence |
MULTIFRAME_FINAL | 0x20 | 10 | Final frame of multi-frame sequence |
MULTIFRAME_MIDDLE | 0x30 | 11 | Middle frame of multi-frame sequence |
The mask MASK_MULTIFRAME_BITS (0x30) isolates these two bits from the first data byte.
3b.6 CAN Identifier Field Masks
These masks extract individual fields from the 29-bit CAN identifier:
| Constant | Value | Extracts |
|---|---|---|
RESERVED_TOP_BIT | 0x10000000 | Bit 28 (must be 0) |
CAN_OPENLCB_MSG | 0x08000000 | Bit 27 (1 = OpenLCB message) |
MASK_CAN_FRAME_SEQUENCE_NUMBER | 0x07000000 | Bits 26-24 (frame type / seq #) |
MASK_CAN_VARIABLE_FIELD | 0x00FFF000 | Bits 23-12 (MTI / command) |
MASK_CAN_SOURCE_ALIAS | 0x00000FFF | Bits 11-0 (source alias) |
3b.7 can_statemachine_info_t
Context block passed through the CAN state machine on every iteration:
typedef struct can_statemachine_info_struct {
openlcb_node_t *openlcb_node; // Node currently being processed
can_msg_t *login_outgoing_can_msg; // Statically-allocated login frame (CID/RID/AMD)
uint8_t login_outgoing_can_msg_valid : 1; // Set when login frame needs transmitting
can_msg_t *outgoing_can_msg; // Pool-allocated reply frame; freed after TX
uint8_t enumerating : 1; // Set when handler will produce N reply frames
uint8_t current_tick; // Snapshot of global 100ms tick
} can_statemachine_info_t;
login_outgoing_can_msg points to a static variable inside can_main_statemachine.c -- do not free it. outgoing_can_msg is pool-allocated and must be freed after successful transmission.
3b.8 Alias Mapping Types
alias_mapping_t
One entry in the alias mapping table -- a Node ID / 12-bit alias pair:
typedef struct alias_mapping_struct {
node_id_t node_id; // Permanent 48-bit Node ID
uint16_t alias; // Temporary 12-bit CAN alias (0x001-0xFFF)
uint8_t is_duplicate : 1; // Set by ISR when another node claims this alias
uint8_t is_permitted : 1; // Set after successful login (AMD transmitted)
} alias_mapping_t;
alias_mapping_info_t
Container for all alias mapping entries plus a fast-check duplicate flag:
typedef struct alias_mapping_info_struct {
alias_mapping_t list[ALIAS_MAPPING_BUFFER_DEPTH]; // All registered mappings
bool has_duplicate_alias; // True if any entry has is_duplicate set
} alias_mapping_info_t;
The has_duplicate_alias flag enables the main loop to quickly check if any alias conflict needs resolution, without scanning the entire table on every iteration.
3b.9 listener_alias_entry_t
Used by the listener alias table for train consist members:
typedef struct listener_alias_entry_struct {
node_id_t node_id; // Listener Node ID (from protocol layer attach). 0 = unused.
uint16_t alias; // Resolved CAN alias (0 = not yet resolved)
} listener_alias_entry_t;
The total number of listener alias table slots is computed as:
#define LISTENER_ALIAS_TABLE_DEPTH \
(USER_DEFINED_MAX_LISTENERS_PER_TRAIN * USER_DEFINED_TRAIN_NODE_COUNT)
Alias mapping entries and listener alias entries may be written by the CAN RX interrupt (on AMD arrival) and read by the main thread. All accesses must be protected by the lock_shared_resources / unlock_shared_resources mechanism described in Chapter 6.
3b.10 CAN-Specific Constants
| Constant | Value | Description |
|---|---|---|
LEN_CAN_BYTE_ARRAY | 8 | CAN 2.0 frame data length |
OFFSET_CAN_WITHOUT_DEST_ADDRESS | 0 | Data starts at byte 0 (global messages) |
OFFSET_CAN_WITH_DEST_ADDRESS | 2 | Data starts at byte 2 (addressed messages, bytes 0-1 carry dest alias) |
LEN_CAN_FIFO_BUFFER | USER_DEFINED_CAN_MSG_BUFFER_DEPTH + 1 | FIFO slot count (extra slot so head==tail means empty) |