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]
TOC:
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 isn’t 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 http://www.can-cia.org, http://en.wikipedia.org/wiki/Controller_area_network,
http://en.wikipedia.org/wiki/OBD-II_PIDs
Overview
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.
Description
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) |
Purpose |
Start-of-frame |
1 |
Denotes the start of frame transmission |
Identifier |
11 |
A (unique) identifier for the data which also
represent the message priority |
Remote transmission request (RTR) |
1 |
Dominant (0) (see Remote Frame below) |
Identifier extension bit (IDE) |
1 |
Must be dominant (0)Optional |
Reserved bit (r0) |
1 |
Reserved bit (it must be set to dominant (0),
but accepted as either dominant or recessive) |
Data length code (DLC)* |
4 |
Number of bytes of data (0-8 bytes) |
Data field |
0-8 bytes |
Data to be transmitted (length dictated by
DLC field) |
CRC |
15 |
Cyclic Redundancy Check |
CRC delimiter |
1 |
Must be recessive (1) |
ACK slot |
1 |
Transmitter sends recessive (1) and any
receiver can assert a dominant (0) |
ACK delimiter |
1 |
Must be recessive (1) |
End-of-frame (EOF) |
7 |
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.
Query
Response
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:
- 16 bit ARM7TDMI-STM based Philips Semiconductors
LPC2119 board
- ELM327 OBD to RS232 Interpreter
-2x USB to RS232 adapter
Connections:
PC OBD
scanner CAN
transceiver CAN
Controller LPC2119 PC
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:
Legend canDriver.h canHardwareInterface.h canDriverInternal.h canDriverInternal.c canLpc21xx.c lpc21xx.h Internal implementation Implementation of CAN driver interface External interface canDriver.c User API Platform independent code LPC21xx
Specific code LPC21xxCanRegistersSafeAccess.c lpc21xxAccFilter.c
·
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.
·
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 we’ve
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.