Chapter 22 — CAN Login Sequence
Before a node can participate on a CAN bus, it must allocate a unique 12-bit alias through the CAN login sequence defined in the OpenLCB CAN Frame Transfer Standard. This chapter details the CID frame construction, LFSR seed generation, alias extraction, timing requirements, and collision handling, as implemented in can_login_statemachine.c and can_login_message_handler.c.
22.1 Login Sequence Overview
The CAN login is a 10-state process driven by the CAN login state machine. Each call to CanLoginStateMachine_run() dispatches to the handler for the current run_state:
22.2 LFSR Seed Generation
The OpenLCB CAN Frame Transfer Standard specifies a Linear Feedback Shift Register (LFSR) algorithm for generating unpredictable aliases from Node IDs. The implementation splits the 48-bit seed into two 24-bit halves:
static uint64_t _generate_seed(uint64_t start_seed) {
uint32_t lfsr2 = start_seed & 0xFFFFFF; // lower 24 bits
uint32_t lfsr1 = (start_seed >> 24) & 0xFFFFFF; // upper 24 bits
uint32_t temp1 = ((lfsr1 << 9) | ((lfsr2 >> 15) & 0x1FF)) & 0xFFFFFF;
uint32_t temp2 = (lfsr2 << 9) & 0xFFFFFF;
lfsr1 = lfsr1 + temp1 + 0x1B0CA3L;
lfsr2 = lfsr2 + temp2 + 0x7A4BA9L;
lfsr1 = (lfsr1 & 0xFFFFFF) + ((lfsr2 & 0xFF000000) >> 24);
lfsr2 = lfsr2 & 0xFFFFFF;
return ((uint64_t)lfsr1 << 24) | lfsr2;
}
The magic constants 0x1B0CA3 and 0x7A4BA9 are specified by the standard (TN section 6.1.3). The shift-and-add pattern ensures that even closely related Node IDs produce different alias sequences.
22.3 Alias Extraction
A 12-bit alias is extracted from the 48-bit seed by XOR-folding:
static uint16_t _generate_alias(uint64_t seed) {
uint32_t lfsr2 = seed & 0xFFFFFF;
uint32_t lfsr1 = (seed >> 24) & 0xFFFFFF;
return (lfsr1 ^ lfsr2 ^ (lfsr1 >> 12) ^ (lfsr2 >> 12)) & 0x0FFF;
}
Since alias 0x000 is invalid per spec, if the extraction produces zero, the seed is advanced and extraction is retried in a loop until a non-zero alias results.
22.4 CID Frame Construction
Four CID frames broadcast the 48-bit Node ID in 12-bit chunks embedded in the CAN identifier. Each frame has zero data bytes.
| Frame | CAN Identifier (29 bits) | Node ID Bits |
|---|---|---|
| CID7 | RESERVED_TOP_BIT | 0x07000000 | (node_id >> 24) & 0xFFF000 | alias |
Bits 47-36 |
| CID6 | RESERVED_TOP_BIT | 0x06000000 | (node_id >> 12) & 0xFFF000 | alias |
Bits 35-24 |
| CID5 | RESERVED_TOP_BIT | 0x05000000 | node_id & 0xFFF000 | alias |
Bits 23-12 |
| CID4 | RESERVED_TOP_BIT | 0x04000000 | (node_id << 12) & 0xFFF000 | alias |
Bits 11-0 |
The 29-bit CAN identifier is structured as: bit 28 = reserved (set to 1), bits 27-24 = CID frame number (7,6,5,4), bits 23-12 = Node ID chunk, bits 11-0 = alias.
22.5 Timing: The 200ms Wait
After transmitting CID4, the node waits at least 200ms for potential collision reports. The implementation uses the global 100ms tick counter:
void CanLoginMessageHandler_state_wait_200ms(can_statemachine_info_t *info) {
uint8_t elapsed = (uint8_t)(info->current_tick
- (uint8_t)info->openlcb_node->timerticks);
if (elapsed > 2) {
info->openlcb_node->state.run_state = RUNSTATE_LOAD_RESERVE_ID;
}
}
The 100ms timer tick granularity means the actual wait is between 200ms and 300ms. The comparison elapsed > 2 requires at least 3 tick increments (300ms worst case). The snapshot is taken when CID4 is loaded. This satisfies the spec's 200ms minimum requirement.
22.6 RID and AMD Frame Construction
| Frame | CAN Identifier | Payload | Effect |
|---|---|---|---|
| RID | RESERVED_TOP_BIT | 0x00700000 | alias |
0 bytes | Claims the alias -- other nodes must stop using it |
| AMD | RESERVED_TOP_BIT | 0x00701000 | alias |
6 bytes: Node ID (big-endian) | Defines the alias-to-Node-ID mapping for all observers. Sets is_permitted = true. |
22.7 State Machine Dispatch
The CanLoginStateMachine_run() function dispatches to the correct handler based on the node's run_state:
| run_state | Value | Handler | Description |
|---|---|---|---|
RUNSTATE_INIT | 0 | state_init | Seed = Node ID, skip to GENERATE_ALIAS |
RUNSTATE_GENERATE_SEED | 1 | state_generate_seed | Advance LFSR one step (conflict retry only) |
RUNSTATE_GENERATE_ALIAS | 2 | state_generate_alias | Extract 12-bit alias, register in table |
RUNSTATE_LOAD_CHECK_ID_07 | 3 | state_load_cid07 | Load CID7 frame |
RUNSTATE_LOAD_CHECK_ID_06 | 4 | state_load_cid06 | Load CID6 frame |
RUNSTATE_LOAD_CHECK_ID_05 | 5 | state_load_cid05 | Load CID5 frame |
RUNSTATE_LOAD_CHECK_ID_04 | 6 | state_load_cid04 | Load CID4 frame, snapshot tick |
RUNSTATE_WAIT_200ms | 7 | state_wait_200ms | Wait for collisions |
RUNSTATE_LOAD_RESERVE_ID | 8 | state_load_rid | Load RID frame |
RUNSTATE_LOAD_ALIAS_MAP_DEFINITION | 9 | state_load_amd | Load AMD frame, mark permitted |
On the very first login, RUNSTATE_INIT sets the seed to the Node ID and jumps directly to RUNSTATE_GENERATE_ALIAS, skipping RUNSTATE_GENERATE_SEED. The GENERATE_SEED state is only entered on alias conflict retry, where the seed must be advanced to produce a different alias.
22.8 Collision Detection and Re-seed Flow
If another node's frame arrives using the same alias during the CID or wait phases, the RX handler sets the is_duplicate flag in the alias mapping table. The CAN main state machine detects this and restarts the login:
22.9 The on_alias_change Callback
When a new alias is generated and registered, the on_alias_change optional callback (from can_config_t) is invoked. This allows the application to be notified of alias changes, which may be useful for debugging or for updating external mapping caches.
22.10 Source Files
| File | Purpose |
|---|---|
drivers/canbus/can_login_statemachine.c | State machine dispatcher -- maps run_state to handler function |
drivers/canbus/can_login_message_handler.c | Handler implementations: LFSR, CID/RID/AMD frame construction, timing |
drivers/canbus/can_config.c | Wiring -- builds the interface structs that connect dispatcher to handlers |