Linux-QT串口通信


  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方法就可以處理數據啦,根據具體協議來操作;

   

   

R: "2020.12.13 06:34:11.217 Send: 7e 31 31 30 31 32 41 34 34 30 30 30 30 46 44 41 32 0d "
R: "2020.12.13 06:34:11.239 Revice: 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 46 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 46 31 30 30 30 30 30 30 30 30 30 30 30 30 30 30 45 34 45 42 0a 7e 31 31 30 31 32 41 30 30 36 30 38 32 31 30 30 30 30 30 30 30 30 30 30 30 46 30 30 30 33 38 30 30 30 30 30 30 30 30 30 30 30 30 30 "

   相對比較簡單,QT5是有了自帶的串口操作類,但貌似用QT4.8.6的更多呀...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM