Skip to content

Latest commit

 

History

History
256 lines (192 loc) · 10.2 KB

sendandreceive.rst

File metadata and controls

256 lines (192 loc) · 10.2 KB

Send and Receive

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.

When you have a ~canlib.canlib.Channel object, use ~canlib.canlib.Channel.busOn to go on bus and ~canlib.canlib.Channel.busOff to go off bus.

If you have multiple ~canlib.canlib.Channel objects to the same controller, the controller will go off bus when the last of the ~canlib.canlib.Channel objects go off bus (i.e. all ~canlib.canlib.Channel objects must be off bus for the controller to be off bus). You can use ~canlib.canlib.Channel.readStatus and watch the flag ~canlib.canlib.Stat.BUS_OFF to see if the controller has gone off bus.

You can set a channel to silent mode by using the ~canlib.Driver.SILENT mode if you want it to be on-bus without interfering with the traffic in any way, see can-driver-modes.

This example opens a channel, takes it on-bus, then takes it off-bus and closes it:

>>> from canlib import canlib
... with canlib.openChannel(channel=1) as ch:
...     ch.busOn()
...     ...
...     ch.busOff()

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 ~canlib.canlib.Channel.read, which will raise the exception ~canlib.canlib.CanNoMsg if there was no message available.

The ~canlib.Frame.flags attribute of the ~canlib.Frame returned by ~canlib.canlib.Channel.read contains a combination of the ~canlib.canlib.MessageFlag flags, including ~canlib.canlib.MessageFlag.FDF, ~canlib.canlib.MessageFlag.BRS, and ~canlib.canlib.MessageFlag.ESI if the CAN FD protocol is enabled, and error flags such as ~canlib.canlib.MessageFlag.OVERRUN which provides you with more information about the message; for example, a frame with a 29-bit identifier will have the ~canlib.canlib.MessageFlag.EXT bit set, and a remote frame will have the ~canlib.canlib.MessageFlag.RTR bit set. Note that the flag argument is a combination of the ~canlib.canlib.MessageFlag, so more than one flag might be set.

See can-frames for more information.

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?

  • If you want to read just a message with a specified identifier, and throw all others away, you can call ~canlib.canlib.Channel.readSpecificSkip. 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 ~canlib.canlib.Channel.read with a timeout.
  • 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 ~canlib.canlib.Channel.readSyncSpecific.

The following code fragment reads the next available CAN message, (using default bitrate 500 kbit/s):

>>> from canlib import canlib
... with canlib.openChannel(channel=0) as ch:
...     ch.busOn()
...     frame = ch.read(timeout=1000)
...     ch.busOff()
>>> frame
Frame(id=709, data=bytearray(b'\xb5R'), dlc=2, flags=<MessageFlag.STD: 2>, timestamp=3)

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 ~canlib.canlib.Channel.canAccept function.

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

If you want to remove an acceptance filter, call ~canlib.canlib.Channel.canAccept with the mask set to ~canlib.AcceptFilterFlag.NULL_MASK.

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

>>> from canlib import canlib
>>> ch = canlib.openChannel(channel=0)
>>> ch.canAccept(0x0f0, canlib.AcceptFilterFlag.SET_MASK_STD)
>>> ch.canAccept(0x060, canlib.AcceptFilterFlag.SET_CODE_STD)
>>> ...
>>> ch.close()

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

How acceptance filters can be used in a smaller project:

>>> from canlib import canlib
>>> ch = canlib.openChannel(channel=0)
>>> # The acceptance filter only have to be called once for each ch object
>>> ch.canAccept(0x0f0, canlib.AcceptFilterFlag.SET_MASK_STD)
>>> ch.canAccept(0x060, canlib.AcceptFilterFlag.SET_CODE_STD)
>>> ...
>>> # We can now run the rest of the program and the acceptance filter
>>> # will reject unwanted CAN messages.
>>> while(True):
>>>     frame = ch.read()
>>>     ...
>>> ...

Code and Mask Format

Explanation of the code and mask format used by ~canlib.canlib.Channel.canAccept() and `~canlib.canlib.objbuf.MessageFilter`:

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.

Sending Messages

You transmit messages by calling ~canlib.canlib.Channel.write. Outgoing CAN messages are buffered in a transmit queue and sent on a First-In First-Out basis. You can use ~canlib.canlib.Channel.writeSync to wait until the messages in the queue have been sent.

Sending a CAN message:

>>> from canlib import canlib, Frame
... with canlib.openChannel(channel=0) as ch:
...     ch.busOn()
...     frame = Frame(id_=234, data=[1,2])
...     ch.write(frame)
...     ch.busOff()

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 ~canlib.MessageFlag.EXT flag in the flag argument, the message will be transmitted with a 29-bit identifier. Conversely, received 29-bit-identifier messages have the ~canlib.MessageFlag.EXT flag set.

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:

>>> frame = Frame(id_=1234, data=[1,2,3,4,5,6,7,8], flags=canlib.MessageFlag.EXT)
>>> frame
Frame(id=1234, data=bytearray(b'\x01\x02\x03\x04\x05\x06\x07\x08'), dlc=8, flags=<MessageFlag.EXT: 4>, timestamp=None)
>>> ch.write(frame)

Object Buffers

Some of the Kvaser interfaces are equipped with hardware buffers for automatic sending and responding to messages. They can be used when the timing conditions are strict, and might not be possible to fulfill on the application level. The number of buffers are, depending on the device, typically limited to around 8 buffers.

There are two types of buffers, auto response and auto transmit.

  • Auto response sends a defined message immediately upon receiving a message meeting some condition.
  • Auto transmit sends a message periodically, with higher timing accuracy than can be achieved by an application working through driver and operating system.

The following example sets up an Auto response object buffer which responds with a CAN frame with CAN ID 200 when a CAN frame with CAN ID 100 is received.:

>>> from canlib import canlib, Frame
>>> ch = canlib.openChannel(0)
>>> msg_filter = canlib.objbuf.MessageFilter(code=100, mask=0xFFFF)
>>> frame = Frame(id_=200, data=[1, 2, 3, 4])
>>> response_buf = ch.allocate_response_objbuf(filter=msg_filter, frame=frame)
>>> response_buf.enable()

When creating the ~.canlib.objbuf.MessageFilter, you can use ~.canlib.objbuf.MessageFilter() to verify that the correct CAN ID will be filtered:

>>> msg_filter = canlib.objbuf.MessageFilter(code=100, mask=0xFFFF)
>>> msg_filter(100)
True
>>> msg_filter(110)
False

See also code_and_mask_format for an explanation of the code and mask format used by ~.canlib.objbuf.MessageFilter.

The following example sets up an Auto transmit buffer to periodically send a CAN frame with CAN ID 300 every second, for 5 seconds.:

>>> from canlib import canlib, Frame
>>> ch = canlib.openChannel(0)
>>> frame = Frame(id_=300, data=[1, 2, 3, 4])
>>> periodic_buffer = ch.allocate_periodic_objbuf(period_us=1_000_000, frame=frame)
>>> periodic_buffer.set_msg_count(5)
>>> periodic_buffer.enable()

For more advanced usecases, see t_programming.