CAN Part 2 - CAN Basics

In Part 1 we discussed the various types of CAN, how they differ from serial, and some hardware options.  In Part 2 we are going to talk about some more basics on what it means to use CAN and some of its features.

More Serial Comparisons (sorry)

So if you didn't get enough serial and CAN comparisons in the last part then sorry but you're going to hear it a few more times.  But instead of talking about the pros and cons of one or the other I want to talk about what it looks like when you send or receive data.  LabVIEW ships with several good examples on how to send and receive serial data and the one I always tell beginners to look at first is the Simple Serial example found by searching Simple Serial in the Help >> Find Example tool, if you have NI-VISA installed.

Simple Serial Example


Here we see the Simple Serial example and it is quite simple.  We configure our VISA resource and open it, then write a string of data, which is essentially an array of bytes.  We wait some amount of time, we read how many bytes are waiting in the buffer to be read, and read that data as a string which can be thought of as an array of bytes.  And then we close the port and have a basic error dialog.

The thing to realize with serial is we send an array of bytes, and we receive an array of bytes.  There is no defined protocol other than that.  Some manufacturers implement Modbus on serial to get some more structured control, but at the root of it you just are left with bytes coming in and bytes coming out with no way of knowing how many bytes to expect, or if there is a terminating character, or if there is data corruption along the way.  There is also no way of knowing if your data got to the destination, unless there is some kind of reply built into the device you are talking to.  CAN however has more structure to it in the form of the CAN Frame, and more robusness in the form of hardware features that help ensure the message sent gets to the receiver.

CAN Frame

Before we get into the LabVIEW example of CAN lets first understand the type of data we can receive and send.  With CAN in general the data you get and send usually isn't raw array of bytes.  In almost all situations the smallest amount of data you will get from a CAN read function is a single CAN Frame.  This frame contains multiple components of various data types.  At the low level a frame does get transmitted as a series of bits on the bus, which can be viewed with a scope.  But when we are discussing what is sent and received in CAN, the lowest level we will discuss is a frame which is between 41 and 113 bits of data.

All of the data that makes up a frame can be viewed differently through various APIs but the easiest to visualize is the XNet CAN frame, as seen below.

ID / Identifier / Arbitration ID (Scalar U32)

The first component to a Frame that we will discuss is the ID or Arbitration ID.  This is a scalar decimal number, represented as a U32.  The ID can either be 11 bits for a standard frame, or 29 bits for an extended frame.  A single node on the bus can transmit and receive both extended and standard frames at any time.  Furthermore IDs that fit in the first 11 bit range can be extended or standard.  So you can have an ID of 0x01 that is standard, and an ID that is 0x01 that is extended, where the data can be treated differently for each type even though the IDs are the same.

The Purpose of this ID is to categorize the data coming in.  We might want to designate the frame with ID 0x10 as a frame containing status information, and the frame 0x20 containing temperature information.  Or we may choose an ID of 0x30 as the frame we expect to see a request on, and we may choose to use an ID of 0x40 as the frame we send responses on.  It is also quite common for IDs to be assigned to specific devices on the bus.  So if we read a frame with ID 0x50 it might mean that a particular piece of hardware sent it.  An ID is often associated with a piece of hardware and seeing that ID means that piece of hardware is power and transmitting data.  One analogy I've made is with a ringing phone, and in that example it is clear that an ID shouldn't be reused for multiple purposes but there isn't a reason this any restriction keeping this from being done.

One other purpose for the ID is priority.  The lower the CAN ID the higher the priority.  This means if two devices are on the bus, one transmitting on ID 0x10, and another device is trying to send a frame with ID 0x20 then the 0x10 frame will go out, and the 0x20 frame will have to retry later hopefully when either the bus is idle, or a frame with a smaller ID (lower priority) is being sent.  This is often not a concern, since each device on the bus should attempt to minimize the amount of data it sends.  If the bus load reaches 90% or more, dropped frames, and retries might cause frames to not be received by all the nodes on the bus.  More details on priority and re-transmission will be discussed at the end of this post.  But for now lets continue with the Frame contents.

Extended (Boolean)

The second component in the frame we've already talked about.  It is a boolean which indicates if the frame is from an extended ID, or a standard one.  Because the ID is a U32, and only 29 bits at most are used, NI-CAN has chosen to bit mask the ID and an ID with the bit 30th bit true indicates an extended frame.  Other APIs choose to bundle the frame into a cluster, and some have it as a discreet input to a subVI.  This designation of if a frame is extended or standard exists for frames read, and frames to be written.

Payload (Array of U8 Bytes)

Another component of a frame is the actual data, or payload being sent or received.  For all versions of CAN except CAN-FD, the payload can be anywhere between 0 and 8 bytes by whole byte increments.  CAN-FD is a special and can have payloads up to 64 bytes, but not always in 1 byte increments.  Within these 8 bytes we can pack all kinds of information.

Lets say we have 64 relays in our system and we want to send the state of the relay (on or off) out over CAN so other nodes can read it.  We could do this by having 64 different frames, each with its own unique ID.  Or we could choose to have a single ID with 64 bits of information packed into the 8 bytes of payload.

