Working with CAN
Previous: Adding commands to the console
The POSIX and S32K148 builds support CAN but in different ways.
The S32K148 development board has CAN hardware and this is used to send and receive CAN frames
whereas on the POSIX platform a CAN socket is used to send CAN frames to a Virtual CAN interface
(note this requires that the POSIX platform used includes SocketCan
support).
POSIX build
Assuming you have successfully built and run the executable app.referenceApp.elf
on a POSIX platform
as instructed in Set up build environment for POSIX platform on Ubuntu 22.04
then you may see one of the following errors during startup of app.referenceApp.elf
…
1617576: RefApp: CAN: ERROR: [SocketCanTransceiver] Failed to create socket (node=vcan0, error=-1)
or
1617576: RefApp: CAN: ERROR: [SocketCanTransceiver] Failed to ioctl socket (node=vcan0, error=-1)
which indicate that a socket could not be opened using the Virtual CAN interface vcan0
-
you need to set it up. On Ubuntu 22.04, the following commands will set it up…
sudo ip link add dev vcan0 type vcan
sudo ip link set vcan0 mtu 16
sudo ip link set up vcan0
Note that the above commands are also in the script tools/can/bring-up-vcan0.sh
.
If you see an error Error: Unknown device type.
from the above commands then you need to figure out how to set up SocketCan
support
and support for device type vcan
on your platform.
Note
Please note while the standard Ubuntu 22.04 release includes SocketCan
support,
the version of Ubuntu 22.04 available for WSL does not
- to perform the steps in this exercise on WSL, a custom kernel would need to be created with SocketCan
support.
See Add SocketCAN support in WSL.
Once vcan0
is set up the above error messages should no longer be seen during the application’s initialization.
In DemoSystem.cpp
, the software sends a CAN frame every second with Frame ID = 0x558
and with 4 bytes of data containing a counter.
If initialization completes successfully,
you should see a log message every second indicating a CAN frame was sent…
2477109: RefApp: CAN: INFO: [CanDemoListener] CAN frame sent, id=0x558, length=4
You can check that this is sent to vcan0
using the utility candump
which can be installed on Ubuntu 22.04 as follows…
sudo apt install can-utils
While app.referenceApp.elf
is running, in another shell terminal run…
candump vcan0
and you should see the CAN frames being received every second and the counter values incrementing, like this…
vcan0 558 [4] 00 00 00 00
vcan0 558 [4] 00 00 00 01
vcan0 558 [4] 00 00 00 02
vcan0 558 [4] 00 00 00 03
vcan0 558 [4] 00 00 00 04
Using cansend
(also installed with can-utils
) you can send a CAN frame to vcan0
.
With app.referenceApp.elf
running in one shell terminal and candump vcan0
running in another shell terminal,
open a third shell terminal and send a CAN frame as follows…
cansend vcan0 321#AABBCCDDEEFF
The above command sends a CAN Frame with ID = 0x321
and 6 bytes of data 0xAABBCCDDEEFF
to vcan0
.
You should see it in the output from candump vcan0
like this…
vcan0 321 [6] AA BB CC DD EE FF
and you should see it being received by the application in its log like this…
14658083: RefApp: CAN: DEBUG: [SocketCanTransceiver] received CAN frame, id=0x321, length=6
Next, take a look at the code in referenceApp/executables/referenceApp/application/src/app/CanDemoListener.cpp
.
You should see this code in CanDemoListener::init()
…
_canFilter.add(0x123);
_canFilter.add(0x124);
This sets up CanDemoListener
as a listener for CAN frames with these IDs.
If these are received then CanDemoListener::frameReceived
will be called, which contains…
void CanDemoListener::frameReceived(::can::CANFrame const& frame)
{
Logger::info(
CAN,
"[CanDemoListener] received CAN frame, id=0x%X, length=%d",
(int)frame.getId(),
(int)frame.getPayloadLength());
if (_canTransceiver != nullptr)
{
::can::CANFrame newFrame(frame.getId() + 1, frame.getPayload(), frame.getPayloadLength());
auto result = _canTransceiver->write(newFrame);
Logger::info(CAN, "[CanDemoListener] response queued, result = %d", (int)result);
}
}
This code means that if a CAN Frame with ID 0x123
or 0x124
is received,
then a new frame is created with the same data and its ID incremented by 1
and is transmitted by the CAN transceiver.
Let’s test that.
With app.referenceApp.elf
running in one shell terminal, candump vcan0
running in another shell terminal,
in a third shell terminal send a CAN frame as follows…
cansend vcan0 124#88776655
In the application’s log output you should see…
16040039: RefApp: CAN: DEBUG: [SocketCanTransceiver] received CAN frame, id=0x124, length=4
16040039: RefApp: CAN: INFO: [CanDemoListener] received CAN frame, id=0x124, length=4
16040039: RefApp: CAN: INFO: [CanDemoListener] response queued, result = 0
16040041: RefApp: CAN: INFO: [CanDemoListener] CAN frame sent, id=0x125, length=4
and in the output from candump vcan0
you should see…
vcan0 124 [4] 88 77 66 55
vcan0 125 [4] 88 77 66 55
which shows the frame with ID 0x124
sent by cansend
and the response frame with ID 0x125
sent by the application.
S32K148 build
To prove is CAN working in a S32K148 build you will need CAN hardware that connects to the CAN interface on the S32K148 development board’s J11 connector. Any device that supports Classic CAN (CAN-FD is not needed) should work if its CAN parameters are set up as follows…
Bitrate |
Sample point |
---|---|
500kbits/s |
88% |
The same DemoSystem
and CanDemoListener
classes are used in the S32K148 and POSIX builds
(note different CanSystem
are used)
so you should be able to see the same CAN behaviour on the S32K148
as described above for the POSIX build.
Find the DemoSystem
, CanDemoListener
and CanSystem
classes and examine how these work.
In particular note that for the POSIX build, CanSystem
is implemented using ::can::SocketCanTransceiver
whereas for the S32K148 build, CanSystem
is implemented using bios::CanFlex2Transceiver
both of which are alternative implementations of ::can::AbstractCANTransceiver
.
Next: Using UDS