Skip to main content

CAN

1. Introduction

CAN, short for Controller Area Network, is a serial communication protocol widely used for real-time data communication. Initially developed by the German company Bosch for communication between electronic control units (ECUs) in automobiles, CAN protocol's efficiency and reliability have led to its extensive use in other fields, such as industrial automation, medical devices, marine systems, and aerospace.

2. Enabling CAN

The RM_IO offers a high degree of flexibility, allowing any RMIO pin to be designated as a CAN interface. Given that different customers have significantly varied requirements for using the CAN interface, the pre - enabled settings we provide may not meet the actual needs of all customers. Therefore, the CAN interface function is not enabled by default in the factory image. It is recommended that customers download the SDK on their own, add the device tree provided at the end of this section to the device tree, and then compile the image to enable the CAN interface according to their needs. This example uses the USB-CAN-A module from Waveshare for testing.

3. Common Commands

  1. Communicate with the USB-CAN-FD module, setting arbitration and data bitrates to 1 Mbps.

    sudo su 
    ip link set can0 down
    ip link set can0 type can bitrate 1000000 dbitrate 1000000 fd on
    ip link set can0 up
  2. CAN FD transmission:

    Sending (standard frame, data frame, ID: 123, data: DEADBEEF): cansend can0 123##1DEADBEEF
    Sending (extended frame, data frame, ID: 00000123, data: DEADBEEF): cansend can0 00000123##1DEADBEEF
  3. CAN 2.0 transmission:

    Sending (standard frame, data frame, ID: 123, data: DEADBEEF): cansend can0 123#DEADBEEF
    Sending (standard frame, remote frame, ID: 123): cansend can0 123#R
    Sending (extended frame, data frame, ID: 00000123, data: DEADBEEF): cansend can0 00000123#12345678
    Sending (extended frame, remote frame, ID: 00000123): cansend can0 00000123#R
  4. Receiving CAN 2.0 and CAN FD frames:

    candump can0

4. CAN Testing (Python Program)

If you want to test the CAN bus using Python, the python-can library is a highly suitable choice. It provides a high-level abstraction for CAN communication, supporting various backend interfaces like SocketCAN, PCAN, and Kvaser. With python-can, developers can focus on data transmission and reception without delving into low-level complexities.

  1. Verify library installation:

    root@luckfox:~# python3
    Python 3.11.2 (main, Aug 26 2024, 07:20:54) [GCC 12.2.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import can
    >>> exit()
  2. Example transmission script (Avoid naming your script can.py to prevent conflicts with the python-can library):

    #!/usr/bin/python3
    import can

    def send_can_message():
    # Connect to can0 using SocketCAN
    bus = can.interface.Bus(channel='can0', bustype='socketcan')

    # Create a CAN message
    msg = can.Message(arbitration_id=0x123, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=False)

    # Send the CAN message
    try:
    bus.send(msg)
    print("Message sent on can0")
    except can.CanError:
    print("Message NOT sent")

    if __name__ == "__main__":
    send_can_message()
  3. Run the script and open another terminal to monitor data:

    chmod +x can_test.py
    ./can_test.py

    root@luckfox:/home/luckfox# candump can0
    can0 123 [8] 01 02 03 04 05 06 07 08

5. CAN Testing (C Program)

SocketCAN is a native Linux interface for CAN bus communication. It uses a standard network API, providing a flexible and simplified approach to using CAN, similar to typical network protocols like TCP/IP

  • Key Features:
    • Unified Interface: Treats CAN devices as network interfaces (e.g., can0, can1) accessible via standard networking APIs.
    • Kernel Integration: Part of the Linux kernel, allowing direct system calls for accessing CAN interfaces using socket functions.
    • Protocol Support: Compatible with standard CAN (CAN 2.0) and CAN FD (Flexible Data-rate).
    • Device Abstraction: Abstracts CAN controllers as network devices, enabling management with standard tools like ip.

5.1 Programming with SocketCAN

  1. Create a socket:

    int sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
  2. Bind the socket to a CAN interface:

    struct ifreq ifr;
    strcpy(ifr.ifr_name, "can0");
    ioctl(sockfd, SIOCGIFINDEX, &ifr);

    struct sockaddr_can addr;
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
  3. Send and receive CAN frames:

    write(sockfd, &frame, sizeof(struct can_frame));
    read(sockfd, &frame, sizeof(struct can_frame));
  4. Complete example for CAN transmission:

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

    int main() {
    int sockfd;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;

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

    strcpy(ifr.ifr_name, "can0");
    ioctl(sockfd, SIOCGIFINDEX, &ifr);

    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));

    frame.can_id = 0x123;
    frame.can_dlc = 8;
    for (int i = 0; i < frame.can_dlc; i++) {
    frame.data[i] = i + 1;
    }

    for (int i = 0; i < 3; i++) {
    if (write(sockfd, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {
    perror("Write error");
    return -3;
    }
    }

    printf("CAN message sent on can0\n");
    close(sockfd);
    return 0;
    }
  5. Cross-compile the program. For setup, refer to sections Program Compilation or GPIO.

    arm-none-linux-gnueabihf-gcc  can.c -o can
  6. Open another terminal to monitor received data:

    root@luckfox:/home/luckfox# candump can0
    can0 123 [8] 01 02 03 04 05 06 07 08
    can0 123 [8] 01 02 03 04 05 06 07 08
    can0 123 [8] 01 02 03 04 05 06 07 08

6. Device Tree Overview

  1. The device tree file is located at kernel-6.1/arch/arm/boot/dts/rk3506g-luckfox-lyra.dts. The following code snippet shows how to enable CAN:

    &can0 {
    assigned-clocks = <&cru CLK_CAN0>;
    assigned-clock-rates = <200000000>;
    pinctrl-names = "default";
    pinctrl-0 = <&rm_io30_can0_tx &rm_io31_can0_rx>;
    status = "okay";

    };