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 or Windows
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