CAN

Controller Area Network(CAN) is a serial communication protocol that employs the usage of two differential lines as its bus. It has a shared bus, like I2C, meaning there is no true master.

Working

Bus arbitration

Each CAN node has an ID. Whenever a node wants to transmit data, it starts by transmitting their ID. The node with a lower ID "wins" the arbitration. A node can read back the data it transmits, and if a node transmits and reads back its ID, it knows that it now has control of the bus and can transmit data.

Physical layer

There are two lines used here, they are -

  • CAN H(igh)
  • Can L(ow)
    CAN-L is the complement of the CAN-H line. Depending on the standard, if the difference between voltages on both the lines is above a certain threshold (usually 2V), then it is called dominant voltage, and the bit is read as 1. Similarly, if the difference is close to 0, it is called recessive voltage and is read as 0.

The CAN lines are twisted together (to reduce EM interference) and also connected with a resistor to reduce reflections. The default idle state is the difference between CANH and CANL being close to 0 (recessive).

Each CAN signal (or dataframe) has 64 bits. A dataframe has this structure -

  • SOF (Start Of Frame) - 0
  • ID - 11 bits for standard CAN, 29 for extended CAN. Is used for arbitration. Also has a stuff bit in between just as a check.
  • DLC (Data length code) - The length of the data and a few more bits. Research required
  • Data bits
  • CRC bits
  • ACK and ACK delimiter
  • EOF - Goes back to idle for rest of the bits
  • There is also a 3 bit gap between CAN frames

Libraries and utils

can-utils

The can-utils metapackage comes with tools like cansend and candump. cansend <id>#<data> can be used to send data onto the bus with a specified CAN ID. candump -x any dumps data from the CAN bus which was sent by any of the nodes on the bus.

python-can

An example message being sent

with can.Bus() as bus:
	msg = can.Message(arbitration_id=0x123456, data=[1, 2, 3], is_extended_id=True)
	bus.send(msg) # should be in a try catch block, since arbitration might fail