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)
flowchart LR subgraph ID["29-bit CAN Identifier"] direction LR B28["Bit 28
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:

ConstantValueBits 26-24Description
OPENLCB_MESSAGE_STANDARD_FRAME_TYPE0x01000000001Global or addressed OpenLCB message
CAN_FRAME_TYPE_DATAGRAM_ONLY0x02000000010Datagram complete in single frame
CAN_FRAME_TYPE_DATAGRAM_FIRST0x03000000011First frame of multi-frame datagram
CAN_FRAME_TYPE_DATAGRAM_MIDDLE0x04000000100Middle frame of multi-frame datagram
CAN_FRAME_TYPE_DATAGRAM_FINAL0x05000000101Final frame of multi-frame datagram
CAN_FRAME_TYPE_RESERVED0x06000000110Reserved for future use
CAN_FRAME_TYPE_STREAM0x07000000111Stream 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:

ConstantValueDescription
CAN_CONTROL_FRAME_CID70x07000000Check ID 7 -- first 12 bits of Node ID
CAN_CONTROL_FRAME_CID60x06000000Check ID 6 -- 2nd 12 bits of Node ID
CAN_CONTROL_FRAME_CID50x05000000Check ID 5 -- 3rd 12 bits of Node ID
CAN_CONTROL_FRAME_CID40x04000000Check ID 4 -- last 12 bits of Node ID
CAN_CONTROL_FRAME_RID0x00700000Reserve ID -- claims the alias
CAN_CONTROL_FRAME_AMD0x00701000Alias Map Definition -- maps alias to Node ID
CAN_CONTROL_FRAME_AME0x00702000Alias Mapping Enquiry -- query alias ownership
CAN_CONTROL_FRAME_AMR0x00703000Alias 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:

ConstantValueBits 5-4Description
MULTIFRAME_ONLY0x0000Single frame (complete message)
MULTIFRAME_FIRST0x1001First frame of multi-frame sequence
MULTIFRAME_FINAL0x2010Final frame of multi-frame sequence
MULTIFRAME_MIDDLE0x3011Middle 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:

ConstantValueExtracts
RESERVED_TOP_BIT0x10000000Bit 28 (must be 0)
CAN_OPENLCB_MSG0x08000000Bit 27 (1 = OpenLCB message)
MASK_CAN_FRAME_SEQUENCE_NUMBER0x07000000Bits 26-24 (frame type / seq #)
MASK_CAN_VARIABLE_FIELD0x00FFF000Bits 23-12 (MTI / command)
MASK_CAN_SOURCE_ALIAS0x00000FFFBits 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;
Ownership Rules

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)
Thread Safety

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

ConstantValueDescription
LEN_CAN_BYTE_ARRAY8CAN 2.0 frame data length
OFFSET_CAN_WITHOUT_DEST_ADDRESS0Data starts at byte 0 (global messages)
OFFSET_CAN_WITH_DEST_ADDRESS2Data starts at byte 2 (addressed messages, bytes 0-1 carry dest alias)
LEN_CAN_FIFO_BUFFERUSER_DEFINED_CAN_MSG_BUFFER_DEPTH + 1FIFO slot count (extra slot so head==tail means empty)
← Previous: Ch 3 — Types and Constants Next: Ch 4 — Buffer System →