All kinds of other data can be packed into these 8 bytes.  Like maybe we want to report a temperature which ranges from 0 to 10.  If we increments of 1 degree, the minimum number of bits needed would be 4 (2^4 is greater or equal to 10).  But maybe we want to increment by 0.1 and we'll need 7 bits (2^7 is greater or equal to 100).  Or range from -10 to 10 with increments of 0.01 and we'll need 11 bits (2^11 is greater or equal to 2,000).  We'll get into the details about these scaling techniques in a later post.  But for now just know a frame contains data, and it is between 0 and 8 bytes which can contain between 0 and 64 individual pieces of information.

DLC (Scalar U8)

DLC is a number that represents the number of bytes in the payload.  Because arrays in LabVIEW are unbounded, most LabVIEW APIs don't explicitly have a DLC because this information can be found by taking the array size of the payload.

Time (Time Stamp)

Not all APIs provide this, but the good ones do.  This is a scalar Time Stamp, or a scalar double with some kind of defined epoch, to indicate when the frame was received.  This information is ignored on a CAN write since the data generally just goes out as soon as possible.  But even with periodic writing functions the timing is defined by the database (more on that in a later post).

Because many applications aren't running on a deterministic operating system, a common technique is to wait some amount of time, then call the read function, and have it return all the frames that are in the hardware buffer waiting to be read since the last read.  This technique is common on other protocols like TCP and Serial.  The problem with this is if we wait 100ms and then perform a read and get 10 frames back, we don't know for sure how much time is between the first frame and the 10th frame.  All we know for sure is it is less than 100ms.  With the time component in the frame we can know at what time each frame was read by the hardware.  Not necessarily what time the frame was transferred from the hardware buffer.

Frame Type (Enum / Ring)

Not all APIs provide this and it isn't that common of a feature.  But a frame can be of a few different types.  We've so far been talking about the Data Frame, where data is sent out autonomously.  But if one node requested some data, the node replying could choose to send the data as a Remote Frame indicating that the data is intended for the node that requested it.  All nodes on the bus still read this frame like normal, but a node can choose to ignore it, if it didn't recently issue a request for data.

A frame can also be of the Error type, and Overload type.  These are also not very common but have their uses like when a frame is read by a node but some level of corruption was detected.

Echo (Boolean)

When a frame is sent by your hardware, sometimes it can be read back.  This is a great tool for knowing if and when a frame you intended to write, actually made it onto the bus and was read by the other devices on the bus.  An echo is generally a function of the CAN transceiver but not all APIs support it.

Basic Read / Write

We will go into this example more in the next post, but for now this is intended to show the basic way frames are read and written, as compared to the serial example from earlier.



Similar to the serial we have an open function at the start and a close function at the end.  We also have a button to perform a write, and periodically we query the hardware, and return all the frames that it read and display it as a table.  If you are using the NI-CAN API this is a great starting point, and can help identify wiring issues, before you try to use the CAN bus in a large application.

Automatic Re-transmit
CAN has several useful feature built in to the hardware which makes the bus very robust.  The first thing we'll discuss is that the hardware has an automatically re-transmitting feature, for data that didn't reach any other node on the bus.  This re-transmit functionality is built into the hardware and no special software is needed to take advantage of this feature.  If a device writes a message on the bus, and no device sends an acknowledgement, the device will continue to send that frame over and over until it is read, and then acknowledged by a node on the bus.  The only exception to this is if the hardware is told to send the frame once, and not perform the automatic re-transmit.  This is sometimes known as Single-Shot transmission and most APIs support this.  This single-shot mode should be used with caution because as we will learn, lost frames can be a normal occurrence on a CAN bus.  And re-transmitting when the frame is lost is a good thing.

Devices can be configured with a Listen-Only mode which will receive all CAN messages like normal, but will not acknowledge the messages it reads.  The behavior of the sender will then be the same as if there were no devices on the bus, and it will attempt to re-transmit it over and over again.

There are two primary sources for a frame to be lost and not reach its intended device, priority, and poor wiring of the bus.  In Part 1 I mentioned the standard way to wire a high speed dual wire bus was with two 120 ohm resistors, and with each node being a short distance from the bus.  For long runs twisted shielded pairs of wires may also be necessary to reduce electrical noise.

Priority

As mentioned earlier in this post, the ID of the frame defines the priority of the message.  And since CAN is full duplex with support for potentially hundreds of nodes, it is possible that two devices on the bus are trying to write at the same time.  What will happen in this case is the higher priority message will be sent out (the one with the lower ID value), and a device on the bus will acknowledge it, but the lower priority message won't be read or acknowledge.  As a result the device will wait for the bus to be idle and try to send the message again.  Of course it isn't guaranteed that the second time we write the frame on the bus, that it won't again be lost due to a higher priority frame.  In situations where the bus load is very high, a frame may never go out.  In practice this doesn't happen often.  The intent is to have few high priority messages, since it can preempt other bus traffic.

CRC

When a frame is written to the bus, a 15 bit CRC will also be sent.  This is automatically generated by the transceiver and generally there is no control over it.  When another device on the bus reads the frame it will determine if the message was valid by looking at the CRC that came with the data.  If it fails the CRC check, the device will not send the acknowledgement for receiving the frame.  This partial frame will not be available to software querying the hardware and is usually filtered by the transceiver.  After not receiving the acknowledgement, the original device will re-transmit the frame, hoping it will reach its intended devices.

Part 3...

With the understanding of the various hardware available, and the concept of a CAN frame we can finally start to talk about specific APIs and how to use them.  In Part 3 we will finally look at some NI examples of reading and writing CAN data.