一、前言

芯片使用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晶振上限):

未完待续

四、结语

😔

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