跳到主要内容

CAN

1. 简介

CAN,全称为Controller Area Network(控制器局域网),是一种广泛用于实时数据通信的串行通信协议。CAN协议最初是由德国公司Bosch开发的,用于汽车领域的电子控制单元(ECU)之间的通信。由于其高效、可靠的特性,CAN协议后来被广泛应用于其他领域,如工业自动化、医疗设备、船舶、航空航天等。

2 开启CAN

RM_IO 具有高度灵活性,支持将任意 RMIO 引脚指定为 CAN 接口。考虑到不同客户对于 CAN 接口的使用需求差异较大,我们预先开启的设置可能无法满足所有客户的实际需求。所以,出厂镜像默认未开启 CAN 接口功能。建议客户自行下载 SDK,在设备树中添加本节末尾提供的设备树内容,然后编译镜像,从而按需开启 CAN 接口。本示例以微雪电子的 USB-CAN-A 和 SN65HVD230 CAN Board 为基础进行测试。

3. 常用命令

  1. 与USB-CAN-FD模块通信,设置仲裁域波特率、数据域波特率为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发送:

    发送(标准帧,数据帧,ID:123,date:DEADBEEF): cansend can0 123##1DEADBEEF
    发送(扩展帧,数据帧,ID:00000123,date:DEADBEEF): cansend can0 00000123##1DEADBEEF
  3. CAN 2.0发送:

     发送(标准帧,数据帧,ID:123,date:DEADBEEF): cansend can0 123#DEADBEEF
    发送(标准帧,远程帧,ID:123): cansend can0 123#R
    发送(扩展帧,数据帧,ID:00000123,date:DEADBEEF): cansend can0 00000123#12345678
    发送(扩展帧,远程帧,ID:00000123): cansend can0 00000123#R
  4. CAN 2.0 与 CAN FD 接收。

    candump can0

4. CAN 测试(Python 程序)

如果您想使用 Python 代码进行 CAN 总线测试,python-can 是一个非常合适的 Python 库。它为 CAN 通信提供了高层封装,支持多种底层 CAN 接口,如 SocketCAN、PCAN 和 Kvaser 等,使开发者能够在 Python 中轻松进行 CAN 总线通信。通过 python-can,您可以专注于数据的发送与接收,而无需深入了解底层的复杂细节。

  1. 检测库是否安装成功。

    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()
  2. 编写发送程序(脚本命名尽量不要使用can.py,它可能与 python-can 库产生冲突)。

    #!/usr/bin/python3
    import can


    def send_can_message():
    # 使用 SocketCAN 方式连接到 can0
    bus = can.interface.Bus(channel='can0', bustype='socketcan')

    # 创建一个 CAN 消息
    msg = can.Message(arbitration_id=0x123, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=False)

    # 发送 CAN 消息
    try:
    bus.send(msg)
    print("Message sent on can0")
    except can.CanError:
    print("Message NOT sent")

    if __name__ == "__main__":
    send_can_message()
  3. 运行程序,在打开一个新的终端窗口:

    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 测试(C 程序)

SocketCAN是 Linux 系统中原生支持的一种 CAN 总线(Controller Area Network)接口。它通过标准的网络接口(类似以太网)来使用和配置 CAN 总线,提供了一个与常见网络协议栈(如TCP/IP)类似的接口。这使得使用 CAN 变得更加灵活和简洁。

主要特点:

  • 统一接口:SocketCAN 将 CAN 设备视为网络接口(如can0, can1),通过标准的网络API进行访问和配置。
  • 内核支持:SocketCAN 是 Linux 内核的一部分,可以直接通过系统调用访问,使用标准的 socket 函数进行发送和接收。
  • 协议支持:支持标准 CAN (CAN 2.0) 和 CAN FD(Flexible Data-rate)。
  • 设备抽象:将 CAN 控制器抽象为网络设备,使得通过标准网络工具(如ip命令)配置和管理 CAN 总线变得简单。

5.1 使用 SocketCAN 编程

  1. 创建Socket:

    int sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
  2. 绑定Socket到CAN接口:

    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. 发送和接收CAN帧:

    write(sockfd, &frame, sizeof(struct can_frame));
    read(sockfd, &frame, sizeof(struct can_frame));
  4. CAN 发送数据完整代码:

    #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;

    // 创建 CAN 套接字
    sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (sockfd < 0) {
    perror("Error while opening socket");
    return -1;
    }

    // 指定 CAN 接口(假设为 can0)
    strcpy(ifr.ifr_name, "can0");
    ioctl(sockfd, SIOCGIFINDEX, &ifr);

    // 绑定 CAN 套接字到接口
    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;
    }

    // 准备发送的 CAN 帧
    frame.can_id = 0x123; // 11位标准帧ID
    frame.can_dlc = 8; // 数据长度为8字节
    // 使用循环赋值 CAN 数据帧
    for (int i = 0; i < frame.can_dlc; i++)
    {
    frame.data[i] = i + 1; // 将数据从0x01到0x08依次赋值
    }


    // 发送 CAN 帧
    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. 交叉编译运行程序,搭建交叉编译环境请参考《程序编译》或《GPIO》部分。

    arm-none-linux-gnueabihf-gcc  can.c -o can
  6. 重新打开另一个终端接收数据。

    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. 设备树简介

  1. 设备文件路径位于kernel-6.1/arch/arm/boot/dts/rk3506g-luckfox-lyra.dts,开启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";

    };