A40i使用笔记:qt使用socketcan发送/读取can总线数据(多通道)

一、前言

芯片使用mcp2515,使用spi转can的方式,配置好linux中的设备树和驱动文件后,在can一切正常的情况下才能继续下面的工作,前期我这边也就是调试出驱动,总共调试了两路can,第一路用spi1,第二路用spi2,调试第二路有些不顺利是因为spi2默认引脚电平是1.8v,后来重画电路板加了一个电平转换芯片解决问题,从此就可以驱动两路can啦。此贴是我调试2路spi方式

二、环境

开发环境:window10,ubuntu16.04

目标arm环境:A40i,linux3.10,qt5.9

三、正文

话不多说,正文开始,捞干的说就是在已经调试好can0、can1的设备节点后,在qt上写相应的程序去驱动,官方手册自带的测试方法仅仅能够用于测试,无法用于程序开发,这里介绍在qt5.9上做出can读取和发送数据,使用标准帧或扩展帧两种方式,目前使用最高频率是500k,已经买了24M晶振,准备提升频率到1M

更新:

买的24M晶振到了,换上之后,修改一下设备树的时钟为24000000,测试暂时没发现什么问题,但是有些丢帧,建议使用速率不是必须达到500k以上时,还是用8M晶振,必须1M速率时容易丢帧

        clocks {
            mcp251x_clock:mcp251x_clock{
                   compatible = "fixed-clock";
                   #clock-cells = <0>;
                   clock-frequency = <24000000>;
            };
        };

(福利)本文不做任何保留,全部代码如下,工程文件我就不上传了,没多少东西,都是干的,可在此基础上继续开发。

驱动节点信息

pro文件

QT       += core gui serialport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = socketcan
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
DEPENDPATH += .
INCLUDEPATH += .

# Input
HEADERS += \
           mainwindow.h \
           thread.h
FORMS += \
           mainwindow.ui

SOURCES += \
           main.cpp \
           mainwindow.cpp \
           thread.cpp

CONFIG += mobility
MOBILITY =

 main.cpp

#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    startcan(0);//开启CAN0
    QTimer *time1=new QTimer();
    time1->start(100);
    connect(time1,&QTimer::timeout,[=](){
        on_send_clicked();

    });
}

MainWindow::~MainWindow()
{
    delete ui;
    stopcan();//程序退出,关闭当前开启的CAN
}


//开启can0/1
void MainWindow::startcan(int v)
{
    if(v == 0){
        system("ifconfig can0 down");
        system("ip link set can0 up type can bitrate 500000 triple-sampling on");
        system("ifconfig can0 up");
    }
    else{
        system("ifconfig can1 down");
        system("ip link set can1 up type can bitrate 500000 triple-sampling on");
        system("ifconfig can1 up");
    }
    //创建套接字
    //PF_CAN 为域位 同网络编程中的AF_INET 即ipv4协议
    //SOCK_RAW使用的协议类型 SOCK_RAW表示原始套接字 报文头由自己创建
    //CAN_RAW为使用的具体协议 为can总线协议
    socket =  ::socket(PF_CAN,SOCK_RAW,CAN_RAW);//创建套接字

    struct ifreq ifr;//接口请求结构体
    strcpy((char *)(ifr.ifr_name),v == 0 ? "can0" : "can1");//判断开启的是can0/1

    //fcntl(socket, F_SETFL, ReadMode);        //标志FNDELAY可以保证read函数在端口上读不到字符的时候返回0
    fcntl(socket, F_SETFL, 0);            //回到正常(阻塞)模式

    ioctl(socket,SIOCGIFINDEX,&ifr);//指定 CAN0/1 设备

    addr.can_family = AF_CAN;//协议类型
    addr.can_ifindex = ifr.ifr_ifindex;//can总线外设的具体索引 类似 ip地址
    bind(socket,(struct sockaddr*)&addr,sizeof(addr));//将套接字和canbus外设进行绑定,即套接字与 can0/1 绑定

    //禁用过滤规则,进程不接收报文,只负责发送,如需接受注释掉此函数即可
    //setsockopt(stSocket_LO, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);

    t = NULL;
    t = new Thread(socket);//开启单独线程接受监听
    connect(t,SIGNAL(message(sockaddr,socklen_t)),this,SLOT(msg(sockaddr,socklen_t)));
    t->start();
}
void MainWindow::stopcan()
{
    if(t){//如果线程已经开启,关闭线程
        t->stop();
        t->deleteLater();
    }

    ::close(socket);

    system("ifconfig can0 down");//关闭CAN0
    system("ifconfig can1 down");//关闭CAN1
}
void MainWindow::on_send_clicked()
{
    struct can_frame frame;
    memset(&frame,0,sizeof(struct can_frame));

    frame.can_id = (0x123456 & CAN_EFF_MASK) | CAN_EFF_FLAG;//扩展帧
    //frame.can_id = (0x123456 & CAN_SFF_MASK);//标准帧
    frame.can_dlc= 8;
    frame.data[0]= 0x11;
    frame.data[1]= 0x22;
    frame.data[2]= 0x33;
    frame.data[3]= 0x44;
    frame.data[4]= 0xaa;
    frame.data[5]= 0xbb;
    frame.data[6]= 0xcc;
    frame.data[7]= 0xdd;

    //发送can数据:方式一
    //sendto(socket,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr));
    //发送can数据:方式二
    write(socket, &frame, sizeof(frame)); //发送 frame
    return;
}
void MainWindow::msg(sockaddr addr,socklen_t num)
{
    //qDebug()<<addr<<num<<buf;

}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QProcess>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QThread>
#include <QTimer>
#include <QDateTime>
#include <QDebug>
#include "thread.h"

