linux下QT基于socket的can网络通信

  • Post author:
  • Post category:linux


附上Linux下周立功can配置文档:

1.cpp代码

Can_Control::Can_Control()
{


}
    int Can_Control::can_send()
    {
        int stSocket_LO, stSend_LO;
        struct sockaddr_can addr; //can总线的地址 同socket编程里面的 socketaddr结构体 用来设置can外设的信息
        struct ifreq ifr;//接口请求结构体

        struct can_frame frame[2] = {{0}}; //要发送的buffer
        /* 创建socket套接字
        PF_CAN 为域位 同网络编程中的AF_INET 即ipv4协议
        SOCK_RAW使用的协议类型 SOCK_RAW表示原始套接字 报文头由自己创建
        CAN_RAW为使用的具体协议 为can总线协议
        */
        stSocket_LO = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字
        strcpy(ifr.ifr_name, "can0" );
        ioctl(stSocket_LO, SIOCGIFINDEX, &ifr); //指定 can0 设备
        addr.can_family = AF_CAN; //协议类型
        addr.can_ifindex = ifr.ifr_ifindex; //can总线外设的具体索引 类似 ip地址
        bind(stSocket_LO, (struct sockaddr *)&addr, sizeof(addr));//将套接字和canbus外设进行绑定,即套接字与 can0 绑定
        //禁用过滤规则,本进程不接收报文,只负责发送
        setsockopt(stSocket_LO, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
        //生成两个报文
        frame[0].can_id = 0x11;
        frame[0]. can_dlc = 1;
        frame[0].data[0] = '1';
        frame[1].can_id = 0x22;
        frame[1]. can_dlc = 1;
        frame[1].data[0] = '2';
        //循环发送两个报文
    while(1)
        {
            stSend_LO = write(stSocket_LO, &frame[0], sizeof(frame[0])); //发送 frame[0]
            if(stSend_LO != sizeof(frame[0]))
            {
                printf("Send Error frame[0]\n!");
                break; //发送错误,退出
            }
            sleep(1);
            stSend_LO = write(stSocket_LO, &frame[1], sizeof(frame[1])); //发送 frame[1]
            if(stSend_LO != sizeof(frame[0]))
            {
                printf("Send Error frame[1]\n!");
                break;
            }
        sleep(1);
        }
        close(stSocket_LO);//关闭套接字
        return 0;
    }

    int Can_Control::can_recv()
    {

        int stSocket_LO, stRecv_LO;
                struct sockaddr_can addr;
                struct ifreq ifr;
            struct can_frame frame;
                struct can_filter rfilter[1];
                stSocket_LO = socket(PF_CAN, SOCK_RAW, CAN_RAW); //创建套接字
                strcpy(ifr.ifr_name, "can0" );
                ioctl(stSocket_LO, SIOCGIFINDEX, &ifr); //指定 can0 设备
            addr.can_family = AF_CAN;
            addr.can_ifindex = ifr.ifr_ifindex;
                bind(stSocket_LO, (struct sockaddr *)&addr, sizeof(addr)); //将套接字与 can0 绑定
                //定义接收规则,只接收表示符等于 0x11 的报文
                rfilter[0].can_id = 0x11;
                rfilter[0].can_mask = CAN_SFF_MASK;
                //设置过滤规则
                setsockopt(stSocket_LO, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
                while(1)
                {
                    stRecv_LO = read(stSocket_LO, &frame, sizeof(frame)); //接收报文
                    //显示报文
                if(stRecv_LO > 0)
                    {
                    printf("Idata[0]=0x%x\n",frame.data[0]);

                    }
                }
                close(stSocket_LO);
                return 0;
    }




    int Can_Control::socket_connect(const char*m_port)
    {
        int stSocket_LO = -1;
        int ret = 0;
        struct sockaddr_can canbus_addr_LO;   //can总线的地址 同socket编程里面的 socketaddr结构体 用来设置can外设的信息
        struct ifreq ifreq_LO;           //接口请求结构体
        stSocket_LO = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字
        if(stSocket_LO<0)
        {
            QMessageBox::information(NULL, QString::fromLocal8Bit("错误"), QString::fromLocal8Bit("打开CAN设备失败"));
            return -1;
        }
        strcpy(ifreq_LO.ifr_name, "can0");//对CAN接口进行初始化,如设置CAN接口名,即当我们用ifconfig命令时显示的名字
        ret=ioctl(stSocket_LO, SIOCGIFINDEX, &ifreq_LO); //指定 can设备
        if(ret<0)
        {
            QMessageBox::information(NULL, QString::fromUtf8("错误"), QString::fromUtf8("匹配CAN设备错误"));
            return -1;

        }
        /* 设置CAN协议 */
        canbus_addr_LO.can_family = AF_CAN; //协议类型
        canbus_addr_LO.can_ifindex = ifreq_LO.ifr_ifindex; //can总线外设的具体索引 类似 ip地址
        ret=bind(stSocket_LO, (struct sockaddr *)&canbus_addr_LO, sizeof(canbus_addr_LO));//将套接字和canbus外设进行绑定,即套接字与 can0 绑定
        if(ret<0)
        {
            close_socket(stSocket_LO);//关闭套接字
            QMessageBox::information(NULL, QString::fromUtf8("错误"), QString::fromUtf8("绑定套接字错误"));
            return -1;

        }
        return stSocket_LO;


    }


    void Can_Control::close_socket(const int sockfd)
    {
        if (sockfd != -1)
        {
            close(sockfd);
        }
    }

    void Can_Control::set_can_filter()//设置滤波
    {

        const int n = 1;
        struct can_filter rfilter[n];

        // 过滤规则:当<received_can_id> & mask == can_id & mask时,接收报文,否则过滤掉.
        // 可以同时添加多条过滤规则

        // 在用来发送CAN帧的CAN_RAW套接字上不接收任何CAN帧
        rfilter[0].can_id   = 0x00000000;
        rfilter[0].can_mask = CAN_EFF_MASK;
        (void)setsockopt(send_socket_fd, SOL_CAN_RAW, CAN_RAW_FILTER, rfilter, n * sizeof(struct can_filter));

        // 在用来接收CAN帧的CAN_RAW套接字上禁用接收过滤规则
        (void)setsockopt(recv_socket_fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);

    }



    int Can_Control::send_frame( int sockfd, const void* data, const int count)
    {
        int ret = write(sockfd, (const char*)data, count);
        if (ret != count)
        {
            QMessageBox::information(NULL, QString::fromUtf8("错误"), QString::fromUtf8("发送错误"));
            return 1;
        }
        return  0;

    }

    int Can_Control::recv_frame( int sockfd, byte* buf, const int count, const int timeout_ms)
    {
        struct timeval tv_timeout;
        tv_timeout.tv_sec  = timeout_ms /1000;
        tv_timeout.tv_usec = (timeout_ms % 1000) * 1000;
        fd_set fs_read;

        FD_ZERO(&fs_read);
        FD_SET(sockfd, &fs_read);	//如果fd == -1, FD_SET将在此阻塞

        int ret = select((int)sockfd + 1, &fs_read, NULL, NULL, &tv_timeout);
        if (ret == 0) // recv 超市
        {
            return  0;
        }
        if (ret < 0) // select 错误
        {
            return  ret;
        }


        ret = read(sockfd, (char*)buf, count);

        //ret = recv(sockfd, (char*)buf, count, 0);


        if (ret <= 0)
        {
            return  -1;
        }
        return  ret;
    }

用Can_Control类管理can的通信。

can_send()和can_recv()函数是示例函数,调用可以直接接收发送,用意在于帮助大家了解基于Socket的can通信的流程。

后面对各模块的配置函数,如链接函数负责开启can通信并进行相应配置,本例还有许多可优化的地方,欢迎大家积极参与并分享。

下面是头文件:

#ifndef CAN_CONTROL_H
#define CAN_CONTROL_H

#include <QString>
#include <stdio.h>
#include <stdlib.h>
#include <string.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>
#include <QDebug>

#define printf qDebug
typedef __u8        byte;
static int  send_socket_fd=-1 ;//接受发送套接字
static int  recv_socket_fd=-1 ;
class Can_Control
{
public:
    Can_Control();
    int can_send();
    int can_recv();
    int socket_connect(const char*m_port);
    void close_socket(const int sockfd);
    void set_can_filter();//设置滤波
    int send_frame(int sockfd, const void* data, const int count);
    int recv_frame( int sockfd, byte* buf, const int count, const int timeout_ms);
     //struct canfd_frame Cs_nframe;   //要发送的buffer

    //struct canfd_frame Rc_nframe;   //要接收的buffer
    //string m_candevice;//can设备号,默认can0
    int  stSocket;



};

#endif // CAN_CONTROL_H

用法实例:

void MainWindow::on_toolButton_4_clicked()
{
  // Errormsg_Dlg *errordlg=new Errormsg_Dlg();
  // errordlg->show();
    struct can_frame frame[2] = {{0}}; //要发送的buffer

    frame[0].can_id = 0x11;
    frame[0]. can_dlc = 8;
    frame[0].data[0] = 0x03;
    frame[0].data[1] = 0x1a;
    frame[1].can_id = 0x22;
    frame[1]. can_dlc = 1;
    frame[1].data[0] = 0x22;
 //const byte* data, const int count
    int test;
   // __u8  send_frame_data[8] = {0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

    // memcpy(frame[0].data, send_frame_data, 8);
    test=can0.socket_connect("can0");
    can0.set_can_filter();
    can0.send_frame(test,&frame[0],sizeof(frame[0]));//
}

void MainWindow::on_toolButton_5_clicked()
{
    //Operation_records_Dlg* recorddlg=new Operation_records_Dlg();
   // recorddlg->show();
   struct can_frame recvframe;

        byte *precvframe = (byte *)&recvframe;

        const int can_frame_len = sizeof(can_frame);
    int test;
    int ret;
    //struct can_frame frame[50];
    test=can0.socket_connect("can0");
    memset(precvframe, 0x00, can_frame_len);
    ret=can0.recv_frame(test,precvframe,can_frame_len,5*1000);
    if(ret>0)
    {
        QString buffer;

        QByteArray temp((const char*)(recvframe.data),8);

           buffer = temp.toHex();
        QMessageBox::information(NULL, QString::fromUtf8("错误"), buffer);
    }
}

使用按钮槽函数触发发送没有毛病,但是在接收时,需将cantest的发送定为持续的,500ms发一次,发送50次。

好像只能一次发送一包数据,如果有谁知道如何实现一次发送多包数据的还望告知,感谢!!



版权声明:本文为liuwinner原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。