CAN Driver

This site was created for our work in the Embedded Systems Workshop course given in Tel-Aviv University on 2010 by Prof. Sivan Toledo.

Project authors:

Yair Hoffman [yhoffman at gmail], Ofer Sharone [ofersha2 at gmail], Ohad Shai [yael.ohad.shai at gmail]



Idea 1

Protocol 2

Hardware Used 6

Implementation 8

Problems Encountered 10




Our original fascination with CAN technology (used mainly in Automobile industries, explained below) was driven by the notion of us being able to communicate with a real car, managing to perform data 'sniffing' on the internal communications within the car. Unfortunately, since inner communication protocols of cars are vendor specific and aren't publicly disclosed, this turned out to be unfeasible within the limits of this project. Therefore, we shifted direction to the opposite: programming a micro-controller to impersonate as an automobile communicating with an OBD scanner (a protocol intended for reading diagnostic data from automobiles, based on CAN communication). To this end, we needed to implement a very basic CAN driver, covering very basic features of the protocol (i.e. sending/receiving). Since implementing the OBD protocol isnt very challenging (especially with no meaningful data representing a real car's engine's state), we decided to concentrate our efforts on creating a CAN Driver that provides easy, simple and portable API, covering all the major features of the protocol.


The following is based on info from,,


CAN is a message-based protocol, designed specifically for automotive applications but now also used in other areas such as industrial automation and medical equipment.

CAN is one of five protocols used in the OBD-II vehicle diagnostics standard. The OBD standard has been mandatory for all cars and light trucks sold in the United States since 1996, and the EOBD standard has been mandatory for all petrol vehicles sold in the European Union since 2001 and all diesel vehicles since 2004.

Since 2008 CAN is the only protocol used by OBD-II.


Controller Area Network (CAN) is a data link layer protocol. When data is transmitted, no stations are addressed, but instead, an identifier that has to be unique throughout the network designates the content of the message. The identifier defines not only the content but also the priority of the message. This is important for the bus arbitration procedure when several stations are competing for bus access. Bus access conflicts are resolved by bit-wise arbitration on the identifier bits involved by each station observing the bus level bit for bit. In accordance with the "wired- and" mechanism, by which the dominant state (logical 0) overwrites the recessive state (logical 1), the competition for bus allocation is lost by all those stations with recessive transmission and dominant observation. All "losers" automatically become receivers of the message with the highest priority and do not re-attempt transmission until the bus is available again.

Message frame formats

The CAN protocol supports two message frame formats, the only essential difference is the identifier (ID) length. In standard format (or CAN 2.0 A) the length of the ID is 11 bit and in extended format (or CAN 2.0 B) the length is 29 bit. As the two formats have to co-exist on one physical bus line it is laid down, which message has higher priority: the standard message always has higher priority to the extended message. There are some trade-off using messages in extended format: higher bandwidth requirements, longer bus latency times, and less powerful error detection capability.

CAN have four frame types:


Data frame - Base frame format

Field name

Length (bits)




Denotes the start of frame transmission



A (unique) identifier for the data which also represent the message priority

Remote transmission request (RTR)


Dominant (0) (see Remote Frame below)

Identifier extension bit (IDE)


Must be dominant (0)Optional

Reserved bit (r0)


Reserved bit (it must be set to dominant (0), but accepted as either dominant or recessive)

Data length code (DLC)*


Number of bytes of data (0-8 bytes)

Data field

0-8 bytes

Data to be transmitted (length dictated by DLC field)



Cyclic Redundancy Check

CRC delimiter


Must be recessive (1)

ACK slot


Transmitter sends recessive (1) and any receiver can assert a dominant (0)

ACK delimiter


Must be recessive (1)

End-of-frame (EOF)


Must be recessive (1)


OBD-II query using CAN (11-bit) Bus format

The PID query and response occurs on the vehicle's CAN Bus. Physical addressing uses particular CAN IDs for specific modules (e.g., 720 for the instrument cluster in Fords). Functional addressing uses the CAN ID 7DFh, to which any module listening may respond.



The vehicle responds to the PID query on the CAN bus with message IDs that depend on which module responded. Typically the engine or main ECU responds at ID 7E8h. Other modules, like the hybrid controller or battery controller in a Prius, respond at 07E9h, 07EAh, 07EBh, etc. These are 8h higher than the physical address the module responds to. Even though the number of bytes in the returned value is variable, the message uses 8 data bytes regardless. The bytes are:


Hardware Used

- 16 bit ARM7TDMI-STM based Philips Semiconductors LPC2119 board

- ELM327 OBD to RS232 Interpreter

-2x USB to RS232 adapter




OBD scanner

CAN transceiver

CAN Controller LPC2119










Source Code

The Source code can be downloaded here.

Main features of the CAN driver API

        Support different chips by implementing Hardware-specific functions and compile with them (separate file).

        Allow configuration of the CAN Controller: bit rates and bus parameters.

        Handle errors of different kinds in an appropriate manner.

        Support use of few CAN controllers allow work with logical unit of Channel.

        Use of Acceptance filter.

Software (main parts of the source code described):

Units diagram:








Internal implementation

Implementation of CAN driver interface

External interface


User API

Platform independent code

LPC21xx Specific code






























        canDriver.h: The API is documented in canDriver.h where all functions implemented by the driver that can be called by the application are described.

        canDriver.c: This is the driver's implementation itself, which relies on canHardwareInterface.h.

         canHardwareInterface.h & canLpc21xx.c: the canHardwareInterface is simply an abstraction of access to any generic CAN hardware. It is done in this manner, in order to enable future support for different types of CAN hardware (if they comply with the CAN protocol), by simply replacing only the hardware-dependent file implementing this interface, instead of the whole source code.

         User wish to enhance the driver should implement canHardwareInterface.h with his hardware specific implementation, and compile with it.

Problems Encountered

        Hardware Inconsistencies

There were several points at which the hardware itself did not react as expected by us, or as stated in the board's datasheet. For instance, we encountered the following phenomena: When writing to certain bits within some registers, other bits within that register seemed to have their value changed as well (thus, affecting other functionalities of the hardware not intended to be changed). This, for no reason and in an unpredictable manner not even anticipated by the datasheet. The problem was discovered after some functionalities of the hardware did not seem to operate properly. More specifically, exchanging messages with the CAN controller failed. It was difficult to detect the exact origin of the problem; however it was tracked down to the register holding values related to bus timing. It seemed that for some reason, even after setting its contents, it held the wrong values, causing the communication to fail. After some experiments, trying to write different values to different bits, along with checking those bits right after the writing, we noticed the core of the problem (writing values to certain bits, unexpectedly affects the values in other bits). Eventually, we could not figure out what causes this problem, as nothing is specified about it, in the hardware's documentation, and we could not figure out if this is a result of intention or a bug in the hardware. The only thing we could do to overcome it, was change the order of writing to the problematic areas of registers. That is, write the 'affecting' bits before the 'affected' ones. This locally solved the problem of communication failure, however without supplying a direct solution for preventing the bit induction phenomena.

        Static initializers

One interesting bug that weve encountered caused all our static variables to start uninitialized, although each of them was given an explicit initialization line. At first, we suspected the ld initialization file, so we compared it to the lpc2119 examples from other mpus and thoroughly read its specifications. However, it turned out that the bug was in the bootstrap code (startup.S) where the code for copying initial values from .rodata to .data depended on an #ifdef directive that never took effect. This #ifdef directive was probably supposed to be defined in another assembly file, options.S, that was missing from the package.