XCP: reading and writing ECU memory over CAN
XCP (the Universal Measurement and Calibration Protocol, ASAM MCD-1) is how calibration tools peek and poke an ECU's RAM/flash live: read a memory address, write a calibration constant, or stream signal values at high rate (DAQ). It runs over many transports; this page covers XCP-on-CAN, which the sloppyCAN XCP tab decodes and can actively drive.
CRO and DTO
XCP-on-CAN uses exactly two CAN identifiers, configured from the ECU's A2L file. There's no standard pair, every project picks its own, so always check the A2L:
- CRO: Command Receive Object (master → slave). The first byte is the command code (PID); the rest are parameters.
- DTO: Data Transmit Object (slave → master). The first byte classifies the packet.
DTO first byte: the packet class
| Byte 0 | Class | Meaning |
|---|---|---|
| 0xFF | RES | Positive command response, decode it in the context of the command you sent |
| 0xFE | ERR | Error. Byte 1 is the error code (e.g. 0x20 ERR_CMD_UNKNOWN) |
| 0xFD | EV | Event packet (DAQ overload, session terminated, …) |
| 0xFC | SERV | Service request packet |
| 0x00–0xFB | DAQ | Measurement data: the first byte is the PID (ODT number); the rest is sampled signal data |
Why pairing matters: a bare FF RES carries no hint
of what it answers. The same FF … means resources+versions after CONNECT, but
raw memory bytes after UPLOAD. The XCP tab remembers the last command and decodes the
response in that context, which is what makes the log readable.
CONNECT and byte order
Everything starts with CONNECT (0xFF). The positive response tells
the master what the slave can do:
- resource bitmask: which features are available.
CAL/PAG(bit 0),DAQ(bit 2),STIM(bit 3),PGM(bit 4). - COMM_MODE_BASIC: bit 0 is the BYTE ORDER (0 = little-endian/Intel, 1 = big-endian/Motorola). Every multi-byte field after this, MAX_DTO, addresses, DAQ values, uses that order.
- MAX_CTO / MAX_DTO: the largest command/data packet the slave accepts or sends. On classic CAN these are ≤ 8.
Don't hardcode little-endian. Byte order is per-slave and only known after CONNECT. The XCP tab learns it from the response and offers a manual override for decoding traffic you captured before the CONNECT.
Reading and writing memory
| Cmd | Name | What it does |
|---|---|---|
| 0xF6 | SET_MTA | Set the Memory Transfer Address (addr ext + 32-bit address) |
| 0xF5 | UPLOAD | Read N bytes from the MTA; MTA auto-advances |
| 0xF4 | SHORT_UPLOAD | One-shot read: #bytes + address in one frame, no prior SET_MTA needed. The easiest read |
| 0xF0 | DOWNLOAD | Write N bytes to the MTA. Changes ECU memory |
The XCP tab's Read memory form sends a single SHORT_UPLOAD and
hex-dumps the response. Write memory sends SET_MTA then
DOWNLOAD, behind a second explicit confirm, because it modifies the ECU.
Active XCP is not read-only by nature. DOWNLOAD writes real calibration/RAM. The tab gates every transmit on a live bus, the listen-only checkbox being off, and a one-time session confirm; writes add their own confirm. Only point it at a bench ECU you control.
DAQ: streaming measurements
DAQ (Data AcQuisition) is XCP's high-rate measurement mode: instead of polling addresses, the master configures DAQ lists (which signals, how often) and the slave then pushes DTO packets autonomously. Each DAQ DTO's first byte is a PID identifying the ODT (object descriptor table), and the rest is packed signal data.
Full DAQ authoring is a sequence: FREE_DAQ → ALLOC_DAQ →
ALLOC_ODT → ALLOC_ODT_ENTRY → SET_DAQ_PTR →
WRITE_DAQ → SET_DAQ_LIST_MODE → START_STOP_DAQ_LIST.
The XCP tab covers the read-capability + start/stop end of this
(GET_DAQ_PROCESSOR_INFO 0xDA, START_STOP_SYNCH
0xDD) and passively classifies incoming DAQ DTOs by PID; building
DAQ lists from an A2L is left as future work.
The active session
Single transaction in flight. XCP is request/response: send one CRO, wait for its RES/ERR before the next. The tab enforces this (like the ISO-TP/UDS tab) and times out if the slave goes quiet. Turn on Demo mode to drive the whole flow against a simulated slave, including a live DAQ stream, with no hardware.
Common error codes
| Code | Name |
|---|---|
| 0x10 | ERR_CMD_BUSY |
| 0x11 | ERR_DAQ_ACTIVE |
| 0x20 | ERR_CMD_UNKNOWN, command not implemented |
| 0x21 | ERR_CMD_SYNTAX, malformed command |
| 0x22 | ERR_OUT_OF_RANGE, parameter out of range |
| 0x23 | ERR_WRITE_PROTECTED |
| 0x24 | ERR_ACCESS_DENIED |
| 0x25 | ERR_ACCESS_LOCKED, Seed & Key required |
| 0x32 | ERR_VERIFY, slave's verify routine failed |