整理出來自己寫的過程中較為純淨版的序列槽助手,給大家交流學習:http://download.csdn.net/detail/mao19931004/9737142
最近教研室在做高速相機,通過圖像采集卡和自己做的高速相機連結,進而采集和收集圖像。圖像采集卡和高速相機是通過Cameralink連接配接,其中也包含了相機和圖像采集卡之間的序列槽的硬體借口,在老師的要求下,實作了兩種序列槽程式,一種是通過cameralink的API實作序列槽資料的傳輸,一種則是通過USB轉RS422的轉接器,實作對高速相機的寄存器的讀寫。本文主要介紹了通過Qt的QSerialPort和QSerialPortInfo兩個類,實作的序列槽程式。
本文采用的的開發環境是VS2010+Qt5.5.1版本,所有程式不是通過Qt Creator編譯的,如果有需要可以介紹VS2010和Qt環境的搭建和簡單的使用。
- QSerialPort
QSerialPort這個類是從QT5.1開始引入的,之前都是通過QIODevice自己定義序列槽類,進而實作序列槽程式的開發。現在引入這個類了,将會非常友善的開發序列槽程式。為了使用這個類,需要在工程目錄和附加依賴項中加入include的路徑,以及連結庫的路徑,以及連結庫的名稱:
- 項目--->屬性---> C++ ---> 正常 --->C:\Qt\Qt5.5.1\5.5\msvc2010\include\QtSerialPort
- 項目--->屬性---> 輸入++ ---> 附加依賴項--->Qt5SerialPort.lib(如果是Debug版本,則是Qt5SerialPortd.lib)版本
- 源檔案或者頭檔案需要加入#include<QSerialPort>
序列槽的資訊可以通過QSerialPortInfo類獲得,通過這個類,你可以正确的确定你要開啟的序列槽,同時可以獲得序列槽的描述性資訊以及廠家資訊。序列槽有三種打開模式,即ReadOnly,WriteOnly,以及ReadWrite。同時可以設定其停止位,波特率,資料位,校驗方式以及流控,對應的函數方式分别為:setStopBits(),setBaudRates(),setDataBits(),setParity(),以及setFlowControl()。
序列槽資料的寫入是通過writeData(const char * data, qint64 maxSize)以及write(const char * data, qint64 maxSize),前者是protected屬性,隻能在子類中通路,而後者則是public屬性。在序列槽打開并且具有可寫屬性,即可通過write函數寫入資料。
序列槽資料的讀入是通過readData(char * data, qint64 maxSize) ,read(qint64 maxSize)實作的,如果需要一次性讀完所有的資料,則可以通過readAll()全部讀取序列槽緩沖區中的資料。
序列槽内部的緩沖區大小可以通過:setReadBufferSize(qint64 size)實作。當設定緩沖區大小時,序列槽隻能接收size大小的資料流,是以存在資料丢書的可能。當設定為0的時候,并不是指的緩沖區大小為0,而是無窮大,這樣就可以儲存資料的全部接收完整。這是緩沖區大小的預設屬性。
是否有新的資料讀入緩沖區是通過readReady()信号來來确定的。這是通過時間驅動的方式來确定是否有資料可以讀入。此外還有waitForReadyRead()來等待輪詢是否有資料到達序列槽,但是這是一種阻塞性讀入,個人覺得不是太好,是以寫序列槽的時候采用的是事件驅動的方式來确定是夠有資料可以讀入。
- QSerialPortInfo
可能會用得比較多的函數是description(),manufacturer(),以及serialNumber()。進而得到描述性資訊,比如通信端口。USB轉序列槽等描述序列槽的資訊、序列槽的
生産商資訊以及序列槽名,在電腦上表現為COM~等。
- 如何擷取電腦上所有的序列槽
void CameraSerialPort::getSerialPortID()
{
serialInfo=new QSerialPortInfo();
serialList=serialInfo->availablePorts();
int nSerialnum=serialList.length();
for(int i=0;i<nSerialnum;i++)
{
QString serialName=serialList[i].portName();
QString serialDesp=serialList[i].description();
serialPortAssitant.serialPortcomboBox->addItem(serialName);
}
QString currentPort=serialPortAssitant.serialPortcomboBox->currentText();
portToOpen=currentPort;
QString portStatus=currentPort+" closed";
serialPortAssitant.status->statusInfo->setText(portStatus.toUpper());
QPalette font_palette;
font_palette.setColor(QPalette::WindowText,Qt::red);
serialPortAssitant.status->statusInfo->setPalette(font_palette);
}
因為直接從自己的項目檔案拷過來的源碼,這裡稍微介紹一下屬性:
1、變量的定義,在頭檔案中,這裡沒有貼出來,截取定義如下:
QSerialPortInfo* serialInfo;
QList<QSerialPortInfo>serialList;
2、QList<QSerialPortInfo>availablePorts() 傳回的是一個關于QSerialPortInfo的清單,在資料結構QList中存儲。
3、serialPortcomBox是一個QComboBox,下拉清單。
4、最後幾行是用來顯示序列槽的狀态資訊,達到的效果如圖:
- 打開序列槽并且通過序列槽寫資料
得到序列槽資訊後,就可以選擇端口,進行打開和讀寫資料。貼出代碼,然後在給分析分析:
void CameraSerialPort::Write()
{
QString sendMsg=serialPortAssitant.sendLine->text();
QByteArray temp=sendMsg.toLatin1();
if(IsSendByHex)
{
temp=HexStrToByteArray(sendMsg);
}
char *sendContent=temp.data();
qint64 sendedNum=serialPort->write(sendContent,temp.count());
//---------------判斷發送資料是否成功----------------------//
if(sendedNum==-1)
{
errorValue=serialPort->error();
if(IsShowCurrentTime)
{
errorInfo=" ";
currentTime=QDateTime::currentDateTime();
timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
errorInfo=QString::fromLocal8Bit("錯誤提示資訊 ");
errorInfo+=timeinfo;
errorInfo+="\n";
}
else
{
errorInfo=" ";
errorInfo=QString::fromLocal8Bit("錯誤提示資訊 ");
errorInfo+="\n";
}
serialPortAssitant.ReciveWidget->append(errorInfo+getValueContent(errorValue));
return;
}
//-------------顯示發送資料-----------------------//
//temp的size的依據是是否以16進制發送
sendCount+=temp.count();
serialPortAssitant.status->TxByte->setText(QString::number(sendCount));
QString showSendMsg;
if(IsShowSendMsg)
{
if(IsShowCurrentTime)
{
currentTime=QDateTime::currentDateTime();
timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
showSendMsg+=QString::fromLocal8Bit("發送資料 : ");
showSendMsg+=timeinfo;
showSendMsg+="\n";
//判斷顯示16進制還是ACSII字元
if(IsSendByHex)
showSendMsg+=ByteArrayToHexStr(temp);
else
showSendMsg+=temp;
}
else
{
showSendMsg=QString::fromLocal8Bit("發送資料 : ");
if(IsSendByHex)
showSendMsg+=ByteArrayToHexStr(temp);
else
showSendMsg+=temp;
}
serialPortAssitant.ReciveWidget->append(showSendMsg);
}
IsWrittenSuccessed=true;
}
void CameraSerialPort::sendData()
{
if(!IsSerialPortOpen)
{
if(serialPort!=NULL)
{
serialPort->close();
}
serialPort=new QSerialPort(portToOpen);
if(serialPort==NULL)
{
errorValue=serialPort->error();
QString errorContent=getValueContent(errorValue);
if(IsShowCurrentTime)
{
errorInfo=" ";
currentTime=QDateTime::currentDateTime();
timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
errorInfo=QString::fromLocal8Bit("錯誤提示資訊 ");
errorInfo+=timeinfo;
errorInfo+="\n";
}
else
{
errorInfo=" ";
errorInfo=QString::fromLocal8Bit("錯誤提示資訊 ");
errorInfo+="\n";
}
serialPortAssitant.ReciveWidget->append(errorInfo +errorContent+QString::fromLocal8Bit(", 請重新選擇正确的端口\n"));
return;
}
if(!serialPort->open(QIODevice::ReadWrite))
{
errorValue=serialPort->error();
QString errorContent=getValueContent(errorValue);
if(IsShowCurrentTime)
{
errorInfo=" ";
currentTime=QDateTime::currentDateTime();
timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
errorInfo=QString::fromLocal8Bit("錯誤提示資訊 ");
errorInfo+=timeinfo;
errorInfo+="\n";
}
else
{
errorInfo=" ";
errorInfo=QString::fromLocal8Bit("錯誤提示資訊 ");
errorInfo+="\n";
}
serialPortAssitant.ReciveWidget->append(errorInfo +errorContent);
return;
}
<span style="background-color: rgb(255, 0, 0);">connect(serialPort,SIGNAL(readyRead()),this,SLOT(onReadyRead()));</span>
serialPort->setDataBits(QSerialPort::Data8);
serialPort->setStopBits(QSerialPort::OneStop);
serialPort->setParity(QSerialPort::NoParity);
serialPort->setFlowControl(QSerialPort::NoFlowControl);
QString serialStatusInfo;
serialStatusInfo=serialPortAssitant.serialPortcomboBox->currentText().toUpper();
serialStatusInfo+=" OPENED";
serialStatusInfo+=" , ";
serialStatusInfo+=QString::number(serialPort->baudRate());
serialStatusInfo+=" , ";
serialStatusInfo+=serialPortAssitant.DataWidthcomboBox->currentText();
serialStatusInfo+=" , ";
serialStatusInfo+=serialPortAssitant.ParityWidthcomboBox->currentText().toUpper();
serialStatusInfo+=" , ";
serialStatusInfo+=serialPortAssitant.StopWidthcomboBox->currentText();
serialStatusInfo+=" , ";
serialStatusInfo+=serialPortAssitant.FLowControlcomboBox->currentText().toUpper();
QPalette font_palette;
font_palette.setColor(QPalette::WindowText,Qt::darkCyan);
serialPortAssitant.status->statusInfo->setText(serialStatusInfo);
serialPortAssitant.status->statusInfo->setPalette(font_palette);
serialPortAssitant.sendBtn->setText(QString::fromLocal8Bit("發送"));
IsSerialPortOpen=true;
}
else
{
if(IsRepeatSend)
{
repeatSend->start();
}
Write();
}
}
首先看write函數:以下是從write函數中抽離出來的幾行關鍵代碼:
<span style="white-space:pre"> </span>QString sendMsg=serialPortAssitant.sendLine->text();
<span style="white-space:pre"> </span>QByteArray temp=sendMsg.toLatin1();
char *sendContent=temp.data();
qint64 sendedNum=serialPort->write(sendContent,temp.count());
1、第一行是從QLineEdit擷取需要發送的資料資訊;
2、第二行到第三行代碼是需要把QString轉換為char *的資料類型。
3、第四行則是通過QIODevice類的成員函數write寫出資料。
4、剩餘的部分是一些細節的錯誤提示顯示以及顯示資訊,以及非常重要的是以ASCII形式發出資料還是以16進制發送資料。
接着是sendData()函數。
serialPort=new QSerialPort(portToOpen);
if(!serialPort->open(QIODevice::ReadWrite))
connect(serialPort,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
1、第一行是通過portToOpen執行個體化QSerialPort,構造函數為:QSerialPort(const QString & name, QObject * parent = Q_NULLPTR)
2、第二行是打開序列槽,打開模式是ReadWrite,可寫可讀
3、第三行是通過readRead()信号來實作序列槽資料的讀出,事件驅動的方式。這一行我在源代碼中加了紅色,原因是,一定要在打開序列槽後,實作readyRead()信号和對應的槽函數的連接配接,如果在沒有初始化序列槽成功後,然後信号才能啟動。我最開始在CameraSerialPort這個類的初始化中就定義了這個信号槽的連結,一直沒有讀到序列槽資料,結果在網上找了半天的原因,也沒有找到這個問題。想了一下,然後把代碼移到了這裡,就可以了。
打開序列槽和發送資料的結果如圖:
- 序列槽接收資料
void CameraSerialPort::Read()
{
if(serialPort->bytesAvailable()<0)
{
serialPortAssitant.ReciveWidget->setText("No data");
return;
}
QByteArray temp;
temp=serialPort->readAll();
QString receiveMsg;
if(IsReceiveByHex)
receiveMsg=ByteArrayToHexStr(temp);
else
receiveMsg=temp;
if(receiveMsg.isEmpty())
{
if(IsShowCurrentTime)
{
errorInfo=" ";
currentTime=QDateTime::currentDateTime();
timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
errorInfo=QString::fromLocal8Bit("錯誤提示資訊 ");
errorInfo+=timeinfo;
errorInfo+="\n";
}
else
{
errorInfo=" ";
errorInfo=QString::fromLocal8Bit("錯誤提示資訊 ");
errorInfo+="\n";
}
serialPortAssitant.ReciveWidget->append(errorInfo +QString::fromLocal8Bit("沒有讀到資料\n"));
return;
}
//接收到的位元組數以初始的bytes為依據
reciveCount+=temp.count();
serialPortAssitant.status->RxByte->setText(QString::number(reciveCount));
QString showReciveMsg;
if(IsShowCurrentTime)
{
currentTime=QDateTime::currentDateTime();
timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
showReciveMsg+=QString::fromLocal8Bit("接收資料 : ");
showReciveMsg+=timeinfo;
showReciveMsg+="\n";
showReciveMsg+=receiveMsg;
}
else
{
showReciveMsg=QString::fromLocal8Bit("接收資料 : ");
showReciveMsg+=receiveMsg;
}
serialPortAssitant.ReciveWidget->append(showReciveMsg);
}
void CameraSerialPort::onReadyRead()
{
Read();
}
1、讀入資料之前,需要判斷緩沖區是否有資料,有資料才去讀資料
2、如果有資料,則全部讀出緩沖區資料
- 總結
大緻的有關Qt的序列槽類就介紹完了,主要是QSerialPort以及QSerialPortInfo兩個類的使用,當然也需要了解QIODevice。