【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 FrameIdentifierRTRIDEDataCRCAckEnd 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 <iostream>
#include <cstring>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>

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

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 <QCoreApplication>
#include <canlib.h>  // 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();
}

  • 4
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个基于STM32的CAN通信实例,使用了HAL库和标准外设库。 首先,需要在CubeMX中配置CAN控制器。选择相应的引脚和时钟源,然后生成代码。 接下来是主要的代码实现。 1. 初始化CAN控制器 ```c CAN_HandleTypeDef hcan; void MX_CAN_Init(void) { hcan.Instance = CAN1; hcan.Init.Prescaler = 4; //CAN总线波特率=时钟频率/((Prescaler+1)*(BS1+1)*(BS2+1)) hcan.Init.Mode = CAN_MODE_NORMAL; hcan.Init.SyncJumpWidth = CAN_SJW_1TQ; hcan.Init.TimeSeg1 = CAN_BS1_13TQ; hcan.Init.TimeSeg2 = CAN_BS2_2TQ; hcan.Init.TimeTriggeredMode = DISABLE; hcan.Init.AutoBusOff = DISABLE; hcan.Init.AutoWakeUp = DISABLE; hcan.Init.AutoRetransmission = ENABLE; hcan.Init.ReceiveFifoLocked = DISABLE; hcan.Init.TransmitFifoPriority = DISABLE; if (HAL_CAN_Init(&hcan) != HAL_OK) { Error_Handler(); } } ``` 2. 配置CAN过滤器 ```c CAN_FilterTypeDef sFilterConfig; void MX_CAN_Filter_Config(void) { sFilterConfig.FilterBank = 0; sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; sFilterConfig.FilterIdHigh = 0x0000; sFilterConfig.FilterIdLow = 0x0000; sFilterConfig.FilterMaskIdHigh = 0x0000; sFilterConfig.FilterMaskIdLow = 0x0000; sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; sFilterConfig.FilterActivation = ENABLE; sFilterConfig.SlaveStartFilterBank = 14; if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK) { Error_Handler(); } } ``` 3. 发送CAN消息 ```c void CAN_Transmit(uint32_t id, uint8_t* data, uint8_t length) { CAN_TxHeaderTypeDef TxHeader; TxHeader.StdId = id; TxHeader.ExtId = 0; TxHeader.RTR = CAN_RTR_DATA; TxHeader.IDE = CAN_ID_STD; TxHeader.DLC = length; uint32_t TxMailbox; if (HAL_CAN_AddTxMessage(&hcan, &TxHeader, data, &TxMailbox) != HAL_OK) { Error_Handler(); } } ``` 4. 接收CAN消息 ```c void CAN_Receive(void) { CAN_RxHeaderTypeDef RxHeader; uint8_t data[8]; if (HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, data) == HAL_OK) { //处理接收到的数据 } } ``` 最后,在main函数中调用相关函数即可完成CAN通信的初始化和使用。 ```c int main(void) { //初始化CAN控制器和过滤器 MX_CAN_Init(); MX_CAN_Filter_Config(); while(1) { //发送CAN消息 uint8_t data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; CAN_Transmit(0x123, data, 8); //接收CAN消息 CAN_Receive(); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Holy meat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值