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