在Qt5之前,串口通信基本依赖于第三方库,下面是我曾接触过的串口通信类库:
名称 |
语言 |
平台 |
|
QextSerialPort |
QT C++ |
Win/Linux |
|
QSerialPort |
QT C++ |
QT5已经集成 |
|
libserial |
C++ |
Linux |
以上串口通信类库通信过程基本是一下步骤,即:打开串口 > 配置串口参数 > 收发数据。
注意,串口参数(波特率、数据位、停止位、奇偶校验、流控等)必须在串口打开之后进行配置才有效。
在使用串口接收数据时,会出现数据接收不完整的情况,这种情况很常见,尤其是在 QextSerialPort 和 QSerialPort 中,有一段时间我不得不怀疑是不是参数或者是硬件问题。后来发现这并不是异常,在通信中我依赖 readAll() 函数获取串口数据,但 readAll() 由 readyRead()信号触发,readyRead在串口读到起始标志时立即发送,并不一保证一定是当前所发数据的起始部分。因此串口通信双方通信前应制定好通信协议,规定好数据的起始于结尾标志,串口当读到完整的起始于结束标志之后,才认定读完一条完整的数据。
下面分别就这三种串口通信类库进行实例代码的编写。
QextSerialPort使用小例
QextSerialPort在Window平台下可用Win_QextSerialPort进行串口通信,下面对Win_QextSerialPort再一次封装的串口通信类SerialPort:
头文件:serialport.h
1 #ifndef SERIALPORT_H 2 #define SERIALPORT_H 3 4 #include <QObject> 5 6 7 8 /** 9 * @brief 串口参数 10 */ 11 struct SerialConfig{ 12 int baudRate; //波特率 13 int characterSize; //传输位 14 int stopBits; //停止位 15 int parity; //校验位 0无校验 1奇校验 2偶校验 16 //char*serialPort[20]; //设备文件名 17 int flowControl; //流控 18 long timeOut; //超时时间 19 }; 20 21 class Win_QextSerialPort; 22 23 /** 24 * @brief 串口通信类 25 */ 26 class SerialPort : public QObject 27 { 28 Q_OBJECT 29 public: 30 31 explicit SerialPort(QString portName, QObject *parent = 0); 32 //~SerialPort(); 33 34 /** 35 * @brief 串口数据大发送格式 36 */ 37 enum SerialDataType{ 38 ASCLL=0, //ascll方式 39 HEX //二进制方式 40 }; 41 /** 42 * @brief 配置串口参数 43 * @param config 44 */ 45 void setSerila(SerialConfig *config); 46 47 /** 48 * @brief 获取串口开启状态 49 * @return 50 */ 51 bool isOpend(); 52 53 /** 54 * @brief 设置数据发送方式 55 * @param type 56 */ 57 void setSerialDataType(SerialDataType type); 58 signals: 59 /** 60 * @brief 串口获取到数据 61 */ 62 void strSerial(QString); 63 public slots: 64 65 66 /** 67 * @brief 开启串口 68 * @param 串口参数 69 * @return 是否成功打开串口 70 */ 71 bool openSerial(SerialConfig *config); 72 73 /** 74 * @brief 关闭串口 75 */ 76 void closeSerial(); 77 78 /** 79 * @brief 写入数据到串口,系统默认是ascll方式 80 * @param str 待写入数据 81 */ 82 void writeSerial(QString str); 83 private slots: 84 /** 85 * @brief 读取数据 86 */ 87 void readSerial(); 88 private: 89 /** 90 * @brief 基本串口通信类 91 */ 92 Win_QextSerialPort*serial; 93 94 /** 95 * @brief 数据发送形式 96 */ 97 SerialDataType serialDataType; 98 99 100 }; 101 102 #endif // SERIALPORT_H
源文件:serialport.cpp
1 #include "serialport.h" 2 #include "serial_inc/win_qextserialport.h" 3 4 #include <QDebug> 5 6 SerialPort::SerialPort(QString portName,QObject *parent):QObject(parent),serialDataType(HEX) 7 { 8 9 this->serial=new Win_QextSerialPort(portName, QextSerialBase::EventDriven); 10 connect(serial,SIGNAL(readyRead()),this,SLOT(readSerial())); 11 12 } 13 14 void SerialPort::setSerila(SerialConfig *config) 15 { 16 17 //波特率 18 BaudRateType baudRate; 19 20 switch (config->baudRate) { 21 case 9600: 22 baudRate=BAUD9600; 23 break; 24 case 115200: 25 baudRate=BAUD115200; 26 break; 27 default: 28 baudRate=BAUD4800; 29 break; 30 } 31 32 //数据位 33 DataBitsType dataBits; 34 switch (config->characterSize) { 35 case 7: 36 dataBits=DATA_7; 37 break; 38 default: 39 dataBits=DATA_8; 40 break; 41 } 42 43 //停止位 44 StopBitsType stopBits; 45 switch (config->stopBits) { 46 case 1: 47 stopBits=STOP_1; 48 break; 49 case 2: 50 stopBits=STOP_2; 51 break; 52 default: 53 stopBits=STOP_1_5; 54 break; 55 } 56 57 //奇偶校验 58 ParityType parity; 59 switch (config->parity) { 60 case 0: 61 parity=PAR_NONE; 62 break; 63 case 1: 64 parity=PAR_ODD; 65 break; 66 default: 67 parity=PAR_EVEN; 68 break; 69 } 70 71 //数据流控 72 FlowType flow=FLOW_OFF; 73 74 serial->setBaudRate(baudRate); 75 serial->setDataBits(dataBits); 76 serial->setStopBits(stopBits); 77 serial->setParity(parity); 78 serial->setFlowControl(flow); 79 80 serial->setDtr(false); 81 serial->setRts(false); 82 83 84 } 85 86 bool SerialPort::isOpend() 87 { 88 return this->serial!=NULL?serial->isOpen():false; 89 } 90 91 void SerialPort::setSerialDataType(SerialPort::SerialDataType type) 92 { 93 this->serialDataType=type; 94 } 95 96 97 void SerialPort::writeSerial(QString str) 98 { 99 100 if(this->serialDataType==this->ASCLL){ 101 serial->write(str.toLocal8Bit()); 102 }else{ 103 104 QByteArray b; 105 b=QByteArray::fromHex(str.toUtf8()); 106 serial->write(b); 107 } 108 109 } 110 111 void SerialPort::readSerial() 112 { 113 114 qDebug()<<serial->bytesAvailable(); 115 116 QString str=""; 117 118 QByteArray array=serial->readAll(); 119 120 if(this->serialDataType==SerialPort::ASCLL){ 121 str=QString::fromLocal8Bit(array.data()); 122 }else{ 123 array=array.toHex(); 124 str=QString::fromUtf8(array.data()); 125 } 126 127 str=str.trimmed(); 128 129 qDebug()<<"serial get:"<<str; 130 131 emit this->strSerial(str); 132 133 134 } 135 136 137 /**打开 138 * @brief SerialPort::openSerial 139 * @return 140 */ 141 bool SerialPort::openSerial(SerialConfig*config) 142 { 143 144 bool isOpen=serial->open(QIODevice::ReadWrite); 145 146 qDebug()<<"isOpen:"<<isOpen; 147 //如果已经成功打开串口,则经行串口参数配置 148 //这里有一个概念:串口配置必须在串口设备成功打开后才有效! 149 if(isOpen){ 150 this->setSerila(config); 151 } 152 153 return isOpen; 154 } 155 156 void SerialPort::closeSerial() 157 { 158 serial->close(); 159 160 }
QSerialPort 使用小例
QSerialPort通信方式与 Win_QextSerialPort方式类似。
串口初始化代码片段:
1 QSerialPort serial=new QSerialPort(this); 2 3 connect(serial, SIGNAL(readyRead()), this, SLOT(readData())); 4 5 serial->setPortName("COM4"); 6 serial->setBaudRate(9600); 7 serial->setDataBits(QSerialPort::Data8); 8 serial->setParity(QSerialPort::NoParity); 9 serial->setStopBits(QSerialPort::OneStop); 10 serial->setFlowControl(QSerialPort::NoFlowControl); 11 if (serial->open(QIODevice::ReadWrite)) { 12 13 qDebug()<<"yes"; 14 15 } else { 16 17 qDebug()<<"no"; 18 }
数据接收函数:
1 void QPort::readData() 2 { 3 QByteArray data = serial->readAll(); 4 qDebug()<<data; 5 6 }
libserial使用小例
libserial只使用于Linux平台,下载源码后,进行编译和安装。下面是封装的串口通信类 Serial:
头文件:serial.h
1 #ifndef SERIAL_H 2 #define SERIAL_H 3 4 #include "common.h" 5 #include "SerialStream.h" 6 #include "SerialStreamBuf.h" 7 #include <QObject> 8 9 using namespace std; 10 using namespace LibSerial; 11 12 13 typedef struct 14 { 15 int baudRate; //!<波特率 16 int characterSize; //!<传输位 17 int stopBits; //!<停止位 18 int parity; //!校验位 0无校验 1奇校验 2偶校验 19 char*serialPort; //!设备文件名 20 21 }SerialConfig; 22 23 24 class Serial : public QObject 25 { 26 Q_OBJECT 27 public: 28 explicit Serial(QObject *parent = 0); 29 30 signals: 31 32 void message(QString msg); 33 public slots: 34 //打开串口 35 bool open(SerialConfig*config); 36 void work(); 37 void close(); 38 void sendMesg(QString msg); 39 private slots: 40 41 private: 42 /** 43 * @brief 初始化串口数据 44 * @param config 45 */ 46 void initSerialStream(SerialConfig*config); 47 48 /** 49 * @brief 串口字节流 50 */ 51 SerialStream serialStream; 52 53 bool isRuning; 54 }; 55 56 #endif // SERIAL_H
源文件:serial.cpp
1 #include "serial.h" 2 3 //数据开始标示 4 #define SERIAL_DATA_START 2 5 6 //数据结束标示 7 #define SERIAL_DATA_END 3 8 9 Serial::Serial(QObject *parent) : 10 QObject(parent) 11 { 12 } 13 14 bool Serial::open(SerialConfig *config){ 15 16 17 serialStream.Open(config->serialPort); 18 19 if(!serialStream.IsOpen()) 20 { 21 qDebug()<<"cannot open serial "<<config->serialPort; 22 return false; 23 }else{ 24 25 qDebug()<<"success open serial "<<config->serialPort; 26 } 27 28 initSerialStream(config); 29 30 return true; 31 32 } 33 34 35 void Serial::work(){ 36 37 //缓存数据 38 char *msg = new char[255]; 39 int index = 0; 40 char next_byte ; //串口接收到的下一个字节 41 this->isRuning=true; 42 forever{ 43 // 44 if(this->isRuning==false){ 45 break; 46 } 47 48 serialStream.get( next_byte ); 49 50 int char_num=next_byte+0; 51 52 if(char_num<30){ 53 qDebug()<<":"<<next_byte+0; 54 }else{ 55 qDebug()<<next_byte; 56 } 57 58 if(char_num!=SERIAL_DATA_END) 59 { 60 if(char_num==SERIAL_DATA_START){ 61 index=0; 62 continue; 63 } 64 65 msg[index] = next_byte; 66 index++; 67 } 68 else 69 { 70 msg[index] = '