Send and Receive CAN Messages

Bus On / Bus Off

When the CAN controller is on bus, it is receiving messages and is sending acknowledge bits in response to all correctly received messages. A controller that is off bus is not taking part in the bus communication at all.

Use canBusOn() to go on bus and canBusOff() to go off bus.

If you have multiple handles to the same controller, the controller will go off bus when the last of the handles go off bus (i.e. all handles must be off bus for the controller to be off bus). You can use canReadStatus() and watch the flag canSTAT_BUS_OFF to see if the controller has gone off bus.

If you would like to reset a CAN bus controller by taking the channel off bus and then on bus again, you can use canResetBus().

You can set a channel to silent mode by using the canDRIVER_SILENT mode if you want it to be on-bus without interfering with the traffic in any way, see CAN Driver Modes

Example. This example takes an open channel off-bus and then takes it on-bus again, for what purpose it may serve.

canStatus stat;
.
.
.
stat = canBusOff(hnd);
if (stat < 0) {
printf("canBusOff failed, status=%d\n", stat);
exit(1);
}
stat = canBusOn(hnd);
if (stat < 0) {
printf("canBusOn failed, status=%d\n", stat);
exit(1);
}

Reading Messages

Incoming messages are placed in a queue in the driver. In most cases the hardware does message buffering as well. You can read the first message in the queue by calling canRead(); it will return canERR_NOMSG if there was no message available.

The flag parameter of canRead() contains a combination of the message flags canMSG_xxx, including canFDMSG_xxx if the CAN FD protocol is enabled, and error flags canMSGERR_xxx which provides you with more information about the message; for example, a frame with a 29-bit identifier will have the canMSG_EXT bit set, and a remote frame will have the canMSG_RTR bit set. Note that the flag argument is a combination of the canMSG_xxx flags, so more than one bit might be set.

The size of the queues in the driver and hardware is descibed in Message Queue and Buffer Sizes.

Sometimes it is desirable to have a peek into the more remote parts of the queue. Is there, for example, any message waiting that has a certain identifier? You can call canReadSpecific to read that message. Messages not matching the specified identifier will be kept in the queue and will be returned on the next call to canRead.

If you want to read just a message with a specified identifier, and throw all others away, you can call canReadSpecificSkip(). This routine will return the first message with the specified identifier, discarding any other message in front of the desired one.

If you want to wait until a message arrives (or a timeout occurs) and then read it, call canReadWait().

If you want just to wait for an arbitrary message to arrive, but you don't want to read the message, call canReadSync().

If you want to wait until there is at least one message in the queue with a certain identifier, but you don't want to read it, call canReadSyncSpecific().

You can specify a timeout (in milliseconds) for those routines that wait for messages.

Example. Input Queue Handling

As an illustration of the abovementioned routines, consider the case where CAN messages with the following identifiers have arrived to the input queue:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

  1. canRead(hnd, &id, ...) returns canOK and id == 1.
  2. canRead(hnd, &id, ...) returns canOK and id == 2.
  3. canReadSpecific(hnd, 7, ...) returns canOK and the message with id 7.
  4. canReadSpecific(hnd, 7, ...) returns canERR_NOMSG.
  5. canRead(hnd, &id, ...) returns canOK and id == 3.
  6. canReadSpecificSkip(hnd, 5, ...) returns canOK and the message with id 5.
  7. canRead(hnd, &id, ...) returns canOK and id == 6.
  8. canReadSyncSpecific(hnd, 7, ..., 500) will wait for the next message with id=7 (or 500 ms have elapsed) and return canOK or canERR_TIMEOUT.
  9. canRead(hnd, &id, ...) returns canOK and id == 8.
  10. canReadSpecific(hnd, 7, ...) returns canOK and the message with id = 7, if the previous call to canReadSyncSpecific() returned canOK.

Example. The following code fragment reads the next available CAN message, if any.

long id;
unsigned char data[8];
unsigned int dlc, flags;
unsigned long timestamp;
stat = canRead(hnd, &id, data, &dlc, &flags, &timestamp);
if (stat != canERR_NOMSG) {
printf("Failed, status == %d\n", stat);
}

Acceptance Filters

You can set filters to reduce the number of received messages. CANlib supports setting of the hardware filters on the CAN interface board. This is done with the canAccept() function.

You set an acceptance code and an acceptance mask which together determine which CAN identifiers are accepted or rejected.

Example. To set the mask to 0xF0 and the code to 0x60:

stat = canAccept(hnd1, 0x0F0L, canFILTER_SET_MASK_STD);
if (stat < 0) printf("canAccept failed, stat = %d\n", stat);
stat = canAccept(hnd1, 0x060L, canFILTER_SET_CODE_STD);
if (stat < 0) printf("canAccept failed, stat = %d\n", stat);

