CS 452/652 Spring 2026 - Lecture 3
CAN bus and Märklin protocol
May 20, 2026 prev next
MCP - MCP2515 Data Sheet
MRK - Märklin CAN Protocol
Controller Area Network (CAN)
- network protocol and standard
- physical specifications (Layer 1)
- low bitrate → robust against electrical noise
- 10s kbit/s to 10s Mbit/s range, compare 100s Gbit/s in modern Ethernet
- bit-synchronous encoding and transmission
- medium access control and addressing (Layer 2)
- multi-leader serial bus: each participant autonomously decides to send
- collision detection and arbitration needed (cf. original Ethernet)
- message identifier instead of source/destination addressing
- priority encoding ensures realtime capabilities
- 0-bit is dominant → lower identity number wins arbitration
- message types
- data frame: regular data frame
- remote frame: request data (with priority of requested data)
- error frame: transmitted by any node detecting an error
- overload frame: pacing, tell sender to slow down
- message format
- identifier (4 bytes), data length (1 byte), payload (up to 8 bytes)
- base/standard vs extended header format
- 11 bits vs. 29 bits identifier
- Märklin uses extended format
- wire format (vs. device format) - see MCP Page 10
- bit count - ID: 11/29, Meta: 6/8, DLC: 4, CRC: 16, EOF: 7, IFS: 3
- but stuffing done by hardware: every 6th bit must flip
- due to non-return-to-zero (NRZ) encoding
- 64 bits header/etc + 64 bits data + 3 bits IFS + stuffing = 160 bits
- 250 kbit/s → approx. 1562 frames/sec; 640 usec/frame
- network byte order: always big endian
- use
__htonl, etc. (man 3 htonl)
MCP 2515 controller
- configuration and control setup: see iotest
- no RX filtering
- no internal priorities
- 3 TX message buffers: TXB0/1/2
- control register: TXBnCTRL
- bit 3 - TXREQ
- check for 0 before writing frame
- set to 1 to send frame
- priority & other conditions
- message registers (MCP Pages 20-22)
- SIDH: top 8 bits standard identifier
- SIDL: low 3 bits standard identifier, EXIDE, bits 17/16 of extended identifier
- EID8, EID0: extended identifier, bits 0-15 of extended identifier
- DLC: 4 bits data length field
- Dm: 8 registers, one data byte each
- SPI interface (MCP Section 12.5)
- READ/WRITE register instructions, etc.
- sequential writes possible
- using TXB0 should be sufficient
- 2 RX message buffers: RXB0/1 (plus internal assembly buffer)
- control register: RXBnCTRL
- configure rollover, not used for filtering
- (interrupt) flag register: CANINTF
- bits 0,1: RX data available in respective buffer
- check for 1 before reading, clear after reading
- other conditions
- message registers (MCP Pages 30-32):
- SIDH: top 8 bits standard identifier
- SIDL: low 3 bits standard identifier, EXIDE, bits 17/16 of extended identifier
- EID8, EID0: extended identifier
- DLC: 4 bits data length field
- Dm: 8 registers, one data byte each
- SPI interface (MCP Section 12.3): sequential reads possible
- use RXB0, and RXB1 for overflow
- controller status instruction (MCP Section 12.8 and Figure 12-8)
- obtain CANINTF[0] (RX0IF), CANINTF[1] (RX1IF), TX0REQ, TX1REQ, TX2REQ
Märklin CAN protocol
- message format (MRK Page 5)
- extended identifier (29 bits)
- priority (4 bits), can be 0
- command (8 bits)
- response indicator (1 bit)
- hash value (16 bits): collision resolution, can use 0xC300
- CAN bus priority not really used/needed...
- dlc (4 bits): as needed
- data (0-8 bytes): depending on command
- CS3 responses
- same command, dlc, data
- response bit set
- different hash
- simple pacing: wait for response before sending next command
- speed (command 0x04, MRK Page 30)
- dlc = 6
- data 0-3: train number
- data 4-5: speed level 0..1000
- CS3 ↔ locomotive: 14 steps? (see Page 10)
- level = 1 + (step - 1) * 77
- direction (command 0x05, MRK Page 31)
- dlc = 5
- data 0-3: train number
- data 4: 1 forward, 2 backward, 3 reverse
- trains slow down gradually
- do not directly reverse a rolling train
- light (command 0x06, MRK Page 32)
- dlc = 6
- data 0-3: train number
- data 4: function 0 (light)
- data 5: 0 off, 1 on
- switch (command 0x0B, MRK Page 37)
- dlc = 6
- data 0-3: 0x3000 + switch number
- data 4: 0 curved, 1 straight
- data 5: 1 (engage)
- response with data 5 set to 1: response/confirm
- response with data 5 set to 0: solenoid off
- middle double/three-way switches: C/C and S/S not very helpful
- set one to C - the other should switch automatically to S
- sensors (command 0x11, MRK Page 40)
- 5 banks of 16 up to 16 sensors
- banks labeled as A, B, C, D, E on track
- individual sensors labeled A13, A14
- sensors are directional: A15 →, A16 ←
- asynchronous incoming message
- response bit set
- dlc = 8
- data 0,1: ignore (unused identification)
- data 2,3: (bank - 'A') * 16 + (number - 1) + 1
- data 4,5: old, new state (0 free, 1 occupied)
- data 6,7: ignore (time stamp)
- control and configuration
- command 0x00
- data 0-3: 0 (broadcast)
- subcmd in data 4
- 0x01: go (same as button)
- 0x02: halt - set speed 0 to all
- 0x00: stop (same as button)
- other incoming messages
- command 0x18 and response bit: ping
- command 0x00, subcmd 0x00, no response bit: stop via button
- command 0x00, subcmd 0x01, no response bit: go via button
Märklin Quirk
- CS3 keeps list of all "active" locomotives on track
- "Erster Befehl nimmt Lok/Funktionsdecoder in Zyklus auf."
- CS3 seems to cycle through list and refresh commands/status
- init all (potential) trains: all in cycle
- this slows down (reorders?) new commands
- remove train from cycle (command 0x00, subcmd 0x04, MRK Page 16)
- dlc = 5
- data 0-3: train number or 0 for all
- data 4: 0x04 (sub command)
- note this also seems to turn off the lights
Notes
- turn on the headlights! (verify basic communication and train direction)
- initialize track & turnouts to well-defined state
- captive loop, lights on, speed zero, forward direction
- can use wireshark to observe traffic on CAN bus