The CAN Bus is a very interesting wired bus design, coming from the automobile industry (and probably built into every European car made today). It’s a bus with an ingenious design, avoiding bus collisions and supporting a good level of real-time responsiveness.
I’ve been intrigued by this for quite some time, and decided to dive in a bit.
There are several interesting design choices in CAN bus:
- it’s all low-voltage, just 0..5V (even 0..3.3V) is all it takes on each connected node
- the bus is linear, reaching from 40 m @ 1 Mbit/s to 500 m @ 125 kbit/s, or even longer
- signalling is based on voltage between two wires, and terminated by 120 Ω on each end
- signals are self-clocked, with bit-stuffing to insert bit-transitions when needed
But the three most surprising aspects of the CAN bus design are probably the following:
- the design is such that collisions cannot happen: one of the two senders always wins
- each CAN bus packet can have at most 8 bytes of data (and is CRC-checked)
- as described recently, messages have no destination, but only a message ID (type)
What’s also interesting is that – like I2C – this protocol tends to be fully implemented in hardware, and is included in all sorts of (usually ARM-based) microcontrollers. So unlike UARTs, RS485, I2C, and SPI, you simply get complete and valid packets in and out of the peripheral. No need to deal with framing, CRC checking, or timing decisions.
You can almost feel the car-like real-time nature of these design trade-offs:
- short packets – always! – so the bus is released very quickly, and very often
- no collisions, i.e. no degradation in bus use and wasted retransmits as it gets busier
- built-in prioritisation, so specific streams can be sent across with controlled latencies
- with a 16-bit CRC on each 0..8 byte packet, chances of an undetected error are slim
Since my scope includes hardware CAN bus decoding, I decided to try it out:
The message has an ID of 0x101 (message ID’s are either 11 or 29 bits), eight bytes of data (0xAA55AA55FF00FF00), and a CRC checksum 0x1E32. I’m using a 500 KHz bit clock.
If you look closely, you can see that there are never more than 5 identical bits in a row. That’s what bit-stuffing does: insert an opposite bit to avoid longer stretches of identical bits, as this greatly helps deduce exact timings from an incoming bit-stream.
It seems crazy to limit packets to just 8 bytes – what could possibly be done with that, without wasting it all on counters and offsets to send perhaps 4 bytes of real data in each packet? As it turns out, it really isn’t so limiting – it just takes a somewhat different mindset. And the big gain is that multiple information streams end up getting interleaved very naturally. As long as each of them is reasonable, that is: don’t expect to get more than 2 or 3 data streams across a 1 Mb/s bus, each perhaps no more than 100 kb/s. Then again, you can expect these to arrive within a very consistent and predictable time, regardless of what other lower-priority burst traffic is going on.