22.从零开始开发QT软件思路(单片机的串口通信)-- OpenCV从零开始到图像(人脸 + 物体)识别系列
本文作者:小嗷
微信公眾號:aoxiaoji
吹比QQ群:736854977
鏈接:https://mp.weixin.qq.com/s?__biz=MzU1MTgxNjQyMg==&tempkey=OTcyX0EwKzJvbDFaVXVLdUtOYThUdW9QU3ZDajJzNGxjM2tvbjFxdURMZ2dJNTc5SHM0UmRVLVV5LVBFN3dnWTc4eXpvakF2XzlGZmpPSnZJZ3Y5QXpfRXZZVjRUdVJPdFgzaVNac3Q5NlhkMW1IbXpsWG5iZjVhZGllc3luRWFZRy1HWUFTRWJWWnlYSlc0TDZMVUJsRlJDU1lQTTdKVF9SX2lGU2NiYnd%2Bfg%3D%3D&chksm=7b8adc4c4cfd555a1bd85304c823214b212222999801a631abfc0ddac7f85c92d316d88bec2f#rd
本文你會找到以下問題的答案:
今天,有個做測試的小伙伴,問我上位機(jī)什么寫?其實,我一開始想沒有,快滾。不過,還是用當(dāng)年的javaSwing寫的串口軟件,甩他臉?biāo)恪K懔?#xff0c;看看能不能QT利用搞一個串口軟件給他,不能就叫他上網(wǎng)自己查。
業(yè)務(wù)需求:
2.1 我需要怎么做(產(chǎn)品經(jīng)理的一般套路,看看別人產(chǎn)品):
如下圖:
2.2 串口查一查英文單詞是什么?
哦!seial port(其實,小嗷早就知道,英文是什么。只是為了配合那個測試小白)
2.3 打開QT軟件 -> 歡迎 -> 示例 -> 搜serial,如下
鼠標(biāo)移到其中一個小窗框上
英文意思:展示如何使用標(biāo)準(zhǔn)QSerialPort的API,在一個非圖形用戶界面思路上
從小窗口可以看出沒有現(xiàn)成的,都是邊邊角角。
2.4 先不急打開看看示例,轉(zhuǎn)移目標(biāo)查QSerialPort的API
網(wǎng)址如下:
http://doc.qt.io/qt-5/qserialport.html#BaudRate-enum
打開圖如下:
大概意思就是提供函數(shù)進(jìn)入串口,如上圖得出:
cpp需要導(dǎo)入:#include <QSerialPort>(Header) pro需要導(dǎo)入:QT += serialport(qmake)2.5 開始創(chuàng)建項目名QSerialPortTool,一路Next
創(chuàng)建成功后,在pro添加QT += serialport(qmake)
2.6 繼續(xù)看網(wǎng)址的API介紹,發(fā)現(xiàn)如下
公共類型:
- BaudRate:波特率(點擊BaudRate)
波特率是什么?介紹完API下方有介紹。
- DataBits:數(shù)據(jù)位(這個枚舉描述使用的數(shù)據(jù)位的數(shù)量)
- Direction:用法(這個枚舉描述了數(shù)據(jù)傳輸?shù)目赡芊较颉?#xff09;
小嗷簡單說說吧:只能允許輸入/只能允許輸出/同時允許輸入輸出(有點意思,小嗷不懂什么是同時輸入輸出。)
- FlowControl:這個枚舉描述了所使用的流控制。
大概分為軟硬件流控制,-1過時不建議使用。
同理,下方有流控制的解釋
- Parity:奇偶校正(這個枚舉描述了使用的奇偶校驗方案。)
同理下方。
- PinoutSignal:針輸出信號?(這個枚舉描述了可能的RS-232 pinout信號。)
這參數(shù)不太清楚是什么,RS-232指的是:我們臺式電腦的9Pin(9針的插頭,電子專業(yè)俗稱COM口),大概是定義COM口的那個針輸出信號(小嗷猜的)
- SerialPortError:串口錯誤信息(這個枚舉描述了串口::error屬性所包含的錯誤。)
- StopBits:停止位(這個枚舉描述了所使用的停止位的數(shù)量。)
用于表示單個包的最后一位。典型的值為1,1.5和2位。由于數(shù)據(jù)是在傳輸線上定時的,并且每一個設(shè)備有其自己的時鐘,很可能在通信中兩臺設(shè)備間出現(xiàn)了小小的不同步。因此停止位不僅僅是表示傳輸?shù)慕Y(jié)束,并且提供計算機(jī)校正時鐘同步的機(jī)會。適用于停止位的位數(shù)越多,不同時鐘同步的容忍程度越大,但是數(shù)據(jù)傳輸率同時也越慢。
- BaudRate:波特率(點擊BaudRate)
- DataBits:數(shù)據(jù)位(這個枚舉描述使用的數(shù)據(jù)位的數(shù)量)
- Direction:用法(這個枚舉描述了數(shù)據(jù)傳輸?shù)目赡芊较颉?#xff09;
- FlowControl:這個枚舉描述了所使用的流控制。
- Parity:奇偶校正(這個枚舉描述了使用的奇偶校驗方案。)
- PinoutSignal:針輸出信號?(這個枚舉描述了可能的RS-232 pinout信號。)
- SerialPortError:串口錯誤信息(這個枚舉描述了串口::error屬性所包含的錯誤。)
- StopBits:停止位(這個枚舉描述了所使用的停止位的數(shù)量。)
再看看我們對標(biāo)的產(chǎn)品圖:
這時,小嗷大概了解的自己要做什么。即:對標(biāo)產(chǎn)品的圖,除了設(shè)置接收的編碼方式,基本在QSerialPort類中,可以直接調(diào)用API函數(shù)(如:波特率等)進(jìn)行相關(guān)設(shè)置。
在往下就是功能函數(shù):其中,黃色部分有設(shè)置COM的序號什么,估計搞軟件的時候需要用它來設(shè)置COM序號(如:COM 1-256)。
再往下就是重載函數(shù)(重載是什么?)
重載,簡單說,就是函數(shù)或者方法有相同的名稱,但是參數(shù)列表不相同的情形,這樣的同名不同參數(shù)的函數(shù)或者方法之間,互相稱之為重載函數(shù)或者方法。
好了,該補得都補了,下面第4點就來看看如何實現(xiàn)從零開始實現(xiàn)串口代碼部分。
3.1 波特率
單片機(jī)或計算機(jī)在串口通信時的速率。指的是信號被調(diào)制以后在單位時間內(nèi)的變化,即單位時間內(nèi)載波參數(shù)變化的次數(shù),如:
每秒鐘傳送240個字符,而每個字符格式包含10位(1個起始位,1個停止位,8個數(shù)據(jù)位),這時的波特率為240Bd,
比特率為10位*240個/秒=2400bps 1Bps=8bps 1Mbps=128KBps 下載速度最高為128KBps每秒鐘傳送240個二進(jìn)制位,這時的波特率為240Bd,比特率也是240bps。
3.2 流控制
用途:數(shù)據(jù)在傳輸過程中容易出現(xiàn)數(shù)據(jù)丟失的現(xiàn)象。
例如:兩臺計算機(jī)通過串口傳輸數(shù)據(jù)時,或者臺式機(jī)與單片機(jī)之間進(jìn)行通信時,可能由于兩端計算機(jī)的處理速度不同,出現(xiàn)接收端的數(shù)據(jù)緩沖區(qū)已滿,而發(fā)送端依然繼續(xù)發(fā)送數(shù)據(jù),則導(dǎo)致數(shù)據(jù)丟失。
流控制的出現(xiàn)就是為了解決這種數(shù)據(jù)丟失的問題。
工作原理:當(dāng)接收端的數(shù)據(jù)緩沖區(qū)已滿,無法處理數(shù)據(jù)來時,就發(fā)出”不再接收”的信號,發(fā)送端則停止發(fā)送,直到發(fā)送端收到”可以繼續(xù)發(fā)送”的信號再發(fā)送數(shù)據(jù)。
計算機(jī)中常用的兩種流控制分別是硬件流控制(RTS/CTS、DTR/DSR等)和軟件流控制(XON/XOFF)。
硬件流控制 :硬件流控制必須將相應(yīng)的電纜線連上。
硬件流控制常用方式為:RTS/CTS(請求發(fā)送/清除發(fā)送)流控制和DTR/DSR(數(shù)據(jù)終端就緒/數(shù)據(jù)設(shè)置就緒)流控制。
當(dāng)用RTS/CTS流控制時,需將通訊兩端的RTS、CTS線對應(yīng)相連,數(shù)據(jù)終端設(shè)備(如計算機(jī))使用RTS來啟動調(diào)制解調(diào)器或其它數(shù)據(jù)通訊設(shè)備的數(shù)據(jù)流,而數(shù)據(jù)通訊設(shè)備(如調(diào)制解調(diào)器)則用CTS來啟動和暫停來自計算機(jī)的數(shù)據(jù)流。
這種硬件握手方式的過程為:通過程序為接收端緩沖區(qū)大小設(shè)置一個高位標(biāo)志(可為緩沖區(qū)大小的75%)和一個低位標(biāo)志(可為緩沖區(qū)大小的25%),當(dāng)緩沖區(qū)內(nèi)數(shù)據(jù)量達(dá)到高位時,接收端將CTS線置低電平(送邏輯0),當(dāng)發(fā)送端的程序檢測到CTS為低后,就停止發(fā)送數(shù)據(jù),直到接收端緩沖區(qū)的數(shù)據(jù)量低于低位而將CTS置高電平。RTS則用來標(biāo)明接收設(shè)備有沒有準(zhǔn)備好接收數(shù)據(jù)。
DTR/DSR流控制的工作方式與RTS/CTS流控制類似,不再進(jìn)行贅述。
簡單講講就是 RTS:標(biāo)明家屬有沒有準(zhǔn)備好錢; CTS:標(biāo)明綁匪接不接受錢; 有點意思,和服務(wù)器的排他鎖思想類似(學(xué)過服務(wù)器就知道)3.3 奇偶校驗位(Parity)
奇偶校驗位(Parity),在數(shù)據(jù)存儲和傳輸中,字節(jié)中額外增加一個比特位,用來檢驗錯誤。它常常是從兩個或更多的原始數(shù)據(jù)中產(chǎn)生一個冗余數(shù)據(jù),冗余數(shù)據(jù)可以從一個原始數(shù)據(jù)中進(jìn)行重建。不過,奇偶校驗數(shù)據(jù)并不是對原始數(shù)據(jù)的完全復(fù)制。被用在RAID的2、3、4、5級別中。
使用
由于它很簡單,所以奇偶校驗位用于許多計算機(jī)硬件中遇到麻煩時能夠重新操作或者通過簡單的錯誤檢測就能起到很大作用的場合。例如SCSI總線使用奇偶校驗位檢測傳輸錯誤,許多微處理器的指令高速緩存中也包括奇偶校驗位保護(hù)。因為指令緩存數(shù)據(jù)是主內(nèi)存數(shù)據(jù)的副本,所以在發(fā)現(xiàn)錯誤的時候能夠拋棄錯誤數(shù)據(jù)并且重新取回數(shù)據(jù)。
在串行數(shù)據(jù)通信中,常用的格式是 7 個數(shù)據(jù)位、1 個校驗位、1 到 2 個停止位。這種格式用方便的 8 位字節(jié)巧妙地適應(yīng)了所有的 7 位 ASCII 字符。也可以用其它的格式表示,8 位數(shù)據(jù)加上 1 個校驗位可以傳輸任意的 8 位字節(jié)數(shù)據(jù)。
在串行通信中,奇偶校驗位通常是由UART這樣的接口硬件生成、校驗的,在接收方,通過接口硬件中的寄存器的狀態(tài)位傳給 CPU 以及操作系統(tǒng)。錯誤數(shù)據(jù)的恢復(fù)通常是通過重新發(fā)送數(shù)據(jù),這個過程通常由如操作系統(tǒng)輸入輸出程序這樣的軟件處理的。
奇偶校驗塊(其實,可以不講。不過,個人覺得有點意思。涉及硬盤數(shù)據(jù)恢復(fù)原理)
一些冗余磁盤陣列(en:RAID)使用奇偶校驗塊實現(xiàn)冗余。如果陣列中的一塊磁盤出現(xiàn)故障,工作磁盤中的數(shù)據(jù)塊與奇偶校驗塊一起來重建丟失的數(shù)據(jù)。
下面每列表示一個磁盤,假設(shè) A1 = 00000111、A2 = 00000101 以及 A3 = 00000000。A1、A2、A3 異或得到的 Ap 等于 00000010。如果第二個磁盤出現(xiàn)故障,A2 將不能被訪問,但是可以通過 A1、A3 與 Ap 的異或進(jìn)行重建:
A1 XOR A3 XOR Ap = 00000101
冗余磁盤陣列
A1 A2 A3
Ap B1 B2
Bp C1 C2
C3 C4 Cp
注意:數(shù)據(jù)塊是格式 A#,奇偶校驗塊是 Ap。
其實,我們從零開始串口代碼,有2條路選:
兩者的區(qū)別在于:
網(wǎng)址如下:http://doc.qt.io/qt-5/qserialport.html#BaudRate-enum
4.1 點擊Qt Serial Port
4.2 閱讀英文翻譯
得到的信息如下:
使用qt中的串口通信的時候需要用到的兩個頭文件分別為:
#include <QtSerialPort/QSerialPort> #include <QtSerialPort/QSerialPortInfo>除了加上面兩個頭文件之外,還需要在工程文件.pro中加下面一行代碼(上面搞過):
QT += serialport我們一般都需要先定義一個全局的串口對象,記得在自己的頭文件中添加上 n :
QSerialPort *serial;4.3 查一查QSerialPortInfo
QSerialPortInfo英文意思大概是串口信息,小嗷也不清楚是什么,查一查如下:
網(wǎng)址:http://doc.qt.io/qt-5/qserialportinfo.html#details
QSerialPortInfo Class:
提供關(guān)于現(xiàn)有串口的信息。
使用靜態(tài)函數(shù)生成QSerialPortInfo對象列表。列表中的每個QSerialPortInfo對象都代表一個串行端口,并且可以查詢港口名稱、系統(tǒng)位置、描述和制造商。QSerialPortInfo類也可以用作QSerialPort類的setPort()方法的輸入?yún)?shù)。
4.4 點擊examples(例子)
網(wǎng)址:http://doc.qt.io/qt-5/qtserialport-index.html
如下信息:
網(wǎng)址:http://doc.qt.io/qt-5/qtserialport-examples.html
4.5 點擊Blocking Master Example
小嗷其實就是任意點一個例子看看。當(dāng)然,英文內(nèi)容,小嗷還是讀懂大概意思。
納里,就是第一個搜索圖的項目代碼的講解,很好很好,沒有注解的代碼,不是好代碼。小嗷一般都不寫注解,哈哈哈。小嗷估計其余項目例子都一樣,
4.6 點擊Terminal Example
小嗷再翻翻其他項目翻到Terminal Example,看看內(nèi)容就知道可以動手搞定。項目如下:
網(wǎng)址如下:http://doc.qt.io/qt-5/qtserialport-terminal-example.html
4.6.1 在MainWindow主界面創(chuàng)建new QSerialPort(this)對象;
4.6.2 鏈接控件的回調(diào)函數(shù)(信號與槽,打個比方就是:按鈕按一下,調(diào)用這個函數(shù)【槽】。當(dāng)然不怎么專業(yè),為了便于你們理解)
4.6.3 打開串口【設(shè)置什么波特率,幾號串口等,再連接串口】
4.6.4 關(guān)閉串口,讀串口數(shù)據(jù)和發(fā)送串口數(shù)據(jù)
基本上,擁有我們需要的功能:打開串口 -> 發(fā)送數(shù)據(jù)/接收數(shù)據(jù) -> 關(guān)閉串口(當(dāng)然,上面已經(jīng)介紹的錯誤識別。具體情況,在實現(xiàn)過程,看看。)
5.1 添加到.pro
QT += serialport5.2 寫界面(Label + Combo Box【左鍵雙擊控件】)
只寫比較關(guān)鍵操作
5.2.1 Combo Box【左鍵雙擊控件,添加值】,需要翻看上面的功能函數(shù)定義
相關(guān)控件命名:
5.3 頭文件mainwindow.h
代碼如下:
#ifndef MAINWINDOW_H #define MAINWINDOW_H //上圖忘了添加QtSerialPort和QSerialPortInfo兩個類 #include <QMainWindow> #include <QtSerialPort/QSerialPort> #include <QtSerialPort/QSerialPortInfo>namespace Ui { class MainWindow; }class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();//創(chuàng)建按鈕的回調(diào)函數(shù)(響應(yīng)事件)和接收數(shù)據(jù)的回調(diào)函數(shù) private slots:void on_OpenSerialButton_clicked();void ReadData();void on_SendButton_clicked();//定義一個QSerialPort的全局變量:serial private:Ui::MainWindow *ui;QSerialPort *serial; };#endif // MAINWINDOW_H5.4 解析源文件mainwindow.cpp
5.4.1 查找可用的串口
對標(biāo)別人的產(chǎn)品,打開軟件自動獲取當(dāng)前電腦的COM口的信息。
實現(xiàn)思路:
步驟:
第一個問題: 怎么獲取系統(tǒng)是否有用的串口?
小嗷記得之前有個QSerialPortInfo Class查一查,發(fā)現(xiàn)一個有用串口的東東,點擊進(jìn)入
網(wǎng)址: http://doc.qt.io/qt-5/qserialportinfo.html#availablePorts
英文翻譯:返回一個在系統(tǒng)上有用的串口列表
第二個問題:怎么讀取列表(list)中的內(nèi)容
本來小嗷向上網(wǎng)查查怎么讀取列表(list)中的內(nèi)容。這個自動獲取串口怎么這么眼熟?
打開QT提供的例子 -> 在“.cpp”中 ctrl + F 查找“availablePorts”關(guān)鍵字
C++11中引入的auto主要有兩種用途:自動類型推斷和返回值占位
const auto infos = QSerialPortInfo::availablePorts(); //for循環(huán)中的:符號是什么意思 //for(x:y)表示x屬于y,并且遍歷y中的所有元素 for (const QSerialPortInfo &info : infos)serialPortComboBox->addItem(info.portName());一套帶走,搞定,嗷嗷嗷!
實現(xiàn)代碼加強版如下(因為考慮到有的串口沒有關(guān)閉的狀態(tài)):
//查找可用的串口 const auto infos = QSerialPortInfo::availablePorts(); for (const QSerialPortInfo &info : infos) {QSerialPort serial;serial.setPort(info);//如果某個串口打開,讀取正常,統(tǒng)統(tǒng)關(guān)閉if(serial.open(QIODevice::ReadWrite)){ui->PortBox->addItem(info.portName());serial.close();} }設(shè)置選擇的項,如:當(dāng)我數(shù)據(jù)位下拉菜單選擇8時,設(shè)置Data8
//設(shè)置數(shù)據(jù)位數(shù)switch (ui->BitBox->currentIndex()){case 8:serial->setDataBits(QSerialPort::Data8);//設(shè)置數(shù)據(jù)位8break;default:break;}為啥小嗷不寫全?
瞄了一眼,波特率要寫8個,每個3行。大概從波特率到流控估計要寫多60-N行代碼。算了,直接寫死。
5.5 mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);//查找可用的串口const auto infos = QSerialPortInfo::availablePorts();for (const QSerialPortInfo &info : infos){QSerialPort serial;serial.setPort(info);//如果某個串口打開,讀取正常,統(tǒng)統(tǒng)關(guān)閉if(serial.open(QIODevice::ReadWrite)){ui->PortBox->addItem(info.portName());serial.close();}}//開機(jī)設(shè)置所有下拉菜單默認(rèn)顯示第3項(0為第一項,看項目需求)ui->BaudBox->setCurrentIndex(2);ui->BitBox->setCurrentIndex(2);ui->ParityBox->setCurrentIndex(2);ui->BitBox->setCurrentIndex(2);ui->FlowBox->setCurrentIndex(2);}MainWindow::~MainWindow() {delete ui; } void MainWindow::on_OpenSerialButton_clicked() {if(ui->OpenSerialButton->text() == tr("打開串口")){serial = new QSerialPort;//設(shè)置串口名serial->setPortName(ui->PortBox->currentText());//打開串口serial->open(QIODevice::ReadWrite);//設(shè)置波特率serial->setBaudRate(QSerialPort::Baud115200);//設(shè)置波特率為115200//設(shè)置數(shù)據(jù)位數(shù)switch (ui->BitBox->currentIndex()){case 8:serial->setDataBits(QSerialPort::Data8);//設(shè)置數(shù)據(jù)位8break;default:break;}//設(shè)置校驗位switch (ui->ParityBox->currentIndex()){case 0:serial->setParity(QSerialPort::NoParity);break;default:break;}//設(shè)置停止位switch (ui->BitBox->currentIndex()){case 1:serial->setStopBits(QSerialPort::OneStop);//停止位設(shè)置為1break;case 2:serial->setStopBits(QSerialPort::TwoStop);default:break;}//設(shè)置流控制serial->setFlowControl(QSerialPort::NoFlowControl);//設(shè)置為無流控制//關(guān)閉設(shè)置菜單使能ui->PortBox->setEnabled(false);ui->BaudBox->setEnabled(false);ui->BitBox->setEnabled(false);ui->ParityBox->setEnabled(false);ui->StopBox->setEnabled(false);ui->OpenSerialButton->setText(tr("關(guān)閉串口"));//連接信號槽QObject::connect(serial,&QSerialPort::readyRead,this,&MainWindow::ReadData);}else{//關(guān)閉串口serial->clear();serial->close();serial->deleteLater();//恢復(fù)設(shè)置使能ui->PortBox->setEnabled(true);ui->BaudBox->setEnabled(true);ui->BitBox->setEnabled(true);ui->ParityBox->setEnabled(true);ui->StopBox->setEnabled(true);ui->OpenSerialButton->setText(tr("打開串口"));}} //讀取接收到的信息 void MainWindow::ReadData() {QByteArray buf;buf = serial->readAll();if(!buf.isEmpty()){QString str = ui->textEdit->toPlainText();str+=tr(buf);ui->textEdit->clear();ui->textEdit->append(str);}buf.clear(); }//發(fā)送按鈕槽函數(shù) void MainWindow::on_SendButton_clicked() {//Latin1是ISO-8859-1的別名,有些環(huán)境下寫作Latin-1。ISO-8859-1編碼是單字節(jié)編碼,向下兼容ASCII//其編碼范圍是0x00-0xFF,0x00-0x7F之間完全和ASCII一致,0x80-0x9F之間是控制字符,0xA0-0xFF之間是文字符號。serial->write(ui->textEdit_2->toPlainText().toLatin1()); }5.6 效果圖如下:
總結(jié)
以上是生活随笔為你收集整理的22.从零开始开发QT软件思路(单片机的串口通信)-- OpenCV从零开始到图像(人脸 + 物体)识别系列的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用Citrix AppCenter 发
- 下一篇: C++相关英语单词