or, alternatively,

stat = canSetAcceptanceFilter(hnd1, 0x060, 0x0F0, FALSE);
if (stat < 0) printf("canSetAcceptanceFilter failed, stat = %d\n", stat);

This code snippet will cause all messages having a standard (11-bit) identifier with bit7-bit4 in the identifier equal to 0110 (binary) will pass through. Other messages with standard identifiers will be rejected.

Example. How acceptance filters can be used in a smaller project.

long id;
unsigned char data[8];
unsigned int dlc, flags;
unsigned long timestamp;
// The acceptance filter only have to be called once for each handle.
stat = canSetAcceptanceFilter(hnd, 0x060, 0x0F0, FALSE);
if (stat < 0) printf("canSetAcceptanceFilter failed, stat = %d\n", stat);
// We can now run the rest of the program and the acceptance filter will reject unwanted CAN messages.
while (true){
stat = canRead(hnd, &id, data, &dlc, &flags, &timestamp);
if (stat != canERR_NOMSG) {
printf("Failed, status == %d\n", stat);
}
}

Code and Mask Format

Explanation of the code and mask format used by the canAccept(), canSetAcceptanceFilter(), and canObjBufSetFilter() functions:

  • A binary 1 in a mask means "the corresponding bit in the code is relevant"
  • A binary 0 in a mask means "the corresponding bit in the code is not relevant"
  • A relevant binary 1 in a code means "the corresponding bit in the identifier must be 1"
  • A relevant binary 0 in a code means "the corresponding bit in the identifier must be 0"

In other words, the message is accepted if ((code XOR id) AND mask) == 0.

Note
You can set the extended code and mask only on CAN boards that support extended identifiers.
Not all CAN boards support different masks for standard and extended CAN identifiers.
If you want to remove a filter, call canAccept() with the mask set to 0.
On some boards the acceptance filtering is done by the CAN hardware; on other boards (typically those with an embedded CPU,) the acceptance filtering is done by software. canAccept() behaves in the same way for all boards, however.

canSetAcceptanceFilter() and canAccept() both serve the same purpose but the former can set the code and mask in just one call.

Sending Messages

You transmit messages by calling canWrite(). Outgoing CAN messages are buffered in a transmit queue and sent on a First-In First-Out basis. You can use canWriteSync() to wait until the messages in the queue have been sent.

Example. Sending a CAN message.

char msg[8];
...
stat = canWrite(hnd, 234, msg, 8, 0);

Using Extended CAN (CAN 2.0B)

"Standard" CAN has 11-bit identifiers in the range 0 - 2047. "Extended" CAN, also called CAN 2.0B, has 29-bit identifiers. You specify which kind of identifiers you want to use in your call to canWrite(): if you set the canMSG_EXT flag in the flag argument, the message will be transmitted with a 29-bit identifier. Conversely, received 29-bit-identifier messages have the canMSG_EXT flag set.

Example. The following code fragment sends a CAN message on an already open channel. The CAN message will have identifier 1234 (extended) and DLC = 8. The contents of the data bytes will be whatever the data array happens to contain.

canStatus stat;
unsigned char data[8];
:
:
stat = canWrite(hnd, 1234, data, 8, canMSG_EXT);
if (stat < 0) {
printf("Failed, status == %d\n", stat);
}

There are more (but not all) of the message flags canMSG_xxx, including canFDMSG_xxx if the CAN FD protocol is enabled, that are meaningful to set when sending messages.

Object Buffers

CANlib supports object buffers for special purposes. You don't need these object buffers when transmitting and receiving regular messages. Instead, the object buffers allows you to

  • define auto response messages; that is, when a message meeting a certain condition is received, CANlib will respond automatically with a message you define,
  • define auto transmit messages; that is, messages that are transmitted periodically.

The object buffers are implemented by the firmware and hardware of certain devices, e.g. the Kvaser Leaf Professional. The number of buffers in a device is limited. Typically around 10 buffers are available.

Using the object buffers
To use the object buffers, you start with allocating a buffer with a call to canObjBufAllocate(). For an auto response buffer, you define which messages to respond to by calling canObjBufSetFilter(), and the contents of the response is set with canObjBufWrite().

To enable a buffer, call canObjBufEnable(), and call canObjBufDisable() to disable it.

For an auto transmit buffer, call canObjBufSetPeriod() to set the frequency of the message, call canObjBufSetMsgCount() to set the number of messages to send from the buffer, and call canObjBufWrite() to define the contents of the message.

If you want to send a burst of messages at the highest possible pace, use canObjBufSendBurst().

You deallocate a buffer that you don't want to use any longer by a call to canObjBufFree(). You can deallocate all buffers in one call with canObjBufFreeAll().

See also
canObjBufSetFlags()