【CAN通信实例-附代码】

CAN通信实例

  • 写在前面
  • CAN通信报文格式
  • 使用SocketCAN库在Linux系统上进行CAN总线通信
    • 程序代码
    • 代码分析
  • windows下使用Qt进行CAN总线通信

写在前面

通过简单的程序来学习CAN总线的通讯,其中包括Linux系统下使用SocketCan库来进行通信,还有就是使用Qt来实现CAN通信,假设将设备通过CAN总线发送的速度来显示到QT界面上。

CAN通信报文格式

CAN(Controller Area Network)的数据报文格式通常由两种类型的消息构成:标准帧(Standard Frame)和扩展帧(Extended Frame)。这两种帧格式的结构略有不同,下面分别介绍它们的主要特点:
标准帧(Standard Frame):
标准帧用于传输常规的数据和消息。其主要特点包括:
11位标识符:标准帧使用11位的标识符(Identifier)来区分不同类型的消息。这个标识符通常用于表示消息的优先级和类型。

0-8个字节的数据:标准帧可以携带0到8个字节的数据。数据字节包含了消息的实际信息。控制位:标准帧包括一些控制位,用于标识消息的类型(数据帧或远程帧)以及是否需要进行数据重发等。

标准帧的基本结构如下:

Start of Frame Identifier RTR IDE Data CRC Ack End of Frame

Start of Frame:帧起始标志,表示帧的开始。
Identifier:11位标识符,用于标识消息的类型和优先级。
RTR(Remote Transmission Request):1位,指示消息是数据帧还是远程帧。
IDE(Identifier Extension):1位,指示是否使用扩展标识符。
Data:0-8个字节的数据,携带消息内容。
CRC(Cyclic Redundancy Check):15位的CRC校验码,用于检测数据的错误。
Ack:用于表示消息是否被接收成功。
End of Frame:帧结束标志,表示帧的结束。

扩展帧(Extended Frame):
扩展帧用于传输具有更大标识符空间的消息,通常用于更复杂的应用。其主要特点包括:
29位标识符:扩展帧使用29位的标识符来区分不同类型的消息,提供了更大的标识符空间。

0-8个字节的数据:与标准帧类似,扩展帧可以携带0到8个字节的数据。

控制位:扩展帧也包括一些控制位,用于标识消息的类型和其他信息。

扩展帧的基本结构与标准帧类似,但标识符位数更多,CRC位数也可能不同,具体取决于CAN协议版本和实现。
CAN通信的报文格式与硬件和应用有关,通常需要根据实际需求和硬件配置进行适当的配置和设置。这些报文格式用于在CAN总线上传输数据和消息,以实现各种应用,如车辆控制、工业自动化和嵌入式系统通信。

使用SocketCAN库在Linux系统上进行CAN总线通信

C++编写两个程序,一个用于发送CAN消息,另一个用于接收CAN消息。这两个程序都使用SocketCAN库进行CAN总线通信。

程序代码