extern "C" {
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/can.h>
}


namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
    
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
protected:

private slots:
    void on_send_clicked();
    void msg(sockaddr addr, socklen_t num);
    void stopcan();
    void startcan(int v);

private:
    Ui::MainWindow *ui;
    int socket;
    struct sockaddr_can addr;//can总线的地址 同socket编程里面的 socketaddr结构体 用来设置can外设的信息
    Thread *t;
};

#endif // MAINWINDOW_H

thread.cpp

#include "thread.h"
#include "mainwindow.h"
Thread::Thread(int s,QObject *parent) :
    QThread(parent)
{
    socket  = s;
    running = true;
}

void Thread::run()
{
    //qDebug()<<"start can receive Thread!";
    int nbytes;
    int len;
    struct can_frame frame;
    struct sockaddr_can addr;
    char buf[8];

    while(running){
        //接收can数据:方式一
        //nbytes=recvfrom(socket,&frame,sizeof(struct can_frame),0,(struct sockaddr *)&addr,(socklen_t*)&len);
//        if(nbytes>0){
//            memset(buf,0,8);
//            strncpy(buf,(char*)frame.data,8);
//            //emit message(&addr,&len);
//             printf("id=%x,len=%d\n",(struct sockaddr *)&addr,(socklen_t*)&len);
//        }
        //接收can数据:方式二
          nbytes = read(socket, &frame, sizeof(frame)); //接收报文
          if(nbytes > 0){
            printf("id=%x,len=%d,data=%x%x%x%x%x%x%x%x\n",frame.can_id&0x7fffffff,frame.can_dlc,frame.data[0],frame.data[1],frame.data[2],frame.data[3],frame.data[4],frame.data[5],frame.data[6],frame.data[7]);
          }
    }

}

void Thread::stop()
{
    running = false;
}

20230331这里更改方式一注释未注释全,导致偶尔发两次接收一次问题 

thread.h

#ifndef THREAD_H
#define THREAD_H

#include <QThread>
extern "C" {
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/can.h>
}
#ifndef PF_CAN
#define PF_CAN 29
#endif

#ifndef AF_CAN
#define AF_CAN PF_CAN
#endif

class Thread : public QThread
{
    Q_OBJECT
public:
    explicit Thread(int s,QObject *parent = 0);
    
signals:
    void message(sockaddr addr,socklen_t num);
public slots:
    void run();
    void stop();

private:
    int socket;
    bool running;

};

#endif // THREAD_H

全部代码已经贴完,欢迎查看使用,祝君成功!

测试效果如下:

稳定性测试:

500K(8M晶振上限):

未完待续

1M(24M晶振上限):

未完待续

四、结语

😔

  • 10
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
Qt 是一款跨平台的应用程序开发框架,能够用于开发各种类型的软件。上位机是指控制系统中的一个部分,负责与低级控制设备通信,实现控制和监控功能。CAN(Controller Area Network)是一种广泛应用于汽车、工业控制等领域的通信协议。现在,我们来谈一谈 Qt 上位机与 CAN 的结合应用。 Qt 上位机可以通过使用相应的 CAN 接口库来实现与 CAN 总线的通信。Qt 提供了丰富的网络和串口通信功能,发送和接收 CAN 消息可以通过网络通信接口或串口通信接口来完成。通过 Qt 团队或第三方开发的CAN库,我们可以轻松地在 Qt 上位机中实现与 CAN 总线的通信。 使用 Qt 上位机进行 CAN 通信的好处之一是,Qt 的跨平台特性使得我们可以在各种操作系统上运行我们的上位机应用程序。不管是在 Windows、Linux 还是 macOS 等系统上,Qt 都能提供一致性的开发体验和运行结果。 Qt 提供了易于使用且强大的界面设计工具,可以帮助我们设计出直观而美观的用户界面,用于显示和控制 CAN 总线上的数据。通过 Qt 的信号槽机制,我们可以方便地实现对 CAN 数据的处理和分发,使得上位机能够实时地获取和更新来自 CAN 总线的数据。 总之,Qt 上位机的 CAN 应用可以支持用户与 CAN 总线之间的数据交互和通信,提供了易用、跨平台和强大的界面设计能力。这使得我们能够快速开发出功能丰富且可靠的上位机应用,用于实现各种控制和监控任务。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大桶矿泉水

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

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

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

打赏作者

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

抵扣说明:

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

余额充值