PolarFire® SoC Applications - CAN

Last modified by Microchip on 2026/10/19 16:52

Introduction

CAN (Controller Area Network) is an industrial network bus standard developed by Bosch® in the 1980s to connect various Electronic Control Units (ECUs) in vehicles without the need for bulky and complex wiring.

This article focuses on using the PolarFire® SoC's MSS CAN peripherals. It guides you through the required setup, programming the reference design, creating a bare-metal SoftConsole™ project, adding a simple CAN transmit and receive example, and verifying the output on the serial console. The article also covers the Yocto Project® workflow and demonstrates CAN use in embedded Linux®.

Prerequisites

Hardware Setup

Software Setup

Additional Resources

Programming the Reference Design

To make the MSS CAN peripherals available to software at the application level, enable the CAN peripherals in the MSS configurator. In the reference design, the CAN0 and CAN1 peripherals are already enabled, and MSS CAN_0 and CAN_1 are routed to the MCP2562 Chip and available on the J25 and J27 headers with a standard CAN_H/CAN_L interface. Before writing software, it is important to understand where the CAN peripherals are connected on the target board.

The reference design also supports Yocto Linux, so the board-level CAN connection should be checked against the corresponding reference design documentation. For this article, the PolarFire Icicle Kit reference design is used.

can pins icicle kit

CAN 0 (CAN2 in Schematics)CAN 1 (CAN1 in Schematics)
CAN HighJ27 - 1CAN HighJ25 - 1
CAN LowJ27 - 2CAN LowJ25 - 2
GND PinJ27 - 3GND PinJ25 - 3
Information

Note: For more information about peripheral connections, refer to the board-specific reference design documentation:

Error

The MCP2562 chip is not installed on the PolarFire SoC Discovery Kit. So, to communicate via CAN, you have to additionally route/install/configure the MCP2562 chip on your side.

Downloading the Reference Design

Open the latest release page for the PolarFire SoC Icicle Kit reference design:

https://github.com/polarfire-soc/icicle-kit-reference-design/releases

Download the PolarFire SoC Icicle Kit reference design generation FlashPro images from the latest release.

Programming the FPGA Design

Open FlashPro Express.


Create a new project.


Select the reference design job file from the downloaded design package.

Use the job file matching the following pattern:

MPFS_ICICLE_KIT_ES_*\MPFS_ICICLE_KIT_ES_BASE_DESIGN_*\MPFS_ICICLE_KIT_ES_*.job

Click Run to program the Field-Programmable Gate Array (FPGA).

Success

At this point, the FPGA fabric has been programmed with the reference design.

Warning

You have two programming options:

  1. Use the prebuilt reference programming files for a quick start.
  2. Build or modify the Libero SoC project and regenerate the programming file. See the "PolarFire System-on-Chip (SoC) Applications - Microprocessor Subsystem (MSS) and Libero SoC Design Suite" page.

Back to Top

CAN in Linux® Environment

The CAN peripherals must be enabled and configured in Linux. That's why we need a Linux build system to configure it. For this article, we will use the Yocto Project.

Hart Software Services (HSS) Configurations

Hart Software Services (HSS) is the bootloader for PolarFire SoC. It runs first, sets up hardware, launches Linux or other apps, and is essential for multi-core and secure boot.

Objectives:

  • Download and import HSS to SoftConsole
  • Update references and build HSS
  • Deploy HSS to PolarFire Icicle Kit
Information

Note: This application article is verified on HSS 2025.07.

Download HSS.


Import the HSS project to SoftConsole by going to File > Import > Import Existing Project Into Workspace.

Import Projects

Warning

Ensure that you checked Copy projects into workspace.

Browse the HSS folder and import the project into the workspace by clicking Finish.


Copy the MSS XML file into the HSS project.

Copy xml file to hart-software-services/boards/mpfs-icicle-kit/soc_fpga_design/xml/<your xml>.xml.

Information

Note: If your board is a production board (not ES), use boards/mpfs-icicle-kit/... instead of boards/mpfs-icicle-kit-es/...


Copy and rename configurations for HSS.

Copy hart-software-services/boards/mpfs-icicle-kit-es/def_config to hart-software-services/.

Rename def_config to .config.

Edit .config file and update path to your xml by changing next line.

CONFIG_SOC_FPGA_DESIGN_XML="boards/mpfs-icicle-kit-es/soc_fpga_design/xml/<your xml>.xml"

Build HSS and deploy.

Right-click on the project name. 

Click on the build project. 

Select PolarFire SoC program non-secure boot mode 1 run option and deploy project to SoC. 

Warning

Make sure the configuration is set correctly before running it. If not, open External Tools > External Tools Configurations, then update the die and package settings to match your board parameters.

For the Icicle Kit ES:

  • Die: MPFS250T_ES
  • Package: FCVG484

Yocto Project Configurations

In this section, we will create a Linux image and program it into the PolarFire SoC Icicle Kit.

Objectives:

  1. Setting up the Yocto Project building environment.
  2. Enabling the CAN peripherals and including the necessary packages in the build.
  3. Building a Linux image and deploying it into the SoC.

Creating Environment

To create the Linux build environment refer to the "OpenEmbedded/Yocto Project BSP layer for Microchip's SoCs" page.

Configuring CAN Peripheral

Prepare the Microchip Linux kernel (linux-mchp) source tree for local development:

MACHINE=mpfs-icicle-kit devtool modify linux-mchp
Success

We should get the following log:

Recipe linux-mchp now set up to build from /build/workspace/sources/linux-mchp


Add the required libraries and applications to the image.

Open the conf/local.conf file and add the following variable at the end of the file:

CORE_IMAGE_EXTRA_INSTALL += "packagegroup-core-buildessential vim can-utils"

Information
  • packagegroup-core-buildessential - A Yocto Project meta-package that pulls in all the essential build tools needed for compiling software on your embedded system.

    • Includes:

      • gcc → C compiler

      • make → build automation

      • binutils → linker, assembler, etc.

      • pkgconfig → helps locate libraries and headers

      • libc-dev → standard C library headers

      • autoconf, automake, libtool → for building autotools-based projects

  • vim - A powerful text editor used in terminal environments
  • can-utils - package provides a set of small command-line programs for working with these interfaces. It is useful for checking that the CAN driver is loaded, configuring a test, sending and receiving frames, and monitoring bus activity before writing an application.

Save the file and exit.


Open the Linux kernel configuration menu and enable the CAN controller driver.

MACHINE=mpfs-icicle-kit bitbake linux-mchp -c menuconfig


In the configuration menu, navigate to Device Drivers.

​​Device Drives


Open the CAN Device Drivers menu:

​CAN Device Drivers Menu


Make sure that the Microchip PolarFire SoC CAN option is enabled:

​Microchip PolarFire SoC CAN


Save the config by selecting Save > Exit.


Locate the board-specific DTS files and verify that the CAN peripherals are configured.

yocto-dev/build/workspace/sources/linux-mchp/arch/riscv/boot/dts/microchip/mpfs-icicle-kit.dts
yocto-dev/build/workspace/sources/linux-mchp/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-common.dtsi

Make sure that CAN0 and CAN1 are enabled in the DTS (in mpfs-icicle-kit-common.dtsi file):

1
2
3
4
5
6
7
8
&can0 {
        status = "okay";
};

&can1 {
        status = "okay";
};

Compile a customized Yocto Project Linux kernel recipe in a developer-friendly way, producing kernel binaries for PolarFire SoC.

MACHINE=mpfs-icicle-kit devtool build linux-mchp
Warning

To create a bbappend layer, execute the following command:
> devtool finish linux-mchp custom-layer

​​​​Your bbappend layer will be saved at: custom-layer/recipes-kernel/linux/linux-mchp_%.bbappend

Building Linux Image and Deploying

Execute the following command to build the Linux image:

MACHINE=mpfs-icicle-kit bitbake mchp-base-image

Information

Note: Ensure that the initial Linux image has been built without error, so we can start making the modifications.

Here's the list of names of supporting machines:

MACHINEBoard NameDescription
MACHINE=mpfs-icicle-kitMPFS-ICICLE-KIT-ES, MPFS-ICICLE-KITPolarFire SoC Icicle Kit
MACHINE=mpfs-disco-kitMPFS-DISCO-KITPolarFire SoC Discovery Kit
MACHINE=mpfs-video-kitMPFS250-VIDEO-KITPolarFire SoC Video Kit

As the build is done, you can locate your Linux image at:

yocto-dev/build/tmp-glibc/deploy/images/<board_name>/<image-name>.rootfs-***.wic


Follow the "Programming a Linux Image" instructions to deploy the built image to the eMMC/SD card memory.

Information

For additional information, refer to the "OpenEmbedded/Yocto Project BSP layer for Microchip's SoCs" GitHub page.


After booting Linux on the PolarFire Icicle Board, log in as root and check if the MSS CANs devices appear:

Information

Note: Linux exposes CAN controllers through SocketCAN, which makes each controller available as a network interface, such as can0 or can1.

root@mpfs-icicle-kit:/media# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
    link/can
3: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
    link/can
4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 00:04:a3:59:d9:a8 brd ff:ff:ff:ff:ff:ff
5: end0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN mode DEFAULT group default qlen 1000
    link/ether 00:04:a3:59:d9:a7 brd ff:ff:ff:ff:ff:ff
6: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/sit 0.0.0.0 brd 0.0.0.0

If no CAN interfaces are listed, that means the CAN devices are not enabled and you have to double-check the configuration, DTS, and driver modifications needed to be done.

Back to Top

Software Tools and Utilities

CAN-utils Command-Line Tools

Some of the most useful tools included in can-utils are:

  • candump: Displays CAN frames received on one or more CAN interfaces
  • cansend: Sends a single CAN or CAN FD frame
  • cangen: Generates CAN traffic for testing and bus-load measurements
  • cansniffer: Displays CAN traffic and highlights data bytes that change

Using CAN-utils Tools on PolarFire Icicle Kit:

The following examples show how to use these tools from the Linux userspace.

Before running the commands, make sure CAN1 (can0 in Linux) and CAN2 (can1 in Linux) are looped back.

To create the loopback connection, connect CAN1H to CAN2H and CAN1L to CAN2L.


Before using the CAN controllers, configure the can0 and can1 interfaces using standard ip commands with a bitrate of 500 kbit/s.

Configure can0:

# ip link set can0 down
# ip link set can0 type can bitrate 500000 restart-ms 100
# ip link set can0 up

Configure can1:

# ip link set can1 down
# ip link set can1 type can bitrate 500000 restart-ms 100
# ip link set can1 up

 


To check the configuration and status of can0:

# ip -details link show can0
2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
   link/can  promiscuity 0 allmulti 0 minmtu 16 maxmtu 16
   can state ERROR-WARNING (berr-counter tx 126 rx 0) restart-ms 100
         bitrate 500000 sample-point 0.875
         tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1 brp 1
         mpfs_can: tseg1 2..16 tseg2 1..8 sjw 1..4 brp 0..32767 brp_inc 1
         clock 8000000 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 gso_ipv4_max_size 65536 gro_ipv4_max_size 65536 parentbus platform parentdev 2010c000.can
Information

Note: Make sure that both CAN interfaces are in the UP state and use the same bitrate.


Send CAN frames through can0:

# cansend can0 123#1122334455667788
Success

The transmitted frame contains:
CAN ID: 0x123
Data length: 8 bytes
Data: 11 22 33 44 55 66 77 88


Monitor CAN frames received on both interfaces:

# candump can0 can1

The command waits for CAN traffic and displays the interface name, CAN identifier, data length, and data bytes.

Success

candump output after sending the previous CAN frame:

root@mpfs-icicle-kit:~# candump can0 can1
  can0  123   [8]  11 22 33 44 55 66 77 88
  can1  123   [8]  11 22 33 44 55 66 77 88

Run candump in a separate terminal session to monitor the result


Generate continuous CAN traffic on can0:

# cangen can0

Monitor changing CAN data bytes with cansniffer:

# cansniffer can1
Success

cansniffer displays CAN frames and highlights data bytes that change between successive frames. It is useful when checking whether a sensor value, counter, or status field is changing on the bus.

Application Programming in C Language

We can write a C program in userspace to interact with the CAN controllers. The example below performs a CAN loopback test between two CAN controllers.

Boot Linux on your PolarFire Icicle kit. Navigate to /media and create main.c using the vim editor:

cd /media && vim main.c

Copy and paste the following C code to the main.c:

#include <linux/can.h>
#include
<linux/can/raw.h>
#include
<net/if.h>
#include
<poll.h>
#include
<stdio.h>
#include
<string.h>
#include
<sys/ioctl.h>
#include
<sys/socket.h>
#include
<unistd.h>

static int can_open(const char *name)
{
   int fd;
   struct ifreq ifr = {0};
   struct sockaddr_can addr = {0};

    fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
   if (fd < 0) {
        perror("socket");
       return -1;
    }

    strncpy(ifr.ifr_name, name, IFNAMSIZ - 1);

   if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
        perror("SIOCGIFINDEX");
        close(fd);
       return -1;
    }

    addr.can_family  = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;

   if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        close(fd);
       return -1;
    }

   return fd;
}

static int can_test(int tx_fd, int rx_fd,
                    canid_t id, const unsigned char *data, int length)
{
   struct can_frame tx = {0};
   struct can_frame rx;
   struct pollfd pfd = { .fd = rx_fd, .events = POLLIN };

    tx.can_id  = id;
    tx.can_dlc = length;
    memcpy(tx.data, data, length);

   if (write(tx_fd, &tx, sizeof(tx)) != sizeof(tx)) {
        perror("write");
       return -1;
    }

   if (poll(&pfd, 1, 2000) <= 0) {
        perror("CAN receive timeout");
       return -1;
    }

   if (read(rx_fd, &rx, sizeof(rx)) != sizeof(rx)) {
        perror("read");
       return -1;
    }

   if ((rx.can_id & CAN_SFF_MASK) != id ||
        rx.can_dlc != length ||
        memcmp(rx.data, data, length) != 0) {
        printf("Received frame does not match\n");
       return -1;
    }

    printf("CAN ID 0x%03X received correctly\n", id);
   return 0;
}

int main(void)
{
   int can0 = can_open("can0");
   int can1 = can_open("can1");

   const unsigned char data0[] = {
       0x11, 0x22, 0x33, 0x44
    };

   const unsigned char data1[] = {
       0xAA, 0xBB, 0xCC, 0xDD
    };

   if (can0 < 0 || can1 < 0)
       return 1;

    printf("Testing can0 -> can1\n");
   if (can_test(can0, can1, 0x123,
                 data0, sizeof(data0)) < 0)
       goto fail;

    printf("Testing can1 -> can0\n");
   if (can_test(can1, can0, 0x321,
                 data1, sizeof(data1)) < 0)
       goto fail;

    printf("CAN loopback test PASSED\n");

    close(can0);
    close(can1);
   return 0;

fail:
    printf("CAN loopback test FAILED\n");

    close(can0);
    close(can1);
   return 1;
}

After saving the modification, compile the C code on target:

gcc main.c -o main

Configure the CAN interfaces:

ip link set can0 down
ip link set can0 type can bitrate 500000 restart-ms 100
ip link set can0 up

