linux CAN通讯基于Qt代码编写

本文介绍了如何在Qt中实现Linux CAN通信。通过图形界面进行CAN的初始化、接收和发送,特别强调了在接收过程中使用线程以避免界面卡顿的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文章已经生成可运行项目,

由于工作需要,将linux中的can通讯编写到Qt上,通过图形界面的方式演示出来。
在这里插入图片描述

CAN的初始化

int can::startCan(QString baud)
{
     int ret = 0;
     ret = system("ifconfig can0 down");
     if (ret != 0)
     {
          return -1;
     }
     ret = system(QString("ip link set can0 up type can bitrate %1").arg(baul).toLatin1());
     if (ret != 0) 
     {
          return -1;
     }
     ret = system("ifconfig can0 up");
     if (ret != 0) 
     {
          return -1;
     }
    
    //创建套接字
    //PF_CAN 为域位 同网络编程中的AF_INET 即ipv4协议
    //SOCK_RAW使用的协议类型 SOCK_RAW表示原始套接字 报文头由自己创建
    //CAN_RAW为使用的具体协议 为can总线协议
    socket =  ::socket(PF_CAN,SOCK_RAW,CAN_RAW);//创建套接字
    if (socket<0)
    {
            qDebug()<<"[error]: socket\n";
            return -1;
    }
    struct ifreq ifr;//接口请求结构体
    strcpy((char *)(ifr.ifr_name),"can0");//判断开启的是can0/1
    fcntl(socket, F_SETFL, 0);            
    ioctl(socket, SIOCGIFINDEX, &ifr);//指定 CAN0设备
    addr.can_family = AF_CAN;//协议类型
    addr.can_ifindex = ifr.ifr_ifindex;//can总线外设的具体索引 类似 ip地址
    ret = bind(socket,(struct sockaddr*)&addr,sizeof(addr));//将套接字和canbus外设进行绑定,即套接字与 can0 绑定
    if(ret<0)
    {
           qDebug()<<"[error]: can bind fail #"<<ret;
           return -1;
    } 
    return 0;
}

CAN接收

接收这里 要 注意的是,需要用到线程去接收,一开始我只是使用定时器去监控,就会出现当把can开启后,这个界面就会很卡顿,如果使用线程thread就可以解决这个问题。

int can::canrecv( struct can_frame *frame, uint32_t timeout_ms)
{
    int ret,nbytes;
    struct timeval tv;
    struct msghdr msg;
    struct iovec iov;
    struct can_frame frame;
    fd_set rdfs;
    char ctrlmsg[CMSG_SPACE(sizeof(struct timeval) + 3*sizeof(struct timespec) + sizeof(__u32))];

    //poll mode
    tv.tv_sec=timeout_ms/1000;
    tv.tv_usec=(timeout_ms % 1000)*1000;

    FD_ZERO(&rdfs);
    FD_SET(socket, &rdfs);
    ret=select(socket+1, &rdfs, NULL, NULL, &tv);
    if(ret==0) { //timeout
        return 0;
    }
    else if(ret<0) { //error
        return ret;
    }   
    nbytes = read(socket, &frame, sizeof(frame)); //接收报文
    if(nbytes > 0)
    {
            QByteArray array;
            for (int i = 0; i < frame.can_dlc; i++)
            {
                array[i] = frame.data[i];
            }
     }
    return nbytes;
}

can发送

int can::cansend( uint16_t id, uint8_t *buf, uint8_t len)
{
    int i;
    int ret;
    struct can_frame frame;
    int size = sizeof(frame);
    if(socket < 0)
    {
        return -1;
    }
        frame.can_id = id;
        for(i=0; i<len; i++)
        {
            frame.data[i] = buf[i];
            qDebug()<<frame.data[i];
        }
        frame.can_dlc = len;

        qDebug()<<socket<<frame.can_dlc<< frame.can_id<<size;
        ret = write(socket, &frame, size);
        if (ret != size) {
             qDebug()<<"ret="<<ret;
            return -1;
        }

    return len;
}

相关结构体


