Skip to main content

CAN

1. Ovieview

CAN, short for Controller Area Network, is a serial communication protocol widely used for real-time data transmission. Originally developed by the German company Bosch for communication between electronic control units (ECUs) in the automotive industry, the CAN protocol has since been widely adopted in other fields such as industrial automation, medical devices, maritime applications, and aerospace.

2 Enabling CAN

The development board has the CAN interface function enabled by default. This example uses the USB-CAN-A module from Waveshare for testing.

3. Common Commands

  1. Communicate with the USB-CAN-FD module and set the arbitration domain and data domain bit rates to 1M.

    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 Sending:

    Send (Standard frame, Data frame, ID:123, Data:DEADBEEF): cansend can0 123##1DEADBEEF
    Send (Extended frame, Data frame, ID:00000123, Data:DEADBEEF): cansend can0 00000123##1DEADBEEF
  3. CAN 2.0 Sending:

    Send (Standard frame, Data frame, ID:123, Data:DEADBEEF): cansend can0 123#DEADBEEF
    Send (Standard frame, Remote frame, ID:123): cansend can0 123#R
    Send (Extended frame, Data frame, ID:00000123, Data:DEADBEEF): cansend can0 00000123#12345678
    Send (Extended frame, Remote frame, ID:00000123): cansend can0 00000123#R
  4. CAN 2.0 and CAN FD Receiving:

    candump can0

4. CAN Test (Python Program)

If you want to perform CAN bus testing using Python, the python-can library is a very suitable option. It provides a high-level API for CAN communication and supports various lower-level CAN interfaces such as SocketCAN, PCAN, and Kvaser, allowing developers to easily work with CAN bus communication in Python. By using python-can, you can focus on sending and receiving data without delving into the complexities of the underlying details.

  1. Install the python-can library:

    sudo apt install python3-can
  2. Verify the library installation:

    root@luckfox:/home/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()
  3. Write a sending program (avoid naming the 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()
  4. Run the program by opening a new terminal window:

    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 Test (C Program)

SocketCAN is a native CAN bus interface supported by Linux. It uses a standard network interface (similar to Ethernet) to configure and communicate with the CAN bus, providing an API similar to common network protocol stacks (e.g., TCP/IP). This makes working with CAN more flexible and streamlined.

  • 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 <stdlib.h>
    #include <unistd.h>
    #include <net/if.h>
    #include <sys/ioctl.h>
    #include <sys/socket.h>
    #include <linux/can.h>
    #include <linux/can/raw.h>

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

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

    // Specify the CAN interface (assuming it is can0)
    strcpy(ifr.ifr_name, "can0");
    ioctl(sockfd, SIOCGIFINDEX, &ifr);

    // Bind the CAN socket to the interface
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    perror("Error in socket bind");
    return -2;
    }

    // Prepare to send a CAN frame
    frame.can_id = 0x123; // 11-bit standard frame ID
    frame.can_dlc = 8; // Data length is 8 bytes
    // Use a loop to assign CAN data frames
    for (int i = 0; i < frame.can_dlc; i++)
    {
    frame.data[i] = i + 1; // Assign data from 0x01 to 0x08 in sequence
    }


    // Send the CAN frame
    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 the socket
    close(sockfd);

    return 0;
    }
  5. Compile and run the program.

    gcc can.c -o can
    ./can
  6. Reopen another terminal to receive 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 CAN DTS source file has been defined inkernel-6.10/arch/arm64/boot/dts/rockchip/rk3576.dtsi,and we can call it directly.

    can0: can@2ac00000 {
    compatible = "rockchip,rk3576-canfd";
    reg = <0x0 0x2ac00000 0x0 0x1000>;
    interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&cru CLK_CAN0>, <&cru HCLK_CAN0>;
    clock-names = "baudclk", "apb_pclk";
    resets = <&cru SRST_CAN0>, <&cru SRST_H_CAN0>;
    reset-names = "can", "can-apb";
    dmas = <&dmac0 20>;
    dma-names = "rx";
    status = "disabled";
    };
  2. The device file path is located inkernel-6.1/arch/arm64/boot/dts/rockchip/luckfox-omni3576-can.dtsi,the following snippet enablescan0:

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