ip link set can1 down
ip link set can1 type can bitrate 500000 restart-ms 100
ip link set can1 up

Run the main executable:

./main
Success

Running the application, you will get the following response:

root@mpfs-icicle-kit:/media# ./main
Testing can0 -> can1
CAN ID 0x123 received correctly
Testing can1 -> can0
CAN ID 0x321 received correctly
CAN loopback test PASSED

You can also check it with candump can0 can1 from different terminal session and check the results:
mpfs-icicle-kit:~# candump can0 can1
  can0  123   [4]  11 22 33 44
  can1  123   [4]  11 22 33 44
  can0  321   [4]  AA BB CC DD
  can1  321   [4]  AA BB CC DD

Back to Top

CAN in Bare Metal Applications

Building and Programming the SoftConsole Project

To use the MSS CAN0 and CAN1 in a bare-metal application, we have to use SoftConsole to develop, build, and deploy the application.

For this application, let us take one of the GitHub bare-metal reference examples and change it so it uses the MSS CANs and communicates with each other.

Download the mpfs-blank-baremetal bare-metal application project from the mpfs-blank-baremetal repository and import it into the SoftConsole.

Note: If this is your first time importing a project in SoftConsole, watch this video:


Replace the MSS Configuration XML file in the bare-metal project with the XML file used in your Libero SoC design suite project. The path to the file that SoftConsole will use to generate header files, which are then used by the MPFS HAL, is:

mpfs-blank-baremetal > "your_board" > fpga_design > design_description > xx.xml

Replace the application source contents with the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <stdio.h>
#include
<string.h>
#include
"mpfs_hal/mss_hal.h"
#include
"inc/common.h"
#include
"drivers/mss/mss_can/mss_can.h"

static uint64_t seconds_to_ticks(uint32_t seconds)
{
   return (uint64_t)seconds * LIBERO_SETTING_MSS_RTC_TOGGLE_CLK;
}

