基于QT的CAN通讯数据实时波形显示(连载三)========“CAN函数介绍”和“timer线程添加”和“UI表格制作”

前述工作:

    (1)基于QT的CAN通讯数据实时波形显示(连载二)已经可以正确打印接收到的数据,并且观察了debug出的数据,得出接收前期没有丢点的现象。

    (2)采用接收按钮的槽函数,每次只能点一次接收一次数据,并且接收到的数据个数等于程序中的100。

ReceiveNum = VCI_Receive(nDeviceType,nDeviceInd,nCANInd,Receive,100,400);

    注意:下位机上传数据位10ms一次,一次5帧,一次8个uint8_t(char)数据。具体数据格式为:

    具体说明下:128为报头,第2行到第6行为10ms的数据,第7行到第11行为20ms的数据,以此往下,每次传输数据都以128为报头开始传输。这个就是打印出来的数据(仅仅是数据对应,这个表是后来的),其中20是can的id地址,暂时不管。

第二部分:receive函数介绍

ReceiveNum = VCI_Receive(nDeviceType,nDeviceInd,nCANInd,Receive,100,400);

    函数为接收can帧函数,pdf中介绍receivenum为接收到帧的个数,验证后确实是(这个....说话有点问题,肯定是了)。函数中三个形参可以看看pdf就可以了解了,设置见《基于QT的CAN通讯数据实时波形显示(连载二)》,里面介绍的很详细。最后一个参数400是返回时间,可以不管,can正确的时候没啥用,can不正确的时候才有用,没研究,一个软件接收在实验室里面应该没啥问题,就算出故障了,也没办法了,下层硬件和传输线都很好,可能在调试不出来的时候才有用吧,暂且搁置,等can出现故障的时候再研究研究这个参数。

    最重要的是这个100。说明下试验步骤:

    (1)can帧1s传输一次,将100改成5,这个可以随意,也就是能接收到5帧。一直点“接收按钮”,也就是函数不到1s就执行一次,出现receivenum等于0的现象。解释:执行函数,没有接收到can帧,所以接收到帧数等于0。点几次之后,出现receivenum等于1,也就是接收到了1帧can帧。当间隔时间长一点点击接收按钮,差不多3s去执行一次接收函数,这时会出现receivenum等于3的现象。解释:can帧的接收存储在一个缓存里,具体位置应该是QT分配的,具体位置不知道,多大不知道。如果将点的时间加长,比如隔10s点一次,其实缓存里已经存储了10帧的can数据,这个时候就会等于receivenum就会等于5,再次点击,会再出现5帧,再次点击的话,receivenum就等于0了。所以得出结论:can帧会在软件里面缓存,目前没有测试具体能缓存多少帧,不知其上限值。100这个参数即为调出缓存里面数据的最大量。

    (2)can帧20ms传输一次。试验前猜测:点击非常快,比如20ms点击一次(有点夸张,其实用的是定时器),那么应该每次receivenum应该是5才对,应为20ms传输5帧。但是我错了。receivenum前期会很小,慢慢的receivenum会靠近100。receivenum数值和点击的频率没有关系,而是缓存100个数据之后,一次抛出。这个和串口接收挺像。所以这个参数最好设置为大于传输最大数据量一点点,这样就能正确传输了。

第三部分:timer线程函数

    直接点击接收按钮来接收太费劲,所以搞了个按钮,定时去执行receive函数。在h文件里添加。头文件,定义和槽函数。

#include <QtCore/QTimer>
public:
    QTimer m_timer;

private slots:
    void handleTimeout();

        主函数里面添加下列代码,第一句就不用了.......。里面的100是100ms执行一次。其他数据也行,只要别太小,至于最小多少,网上没有统一说法。

    ui->setupUi(this);
//定时器
    connect(&m_timer, &QTimer::timeout, this, &MainWindow::handleTimeout);
    m_timer.setInterval(100);

    槽函数

void MainWindow::handleTimeout()
{
}

    receive函数就放在这里,初始代码在《基于QT的CAN通讯数据实时波形显示(连载二)》有贴出。这样就能规定时间导出can帧数据了。

第四部分:table制作

    为了显示接收到的can数据,采用制表位。选择table widget。

    主程序中加入以下程序,设置下这个表。具体的效果参照第一张图。只搞了id号和8个数据位。这个仅仅是表格的制作,没有数据放置的程序。同时加入相对应头文件。

 #include <QTableWidget>
 #include <QTableWidgetItem>   

    ui->MESSAGE->setColumnCount(9);
    ui->MESSAGE->setHorizontalHeaderLabels(QStringList() << tr("ID") << tr("数据0") << tr("数据1") << tr("数据2") << tr("数据3") << tr("数据4") << tr("数据5") << tr("数据6") << tr("数据7"));
    ui->MESSAGE->setColumnWidth(0,40);
    ui->MESSAGE->setColumnWidth(1,40);
    ui->MESSAGE->setColumnWidth(2,40);
    ui->MESSAGE->setColumnWidth(3,40);
    ui->MESSAGE->setColumnWidth(4,40);
    ui->MESSAGE->setColumnWidth(5,40);
    ui->MESSAGE->setColumnWidth(6,40);
    ui->MESSAGE->setColumnWidth(7,40);
    ui->MESSAGE->setColumnWidth(8,40);
    ui->MESSAGE->setFixedWidth(400);
    ui->MESSAGE->horizontalHeader()->setStretchLastSection(true);
    ui->MESSAGE->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->MESSAGE->setSelectionBehavior(QAbstractItemView::SelectRows);

第五部分:数据代入

        代码如下:

void MainWindow::handleTimeout()
{
    ReceiveNum = VCI_Receive(nDeviceType,nDeviceInd,nCANInd,Receive,300,400);

    int CANID;
    uint8_t Data[8];
    if(ReceiveNum>0)
    {
        for (int i = 0; i < ReceiveNum; i++)
        {
            CANID = Receive[i].ID;

            Data[0]  = Receive[i].Data[0];

            QString strCANID   = QString::number(CANID);

            int nCount = ui->MESSAGE->rowCount();
            ui->MESSAGE->insertRow(nCount);

            QTableWidgetItem  *pItem = new QTableWidgetItem;
            pItem->setText(strCANID);
            ui->MESSAGE->setItem(nCount, 0, pItem);

            pItem = new QTableWidgetItem;
            pItem->setText(strData0);
            ui->MESSAGE->setItem(nCount, 1, pItem);
            ui->MESSAGE->scrollToBottom();
        }

    }
}

    解释:(1)函数名:时间中断的槽函数,

    (2)ReceiveNum为接收到数据的个数。当ReceiveNum大于0时,有数据接收到,执行下列程序。

    (3)receive为结构体的数组,上篇中详细介绍过。将can的id和data放入变量中,代码仅仅显示了id和一个变量的放置。余下的可以自己添加。

    (4)定义字符串,并将变量数据转化为字符串,为啥要转换呢,就是这样用的,没办法啊。

    (5)ncount为行数计数,并且有数据时插入一行,同时显示到表格的最左侧。第一张图中最左侧个数不是自己添加的,而是执行这句话之后自己出来的。

    (6)定义与tablewidget相关的参数,固定格式,将转化为字符串的canid放入其中,同时第一个数据的字符串也放入进去。这样就能在表格中显示接收到的数据了。

    此时可以在制表位中显示can传输的数据了,将表格中的数据测试了下,和下位机传输的can帧相同,可以确定接收到的数据没有丢点。

第六部分:保存

    制表位中的数据直接看的话,需要拉进度条,不太方便,可以存放在excel中。

    (1)加入保存按钮,加入保存按钮槽函数。

private slots:

    void on_SAVE_clicked();

   (2) 主体函数。里面啥意思没看,添加进去就可以用了。将头文件放在前面,后面是代码。注意:保存后的excel是不能直接处理的,里面有空格之类的(不能十进制转化为十六进制),原因和制表位里面的数据格式有关系,先保存,将数据存到txt里面,然后再拷回到excel中,就可以保存为数据类型了。

#include <QDesktopServices>
#include <QUrl>
#include <QFileDialog>
#include <QAxObject>
#include <QMessageBox>


void MainWindow::on_SAVE_clicked()
{
    QString fileName = QFileDialog::getSaveFileName(this,tr("Excle file"),QString("./test.xls"),tr("Excel Files(*.xls)"));    //设置保存的文件名
     if(fileName != NULL)
     {

         QAxObject *excel = new QAxObject;
         if(excel->setControl("Excel.Application"))
         {
             excel->dynamicCall("SetVisible (bool Visible)",false);
             excel->setProperty("DisplayAlerts",false);
             QAxObject *workbooks = excel->querySubObject("WorkBooks");            //获取工作簿集合
             workbooks->dynamicCall("Add");                                        //新建一个工作簿
             QAxObject *workbook = excel->querySubObject("ActiveWorkBook");        //获取当前工作簿
             QAxObject *worksheet = workbook->querySubObject("Worksheets(int)", 1);
             QAxObject *cell;
             int rowCount = ui->MESSAGE->rowCount();
             int columnCount = ui->MESSAGE->columnCount();

             /*添加Excel表头数据*/
             for(int i = 1; i <= columnCount ; i++)
             {
                 cell = worksheet->querySubObject("Cells(int,int)", 1, i);
                 cell->setProperty("RowHeight", 40);
                 cell->dynamicCall("SetValue(const QString&)", ui->MESSAGE->horizontalHeaderItem(i-1)->data(0).toString());
             }
             /*将form列表中的数据依此保存到Excel文件中*/
             for(int j = 2; j <= rowCount + 1;j++)
             {
                 for(int k = 1;k <= ui->MESSAGE->columnCount();k++)
                 {
                     cell = worksheet->querySubObject("Cells(int,int)", j, k);
                     cell->dynamicCall("SetValue(const QString&)",ui->MESSAGE->item(j-2,k-1)->text()+ "\t");
                 }
             }
             /*将生成的Excel文件保存到指定目录下*/
             workbook->dynamicCall("SaveAs(const QString&)",QDir::toNativeSeparators(fileName)); //保存至fileName
             workbook->dynamicCall("Close()");                                                   //关闭工作簿
             excel->dynamicCall("Quit()");                                                       //关闭excel
             delete excel;
             excel = NULL;

             if (QMessageBox::question(NULL,QString::fromUtf8("完成"),QString::fromUtf8("文件已经导出,是否现在打开?"),QMessageBox::Yes|QMessageBox::No)==QMessageBox::Yes)
             {
                 QDesktopServices::openUrl(QUrl("file:///" + QDir::toNativeSeparators(fileName)));
             }
         }
     }

}

第七部分:总结

    (1)can数据可以正确接收,不漏帧。(2)can数据可以显示到制表位中。(3)制表位中数据可以保存为excel,方便处理。

    注:由于小伙伴需要源代码的时间不同,登录邮箱界面太多麻烦,所以建立了一个订阅号,如果有问题或者需要源码,可添加订阅号,留言后会发送源代码或者有任何问题可留言,将积极解决提出的问题。

 

  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值