struct ifreq
  {
# define IFHWADDRLEN 6
# define IFNAMSIZ IF_NAMESIZE
    union
      {
        char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */
      } ifr_ifrn;
 
    union
      {
        struct sockaddr ifru_addr;
        struct sockaddr ifru_dstaddr;
        struct sockaddr ifru_broadaddr;
        struct sockaddr ifru_netmask;
        struct sockaddr ifru_hwaddr;
        short int ifru_flags;
        int ifru_ivalue;
        int ifru_mtu;
        struct ifmap ifru_map;
        char ifru_slave[IFNAMSIZ]; /* Just fits the size */
        char ifru_newname[IFNAMSIZ];
        __caddr_t ifru_data;
      } ifr_ifru;
  };

/**
 * struct sockaddr_can - the sockaddr structure for CAN sockets
 * @can_family:  address family number AF_CAN.
 * @can_ifindex: CAN network interface index.
 * @can_addr:    protocol specific address information
 */
struct sockaddr_can {
	__kernel_sa_family_t can_family;
	int         can_ifindex;
	union {
		/* transport protocol class address information (e.g. ISOTP) */
		struct { canid_t rx_id, tx_id; } tp;

		/* reserved for future CAN protocols address information */
	} can_addr;
};
本文章已经生成可运行项目
Linux下面QTCan通信程序,网络上有很多例子都是互相抄的;本人因为项目原因,通过源代码Socket函数,写了完整的程序,含有2个案例;分享给大家; 这里主要是包含几个步骤,1:绑定Socket;2:cna/can1的设置,3:波特率的设置(如果发送和接收波特率不一致肯定不行的);4:发送;5:接收处理; 下面就贴出部分代码: void MyWindow::startcan(int number) { int ret = 0; //can先关闭 设置好波特率后 再开启can if(number == 0) //can0 { system("ifconfig can0 down");//先关闭 system("ip link set can0 up type can bitrate 50000 triple-sampling on");//设置波特率 system("ifconfig can0 up");//再开启 } else //can1 { system("ifconfig can1 down");//先关闭 system("ip link set can1 up type can bitrate 50000 triple-sampling on");//设置波特率 system("ifconfig can1 up");//再开启 } socket = ::socket(PF_CAN,SOCK_RAW,CAN_RAW); struct ifreq ifr; strcpy((char *)(ifr.ifr_name),number == 0 ? "can0" : "can1"); ioctl(socket,SIOCGIFINDEX,&ifr); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; ret = bind(socket,(struct sockaddr*)&addr,sizeof(addr)); if (ret OpenCan(50000); ArmCan = new Thread(zyhapi,socket,port); ArmCan->start(); } void MyWindow::stopcan(int number) { if(ArmCan) { ArmCan->stop(); ArmCan->terminate(); ArmCan->wait(); } zyhapi->CloseCan(number); } //发送 void MyWindow::on_sendbtn_clicked() { /* struct can_frame frame; memset(&frame,0,sizeof(struct can_frame)); std::string str=ui->edit->text().toStdString(); if(str.length() > 8) { QMessageBox::about(this,"error","length of send string must less than 8 bytes"); return; } */ struct can_frame frame; memset(&frame,0,sizeof(struct can_frame)); char buf[8]={0X20,0XFF,0X01,0X02,0X03,0X04,0XFF,0XFF}; frame.can_id = 0x00000020;//发出去的帧ID即:0X00000020 frame.can_dlc = 8;//帧数据长度 for(int i=0;i<frame.can_dlc;i++) { frame.data[i]=buf[i];//帧数据 } //frame.can_id = 0x123; //strcpy((char*)frame.data,str.c_str()); //frame.can_dlc = str.length(); sendto(socket,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr)); /* struct can_frame frame; char buf[8]={0X20,0XFF,0X01,0X02,0X03,0X04,0XFF,0XFF}; frame.can_id = 0x00000020;//发出去的帧ID即:0X00000020 frame.can_dlc = 8;//帧数据长度 for(int i=0;iWriteCan(frame.can_id,frame.data,frame.can_dlc);//发数据,通过can端口-- 暂时默认 CAN0 端口 */ }
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

赟赟、嵌入式

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

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

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

打赏作者

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

抵扣说明:

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

余额充值