void u54_1(void)
{
    mss_can_msgobject    tx_msg = {0};
    mss_can_msgobject    rx_msg = {0};
    mss_can_filterobject rx_filter;
   uint8_t              counter = 0u;
   char                 line[80];

    (void)mss_config_clk_rst(MSS_PERIPH_CAN0,   (uint8_t)MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
    (void)mss_config_clk_rst(MSS_PERIPH_CAN1,   (uint8_t)MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
    (void)mss_config_clk_rst(MSS_PERIPH_MMUART2,(uint8_t)MPFS_HAL_FIRST_HART, PERIPHERAL_ON);


    MSS_UART_init(&g_mss_uart2_lo, MSS_UART_115200_BAUD,
                  MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY | MSS_UART_ONE_STOP_BIT);
    MSS_UART_polled_tx_string(&g_mss_uart2_lo,
                  (const uint8_t *)"\r\nCAN0 -> CAN1 transfer test\r\n");

    MSS_CAN_init(&g_mss_can_0_lo, CAN_SPEED_8M_1M, (pmss_can_config_reg)0, 16u, 7u);
    MSS_CAN_init(&g_mss_can_1_lo, CAN_SPEED_8M_1M, (pmss_can_config_reg)0, 16u, 7u);

    rx_filter.ACR.L       = 0x00000000u;
    rx_filter.AMR.L       = 0xFFFFFFFFu;
    rx_filter.AMCR_D.MASK = 0xFFFFu;
    rx_filter.AMCR_D.CODE = 0x0000u;
    MSS_CAN_config_buffer(&g_mss_can_1_lo, &rx_filter);

    MSS_CAN_set_mode(&g_mss_can_0_lo, CANOP_MODE_NORMAL);
    MSS_CAN_set_mode(&g_mss_can_1_lo, CANOP_MODE_NORMAL);
    MSS_CAN_start(&g_mss_can_0_lo);
    MSS_CAN_start(&g_mss_can_1_lo);

    tx_msg.ID  = 0x100u;
    tx_msg.IDE = 0u;                       /* standard 11-bit identifier       */
    tx_msg.ID  = MSS_CAN_set_id(&tx_msg);  /* left-align the ID for the hardware*/
    tx_msg.L  |= CAN_TX_WPNL_EBL;          /* REQUIRED so TXREQ latches         */
    tx_msg.DLC = 8u;

   while (1)
    {
       uint8_t  rx_byte = 0u;
       uint8_t  got_rx  = 0u;
       uint64_t t_start;

        tx_msg.DATALOW  = (uint32_t)counter * 0x01010101u;
        tx_msg.DATAHIGH = (uint32_t)counter * 0x01010101u;
        (void)MSS_CAN_send_message(&g_mss_can_0_lo, &tx_msg);

        t_start = readmtime();
       while ((readmtime() - t_start) < seconds_to_ticks(1u))
        {
           if (MSS_CAN_get_message_av(&g_mss_can_1_lo) == CAN_VALID_MSG)
            {
                MSS_CAN_get_message(&g_mss_can_1_lo, &rx_msg);
                rx_byte = (uint8_t)rx_msg.DATALOW;
                got_rx  = 1u;
            }
        }

       if (got_rx)
        {
            sprintf(line, "transmitted %02X received %02X\r\n",
                    (unsigned)counter, (unsigned)rx_byte);
        }
       else
        {
           uint32_t estat = 0u;
            (void)MSS_CAN_get_error_status(&g_mss_can_0_lo, &estat);
            sprintf(line,
                   "transmitted %02X received -- (tx_err=%u rx_err=%u estat=0x%lx)\r\n",
                    (unsigned)counter,
                    (unsigned)MSS_CAN_get_tx_error_count(&g_mss_can_0_lo),
                    (unsigned)MSS_CAN_get_rx_error_count(&g_mss_can_1_lo),
                    (unsigned long)estat);
        }
        MSS_UART_polled_tx_string(&g_mss_uart2_lo, (const uint8_t *)line);

        counter++;
    }
}

Line 1–25: Header comment (what the test does + wiring)

Line 27–31: Includes

Line 33–37: seconds_to_ticks() timing helper

Line 39–45: u54_1() entry + local variables

Line 47–50: Enable clocks for CAN0, CAN1, MMUART2

Line 52–56: Init MMUART2 and print banner

Line 58–61: Init both CAN controllers at 1 Mbit/s

Line 63–68: CAN1 filter accepts all messages

Line 70–74: Set both controllers to normal mode and start

Line 76–89: Build TX frame (ID, WPNL key, DLC 8)

Line 91: Main loop

Line 97–100: Load counter into data bytes, transmit on CAN0

Line 102–112: Wait ~1 s, poll and read CAN1

Line 114–132: Format result or error counters

Line 133–135: Print on MMUART2, increment counter, repeat


Build the project and deploy it either in LIM for Debug mode or eNVM for Release mode.

Note: You can refer to the video for building and debugging bare-metal applications in SoftConsole.

Back to Top

Checking for the Results

Verify that the CAN output works correctly by opening the serial connection and observing the transmitted message.

Open MobaXTerm and set up the serial connections for the available FlashPro UART ports.


Monitor the UART terminal and observe the transaction status approximately every second.


Confirm that the expected output is displayed in the terminal.

Expected Output

Success

If the setup is correct, the UART console displays the​​​ transmitted XX received XX message repeatedly at 1s intervals.

Back to Top

Summary

This article introduced the MSS CAN peripheral on PolarFire SoC devices and showed how to use it in a bare-metal application. You reviewed the required hardware and software, programmed the PolarFire Icicle Kit reference design, created a SoftConsole project, enabled CAN support, added a simple CAN transmit application, and verified the output through a serial terminal.

The article also covered the Yocto Linux flow, configured the CAN interfaces using SocketCAN and can-utils, and demonstrated a user-space C loopback application.

Back to Top