发送程序

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main() {
    const char *can_interface = "can0";  // 根据您的系统和硬件配置选择适当的接口名称

    int socket_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (socket_fd == -1) {
        perror("Socket creation error");
        return 1;
    }

    struct ifreq ifr{};
    std::strncpy(ifr.ifr_name, can_interface, IFNAMSIZ - 1);
    if (ioctl(socket_fd, SIOCGIFINDEX, &ifr) == -1) {
        perror("ioctl error");
        close(socket_fd);
        return 1;
    }

    struct sockaddr_can addr{};
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;

    if (bind(socket_fd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == -1) {
        perror("Binding error");
        close(socket_fd);
        return 1;
    }

    struct can_frame frame{};
    frame.can_id = 0x123;  // 设置消息的标识符
    frame.can_dlc = 4;     // 设置消息数据长度
    frame.data[0] = 0x01;
    frame.data[1] = 0x02;
    frame.data[2] = 0x03;
    frame.data[3] = 0x04;

    if (write(socket_fd, &frame, sizeof(struct can_frame)) == -1) {
        perror("Write error");
        close(socket_fd);
        return 1;
    }

    close(socket_fd);
    return 0;
}

接收程序

#include 
#include 
#include 
#include 
#include 
#include 

int main() {
    const char *can_interface = "can0";  // 根据您的系统和硬件配置选择适当的接口名称

    int socket_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (socket_fd == -1) {
        perror("Socket creation error");
        return 1;
    }

    struct ifreq ifr{};
    std::strncpy(ifr.ifr_name, can_interface, IFNAMSIZ - 1);
    if (ioctl(socket_fd, SIOCGIFINDEX, &ifr) == -1) {
        perror("ioctl error");
        close(socket_fd);
        return 1;
    }

    struct sockaddr_can addr{};
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;

    if (bind(socket_fd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == -1) {
        perror("Binding error");
        close(socket_fd);
        return 1;
    }

    struct can_frame frame{};

    while (true) {
        if (read(socket_fd, &frame, sizeof(struct can_frame)) == -1) {
            perror("Read error");
            close(socket_fd);
            return 1;
        }
        std::cout << "Received Message: ID=" << std::hex << frame.can_id << " Data=";
        for (int i = 0; i < frame.can_dlc; ++i) {
            std::cout << std::hex << static_cast<int>(frame.data[i]) << " ";
        }
        std::cout << std::endl;
    }

    close(socket_fd);
    return 0;
}

代码分析

1、在主函数中,定义了要使用的CAN总线接口的名称:

const char *can_interface = "can0";  // 根据您的系统和硬件配置选择适当的接口名称

2、创建一个套接字来与CAN总线通信:

int socket_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (socket_fd == -1) {
    perror("Socket creation error");
    return 1;
}

3、使用ioctl函数获取CAN接口的索引:

struct ifreq ifr{};
std::strncpy(ifr.ifr_name, can_interface, IFNAMSIZ - 1);
if (ioctl(socket_fd, SIOCGIFINDEX, &ifr) == -1) {
    perror("ioctl error");
    close(socket_fd);
    return 1;
}

4、配置套接字地址以绑定到特定的CAN接口:

struct sockaddr_can addr{};
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;

if (bind(socket_fd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == -1) {
    perror("Binding error");
    close(socket_fd);
    return 1;
}

5、创建一个CAN帧并设置其标识符和数据:

struct can_frame frame{};
frame.can_id = 0x123;  // 设置消息的标识符
frame.can_dlc = 4;     // 设置消息数据长度
frame.data[0] = 0x01;
frame.data[1] = 0x02;
frame.data[2] = 0x03;
frame.data[3] = 0x04;

6、使用write函数将CAN帧发送到CAN总线上:

if (write(socket_fd, &frame, sizeof(struct can_frame)) == -1) {
    perror("Write error");
    close(socket_fd);
    return 1;
}

7、在一个无限循环中,使用read函数接收来自CAN总线的CAN帧,并打印它们的标识符和数据:

while (true) {
    if (read(socket_fd, &frame, sizeof(struct can_frame)) == -1) {
        perror("Read error");
        close(socket_fd);
        return 1;
    }
    std::cout << "Received Message: ID=" << std::hex << frame.can_id << " Data=";
    for (int i = 0; i < frame.can_dlc; ++i) {
        std::cout << std::hex << static_cast<int>(frame.data[i]) << " ";
    }
    std::cout << std::endl;
}

windows下使用Qt进行CAN总线通信

在Windows上使用Qt进行CAN总线通信,可以使用第三方CAN库来支持CAN通信。在Windows环境下,SocketCAN不直接可用,但有一些其他CAN库和工具可以帮助您实现CAN通信。这里使用Kvaser的CANlib库以及Qt来创建一个简单的CAN通信应用程序。假设设备通过CAN总线发送了车速信息,qt端来接收车速信息并显示。

#include 
#include   // Kvaser CANlib头文件

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    // 初始化CANlib
    canStatus stat = canInitializeLibrary();
    if (stat != canOK) {
        qDebug() << "CANlib initialization failed";
        return -1;
    }

    // 打开CAN通道
    int channel = 0; // 通道号,根据您的硬件配置设置
    canHandle hnd = canOpenChannel(channel, canOPEN_ACCEPT_VIRTUAL);

    if (hnd < 0) {
        qDebug() << "Failed to open CAN channel";
        canClose(hnd);
        return -1;
    }

    // 设置CAN通信参数,如波特率
    int bitrate = canBITRATE_500K; // 500 kbps,根据您的硬件和需求设置
    stat = canSetBusParams(hnd, bitrate, 0, 0, 0, 0, 0);
    if (stat != canOK) {
        qDebug() << "Failed to set CAN bus parameters";
        canClose(hnd);
        return -1;
    }

    // 启动CAN通信
    stat = canBusOn(hnd);
    if (stat != canOK) {
        qDebug() << "Failed to start CAN bus";
        canClose(hnd);
        return -1;
    }

    // 接收CAN消息
    while (true) {
        canMessage msg;
        stat = canRead(hnd, &msg);
        if (stat == canOK) {
            if (msg.id == 0x123) { // 假设车速信息的CAN消息标识符为0x123
                int speed = (msg.data[0] << 8) | msg.data[1];
                qDebug() << "Vehicle Speed: " << speed << " km/h";
            }
        }
    }

    // 停止CAN通信并关闭通道
    canBusOff(hnd);
    canClose(hnd);

    // 反初始化CANlib
    canUnloadLibrary();

    return a.exec();
}

你可能感兴趣的:(linux,c++,qt5,CAN)