ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux-QT串口通信

2020-12-13 13:35:46  阅读:219  来源: 互联网

标签:opt cflag QT int 串口 SerialPort Linux include


  Linux-QT串口通信

  环境:Ubuntu18.04 QT4.8.6

  1. QT新建Qt Console Application

#include <QCoreApplication>

#include "ThreadTest.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    ThreadTest * thTest = new ThreadTest();
    thTest->start();

    return a.exec();
}
View Code

  2. 新建一个串口类SerialPort

  .h文件

#ifndef SERIALPORT_H
#define SERIALPORT_H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <QString>
#include <QDebug>

class SerialPort
{
public:
    // 构造
    SerialPort(QString devName);
    // 析构
    ~SerialPort();

public:
    // 设置波特率等
    int set_port_attr (int  baudrate, int  databit,
                        const char *stopbit, char parity, int vtime,int vmin );
    // 数据位
    void set_data_bit (struct termios *opt, unsigned int databit);
    // 校验位
    void set_parity (struct termios *opt, char parity);
    // 停止位
    void set_stopbit (struct termios *opt, const char *stopbit);
    // 写数据
    int Write(const uchar *data,const int len);
    // 读数据
    int Read(void *data,const int len, const int waitms);
    // 设置波特率
    void set_baudrate (struct termios *opt, uint baudrate);

private:
    int serialPortFd;


};

#endif // SERIALPORT_H
View Code

  .CPP文件

#include "SerialPort.h"

// 构造
SerialPort::SerialPort(QString devName)
{
    serialPortFd = open(devName.toAscii().data(), O_RDWR | O_NOCTTY);
    qDebug() << "serialPortFd = " << serialPortFd;

}

// 设置波特率等
int SerialPort::set_port_attr (int  baudrate, int  databit,
                               const char *stopbit, char parity, int vtime,int vmin ){
    struct termios opt;
    tcgetattr(serialPortFd, &opt);
    set_baudrate(&opt, baudrate);
    opt.c_cflag          |= CLOCAL | CREAD;      /* | CRTSCTS */
    set_data_bit(&opt, databit);
    set_parity(&opt, parity);
    set_stopbit(&opt, stopbit);
    opt.c_oflag          = 0;
    //opt.c_lflag           |= 0;
    opt.c_lflag &= ~(ICANON | ECHO | ECHOE);
    opt.c_oflag          &= ~OPOST;
    opt.c_cc[VTIME]     = vtime;
    opt.c_cc[VMIN]      = vmin;
    tcflush (serialPortFd, TCIFLUSH);
    return (tcsetattr (serialPortFd, TCSANOW, &opt));
}

// 设置波特率
void SerialPort::set_baudrate (struct termios *opt, uint baudrate)
{
    cfsetispeed(opt, baudrate);
    cfsetospeed(opt, baudrate);
}

// 数据位
void SerialPort::set_data_bit (struct termios *opt, unsigned int databit)
{
    opt->c_cflag &= ~CSIZE;
    switch (databit) {
    case 8:
        opt->c_cflag |= CS8;
        break;
    case 7:
        opt->c_cflag |= CS7;
        break;
    case 6:
        opt->c_cflag |= CS6;
        break;
    case 5:
        opt->c_cflag |= CS5;
        break;
    default:
        opt->c_cflag |= CS8;
        break;
    }
}

// 校验位
void SerialPort::set_parity (struct termios *opt, char parity)
{
    switch (parity) {
    case 'N':                                              /* 无校验        */
    case 'n':
        opt->c_cflag &= ~PARENB;
        break;
    case 'E':                                              /* 偶校验        */
    case 'e':
        opt->c_cflag |= PARENB;
        opt->c_cflag &= ~PARODD;
        break;
    case 'O':                                              /* 奇校验            */
    case 'o':
        opt->c_cflag |= PARENB;
        opt->c_cflag |= ~PARODD;
        break;
    default:                                                 /* 其它选择为无校验 */
        opt->c_cflag &= ~PARENB;
        break;
    }
}

// 停止位
void SerialPort::set_stopbit (struct termios *opt, const char *stopbit)
{
    if (0 == strcmp (stopbit, "1")) {
        opt->c_cflag &= ~CSTOPB;                            /* 1位停止位t         */
    }  else if (0 == strcmp (stopbit, "1.5")) {
        opt->c_cflag &= ~CSTOPB;                            /* 1.5位停止位    */
    }  else if (0 == strcmp (stopbit, "2")) {
        opt->c_cflag |= CSTOPB;
    }  else {
        opt->c_cflag &= ~CSTOPB;                             /* 1 位停止位        */
    }
}

// 写数据
int SerialPort::Write(const uchar *data,const int len){
    int returnLength = write(serialPortFd, data, len);                        /* 向串口发送字符串            */
    return returnLength;
}
// 读数据  waitms超时时间
int SerialPort::Read(void *data,const int len, const int waitms){
    fd_set inputs;
    struct timeval timeout;
    FD_ZERO(&inputs);
    FD_SET(serialPortFd,&inputs);
    timeout.tv_sec = waitms/1000;
    timeout.tv_usec = (waitms%1000)*1000;
    int returnLength = select(FD_SETSIZE,&inputs,(fd_set *)NULL,(fd_set *)NULL,&timeout);
    if (returnLength == 0){
        //qDebug() << "read timeout!\n";
        return -1;
    }
    //qDebug() << "ComDevice::Read  5";
    if (returnLength == -1){
        qDebug() << "select read device error!\n";
        return -2;
    }

    returnLength = read(serialPortFd, data, len);                        /* 在串口读取字符串            */
    return returnLength;
}

// 析构
SerialPort::~SerialPort(){

}
View Code

  3. 请求串口数据的线程类ThreadTest

  .h文件

#ifndef THREADTEST_H
#define THREADTEST_H

#include <QThread>
#include <QDateTime>

#include "SerialPort.h"

class ThreadTest : public QThread
{
public:
    ThreadTest();
    ~ThreadTest();

private:
    // 线程
    virtual void run();
    // 请求实时数据
    void RequestRealData();
    // 打印数据
    void PrintData(QString str, uchar *data, int length);

private:
    // 校验和,左闭右开
    ushort GetCheckSum(uchar * data, int startIndex, int endIndex);
    // 十六进制转ASCII
    uchar CharToAscii(uchar bHex);
    // ASCII转十六进制
    uchar AsciiToChar(uchar bChar);

private:
    // 串口
    SerialPort * serialPort;
};

#endif // THREADTEST_H
View Code

  .CPP文件

#include "ThreadTest.h"

ThreadTest::ThreadTest()
{
    serialPort = new SerialPort("/dev/ttymxc5");
    serialPort->set_port_attr(9600, 8, "1", 'N',20,255);
}

// 线程
void ThreadTest::run(){
    while (TRUE) {
        RequestRealData();
        sleep(2);
    }
}

// 请求实时数据
void ThreadTest::RequestRealData(){
    // 清除串口内存中数据
//    uchar clearData[148];
//    int clearLen = serialPort->Read(clearData, 148, 1000);
//    qDebug() << "clearLen = " << clearLen;

    uchar dataReq[18] = {0x7E,0x31,0x31,0x30,0x31,0x32,0x41,0x34,0x34,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00,0x0D };
    ushort checkSum = GetCheckSum(dataReq, 1, 13);
    dataReq[13] = CharToAscii((checkSum >> 12)&0x0F);
    dataReq[14] = CharToAscii((checkSum >> 8)&0x0F);
    dataReq[15] = CharToAscii((checkSum >> 4)&0x0F);
    dataReq[16] = CharToAscii((checkSum >> 0)&0x0F);

    PrintData(QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz") + " Send: ",
              dataReq, 18);
    // 2020.12.04 02:58:35

    serialPort->Write(dataReq, 18);

    usleep(20 * 1000);
    uchar realDataAck[148];
    int ret1 = serialPort->Read(realDataAck, 148, 1000);
    int ret2 = 0;
//    if(ret1 < 148){
//        ret1 = ret1 < 0 ? 0 : ret1;
//        usleep(1000 * 100);
//        ret2 = serialPort->Read(realDataAck+ret1, 148-ret1, 1000);
//    }

    if(ret1+ret2 <= 0){
        return;
    }

    PrintData(QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz") + " Revice: ",
              realDataAck, ret1+ret2);

    // ParseAlarmDataAck(realDataAck, ret1);
}

// 校验和,左闭右开
ushort ThreadTest::GetCheckSum(uchar * data, int startIndex, int endIndex){
    ushort sum = 0;
    for(int i = startIndex; i < endIndex; i++){
        sum += data[i];
    }

    sum = (~sum) + 1;
    return sum;
}

// 十六进制转ASCII
uchar ThreadTest::CharToAscii(uchar bHex){
    if((bHex>=0)&&(bHex<=9)){
        return bHex + 0x30;
    }

    return bHex += 0x37;
}

// ASCII转十六进制
uchar ThreadTest::AsciiToChar(uchar bChar){
    if((bChar>=0x30)&&(bChar<=0x39)){
        return bChar - 0x30;
    }else if((bChar>=0x41)&&(bChar<=0x46)){
        // Capital
        return bChar - 0x37;
    }

    // littlecase
    return bChar - 0x57;
}

// 打印数据
void ThreadTest::PrintData(QString str, uchar *data, int length){
    // return;

    for(int i = 0; i < length; i++){
        str += QString("%1 ").arg(data[i], 2, 16, QLatin1Char('0'));
    }

    qDebug() << "R:\t" << str;
}

ThreadTest::~ThreadTest()
{
    if(serialPort != NULL){
        delete serialPort;
        serialPort = NULL;
    }
}
View Code

  相关注释代码中都有了,相对比较简单,主要是读写操作,借用了一个实例,有些BCD码的转换和校验位的处理,实际操作中可以替换掉处理。

  注意事项:

    串口名字肯定是不一样的,图纸上都会有;

    检验位、数据位、奇偶校验这些设置;

    超时时间;

    请求数据的长度,接收数据的长度,这些不如在Windows上好用,都是需要自己注意处理的,有些代码是屏蔽的,比如是请求两次达到你想要的长度,这些可以防止一次请求不完;

    读请求应答ParseAlarmDataAck这个屏蔽的方法可以用来处理数据,这儿只是打印显示了下;

  下面是测试用例,

  运行环境:ARM V7开发板 232串口

  1. 开发板接出线转USB接入到电脑上,打开电脑调试软件,如下图所示,波特率、校验位设置好,打开串口就可以读取到数据了;  

  

   2. 设置串口调试软件发送数据,点击收到回答后发下一帧,即在接收到串口的数据就发一帧应答数据,发送区2,点击自动发,16进制发送;发送区1 2 3都是独立的,都可以发送,咱们用一个即可;

   

  3. 查看开发板上打印的日志,即接收到的数据,打印的Send是发送的数据,Revice就是接收到的数据,接收到数据之后,自己写一个ParseAlarmDataAck方法就可以处理数据啦,根据具体协议来操作;

   

 

   相对比较简单,QT5是有了自带的串口操作类,但貌似用QT4.8.6的更多呀...

标签:opt,cflag,QT,int,串口,SerialPort,Linux,include
来源: https://www.cnblogs.com/7haihai/p/14128363.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有