I.MX6U开发板上的Qt串口编程

真正串口通讯所需的功能是比较多的,此处的上位机只实现发送和接收功能。因为Linux、windows和开发板上均有串口,所以所开发的上位机在其上都是可以运行的。

文章目录

  • [1. 资源简介](#1. 资源简介)
  • [2. 应用实例](#2. 应用实例)
    • [2.1 项目简介:](#2.1 项目简介:)
    • [2.2 03_serialport.pro](#2.2 03_serialport.pro)
    • [2.3 mainwindow.h](#2.3 mainwindow.h)
    • [2.4 mainwindow.cpp](#2.4 mainwindow.cpp)
  • [3. 程序运行效果](#3. 程序运行效果)
  • [4. 串口测试](#4. 串口测试)

1. 资源简介

在正点原子的 I.MX6U 开发板的出厂系统里,默认已经配置了两路串口可用。一路是调试串口UART1(对应系统里的节点/dev/ttymxc0),另一路是UART3(对应系统里的节点/dev/ttymxc2)。由于 UART1 已经作为调试串口被使用。所以我们只能对 UART3 编程,(如需要使用多路串口,请自行设计底板与系统)。

2. 应用实例

2.1 项目简介:

Qt 串口的使用示例,应用到正点原子 I.MX6U 开发板上。

2.2 03_serialport.pro

在 03_serialport.pro 里, 我们需要使用串口,需要在 pro 项目文件中添加串口模块的支持,如下。

cpp 复制代码
1 # 添加串口模块支持
2 QT += core gui serialport
3 4
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
5 6
CONFIG += c++11
7 8
# The following define makes your compiler emit warnings if you use
9 # any Qt feature that has been marked deprecated (the exact warnings
10 # depend on your compiler). Please consult the documentation of the
11 # deprecated API in order to know how to port your code away from it.
12 DEFINES += QT_DEPRECATED_WARNINGS
13
14 # You can also make your code fail to compile if it uses deprecated APIs.
15 # In order to do so, uncomment the following line.
16 # You can also select to disable deprecated APIs only up to a certain
version of Qt.
17 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the
APIs deprecated before Qt 6.0.0
18
19 SOURCES += \
20 main.cpp \
21 mainwindow.cpp
22
23 HEADERS += \
24 mainwindow.h
25
26 # Default rules for deployment.
27 qnx: target.path = /tmp/$${TARGET}/bin
28 else: unix:!android: target.path = /opt/$${TARGET}/bin
29 !isEmpty(target.path): INSTALLS += target

第 2 行,添加的 serialport 就是串口模块的支持。

2.3 mainwindow.h

在头文件"mainwindow.h"的代码如下。

cpp 复制代码
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3 4
#include <QMainWindow>
5 #include <QSerialPort>
6 #include <QSerialPortInfo>
7 #include <QPushButton>
8 #include <QTextBrowser>
9 #include <QTextEdit>
10 #include <QVBoxLayout>
11 #include <QLabel>
12 #include <QComboBox>
13 #include <QGridLayout>
14 #include <QMessageBox>
15 #include <QDebug>
16
17 class MainWindow : public QMainWindow
18 {
19 Q_OBJECT
20
21 public:
22 MainWindow(QWidget *parent = nullptr);
23 ~MainWindow();
24
25 private:
26 /* 串口对象 */
27 QSerialPort *serialPort;
28
29 /* 用作接收数据 */
30 QTextBrowser *textBrowser;
31
32 /* 用作发送数据 */
33 QTextEdit *textEdit;
34
35 /* 按钮 */
36 QPushButton *pushButton[2];
37
38 /* 下拉选择盒子 */
39 QComboBox *comboBox[5];
40
41 /* 标签 */
42 QLabel *label[5];
43
44 /* 垂直布局 */
45 QVBoxLayout *vboxLayout;
46
47 /* 网络布局 */
48 QGridLayout *gridLayout;
49
50 /* 主布局 */
51 QWidget *mainWidget;
52
53 /* 设置功能区域 */
54 QWidget *funcWidget;
55
56 /* 布局初始化 */
57 void layoutInit();
58
59 /* 扫描系统可用串口 */
60 void scanSerialPort();
61
62 /* 波特率项初始化 */
63 void baudRateItemInit();
64
65 /* 数据位项初始化 */
66 void dataBitsItemInit();
67
68 /* 检验位项初始化 */
69 void parityItemInit();
70
71 /* 停止位项初始化 */
72 void stopBitsItemInit();
73
74 private slots:
75 void sendPushButtonClicked();
76 void openSerialPortPushButtonClicked();
77 void serialPortReadyRead();
78 };
79 #endif // MAINWINDOW_H

上面代码是在 mianwindow.h 里声明需要用到的变量,方法及槽函数。

2.4 mainwindow.cpp

mainwindow.cpp 的代码如下。

cpp 复制代码
1 #include "mainwindow.h"
2 #include <QDebug>
3 #include <QGuiApplication>
4 #include <QScreen>
5 #include <QRect>
6 7
MainWindow::MainWindow(QWidget *parent)
8 : QMainWindow(parent)
9 {
10 /* 布局初始化 */
11 layoutInit();
12
13 /* 扫描系统的串口 */
14 scanSerialPort();
15
16 /* 波特率项初始化 */
17 baudRateItemInit();
18
19 /* 数据位项初始化 */
20 dataBitsItemInit();
21
22 /* 检验位项初始化 */
23 parityItemInit();
24
25 /* 停止位项初始化 */
26 stopBitsItemInit();
27 }
28
29 void MainWindow::layoutInit()
30 {
31 /* 获取屏幕的分辨率, Qt 官方建议使用这
32 * 种方法获取屏幕分辨率,防上多屏设备导致对应不上
33 * 注意,这是获取整个桌面系统的分辨率
34 */
35 QList <QScreen *> list_screen = QGuiApplication::screens();
36
37 /* 如果是 ARM 平台,直接设置大小为屏幕的大小 */
38 #if __arm__
39 /* 重设大小 */
40 this->resize(list_screen.at(0)->geometry().width(),
41 list_screen.at(0)->geometry().height());
42 #else
43 /* 否则则设置主窗体大小为 800x480 */
44 this->resize(800, 480);
45 #endif
46 /* 初始化 */
47 serialPort = new QSerialPort(this);
48 textBrowser = new QTextBrowser();
49 textEdit = new QTextEdit();
50 vboxLayout = new QVBoxLayout();
51 funcWidget = new QWidget();
52 mainWidget = new QWidget();
53 gridLayout = new QGridLayout();
54
55 /* QList 链表,字符串类型 */
56 QList <QString> list1;
57 list1<<"串口号:"<<"波特率:"<<"数据位:"<<"检验位:"<<"停止位:";
58
59 for (int i = 0; i < 5; i++) {
60 label[i] = new QLabel(list1[i]);
61 /* 设置最小宽度与高度 */
62 label[i]->setMinimumSize(80, 30);
63 /* 自动调整 label 的大小 */
64 label[i]->setSizePolicy(
65 QSizePolicy::Expanding,
66 QSizePolicy::Expanding
67 );
68 /* 将 label[i]添加至网格的坐标(0, i) */
69 gridLayout->addWidget(label[i], 0, i);
70 }
71
72 for (int i = 0; i < 5; i++) {
73 comboBox[i] = new QComboBox();
74 comboBox[i]->setMinimumSize(80, 30);
75 /* 自动调整 label 的大小 */
76 comboBox[i]->setSizePolicy(
77 QSizePolicy::Expanding,
78 QSizePolicy::Expanding
79 );
80 /* 将 comboBox[i]添加至网格的坐标(1, i) */
81 gridLayout->addWidget(comboBox[i], 1, i);
82 }
83
84 /* QList 链表,字符串类型 */
85 QList <QString> list2;
86 list2<<"发送"<<"打开串口";
87
88 for (int i = 0; i < 2; i++) {
89 pushButton[i] = new QPushButton(list2[i]);
90 pushButton[i]->setMinimumSize(80, 30);
91 /* 自动调整 label 的大小 */
92 pushButton[i]->setSizePolicy(
93 QSizePolicy::Expanding,
94 QSizePolicy::Expanding
95 );
96 /* 将 pushButton[0]添加至网格的坐标(i, 5) */
97 gridLayout->addWidget(pushButton[i], i, 5);
98 }
99 pushButton[0]->setEnabled(false);
100
101 /* 布局 */
102 vboxLayout->addWidget(textBrowser);
103 vboxLayout->addWidget(textEdit);
104 funcWidget->setLayout(gridLayout);
105 vboxLayout->addWidget(funcWidget);
106 mainWidget->setLayout(vboxLayout);
107 this->setCentralWidget(mainWidget);
108
109 /* 占位文本 */
110 textBrowser->setPlaceholderText("接收到的消息");
111 textEdit->setText("www.openedv.com");
112
113 /* 信号槽连接 */
114 connect(pushButton[0], SIGNAL(clicked()),
115 this, SLOT(sendPushButtonClicked()));
116 connect(pushButton[1], SIGNAL(clicked()),
117 this, SLOT(openSerialPortPushButtonClicked()));
118
119 connect(serialPort, SIGNAL(readyRead()),
120 this, SLOT(serialPortReadyRead()));
121 }
122
123 void MainWindow::scanSerialPort()
124 {
125 /* 查找可用串口 */
126 foreach (const QSerialPortInfo &info,
127 QSerialPortInfo::availablePorts()) {
128 comboBox[0]->addItem(info.portName());
129 }
130 }
131
132 void MainWindow::baudRateItemInit()
133 {
134 /* QList 链表,字符串类型 */
135 QList <QString> list;
136 list<<"1200"<<"2400"<<"4800"<<"9600"
137 <<"19200"<<"38400"<<"57600"
138 <<"115200"<<"230400"<<"460800"
139 <<"921600";
140 for (int i = 0; i < 11; i++) {
141 comboBox[1]->addItem(list[i]);
142 }
143 comboBox[1]->setCurrentIndex(7);
144 }
145
146 void MainWindow::dataBitsItemInit()
147 {
148 /* QList 链表,字符串类型 */
149 QList <QString> list;
150 list<<"5"<<"6"<<"7"<<"8";
151 for (int i = 0; i < 4; i++) {
152 comboBox[2]->addItem(list[i]);
153 }
154 comboBox[2]->setCurrentIndex(3);
155 }
156
157 void MainWindow::parityItemInit()
158 {
159 /* QList 链表,字符串类型 */
160 QList <QString> list;
161 list<<"None"<<"Even"<<"Odd"<<"Space"<<"Mark";
162 for (int i = 0; i < 5; i++) {
163 comboBox[3]->addItem(list[i]);
164 }
165 comboBox[3]->setCurrentIndex(0);
166 }
167
168 void MainWindow::stopBitsItemInit()
169 {
170 /* QList 链表,字符串类型 */
171 QList <QString> list;
172 list<<"1"<<"2";
173 for (int i = 0; i < 2; i++) {
174 comboBox[4]->addItem(list[i]);
175 }
176 comboBox[4]->setCurrentIndex(0);
177 }
178
179 void MainWindow::sendPushButtonClicked()
180 {
181 /* 获取 textEdit 数据,转换成 utf8 格式的字节流 */
182 QByteArray data = textEdit->toPlainText().toUtf8();
183 serialPort->write(data);
184 }
185
186 void MainWindow::openSerialPortPushButtonClicked()
187 {
188 if (pushButton[1]->text() == "打开串口") {
189 /* 设置串口名 */
190 serialPort->setPortName(comboBox[0]->currentText());
191 /* 设置波特率 */
192 serialPort->setBaudRate(comboBox[1]->currentText().toInt());
193 /* 设置数据位数 */
194 switch (comboBox[2]->currentText().toInt()) {
195 case 5:
196 serialPort->setDataBits(QSerialPort::Data5);
197 break;
198 case 6:
199 serialPort->setDataBits(QSerialPort::Data6);
200 break;
201 case 7:
202 serialPort->setDataBits(QSerialPort::Data7);
203 break;
204 case 8:
205 serialPort->setDataBits(QSerialPort::Data8);
206 break;
207 default: break;
208 }
209 /* 设置奇偶校验 */
210 switch (comboBox[3]->currentIndex()) {
211 case 0:
212 serialPort->setParity(QSerialPort::NoParity);
213 break;
214 case 1:
215 serialPort->setParity(QSerialPort::EvenParity);
216 break;
217 case 2:
218 serialPort->setParity(QSerialPort::OddParity);
219 break;
220 case 3:
221 serialPort->setParity(QSerialPort::SpaceParity);
222 break;
223 case 4:
224 serialPort->setParity(QSerialPort::MarkParity);
225 break;
226 default: break;
227 }
228 /* 设置停止位 */
229 switch (comboBox[4]->currentText().toInt()) {
230 case 1:
231 serialPort->setStopBits(QSerialPort::OneStop);
232 break;
233 case 2:
234 serialPort->setStopBits(QSerialPort::TwoStop);
235 break;
236 default: break;
237 }
238 /* 设置流控制 */
239 serialPort->setFlowControl(QSerialPort::NoFlowControl);
240 if (!serialPort->open(QIODevice::ReadWrite))
241 QMessageBox::about(NULL, "错误",
242 "串口无法打开!可能串口已经被占用! ");
243 else {
244 for (int i = 0; i < 5; i++)
245 comboBox[i]->setEnabled(false);
246 pushButton[1]->setText("关闭串口");
247 pushButton[0]->setEnabled(true);
248 }
249 } else {
250 serialPort->close();
251 for (int i = 0; i < 5; i++)
252 comboBox[i]->setEnabled(true);
253 pushButton[1]->setText("打开串口");
254 pushButton[0]->setEnabled(false);
255 }
256 }
257
258 void MainWindow::serialPortReadyRead()
259 {
260 /* 接收缓冲区中读取数据 */
261 QByteArray buf = serialPort->readAll();
262 textBrowser->insertPlainText(QString(buf));
263 }
264
265 MainWindow::~MainWindow()
266 {
267 }

第 29~121 行, 界面布局初始化设置,在嵌入式里,根据实际的屏的大小,设置全屏显示。

其中我们用到垂直布局和网格布局。

习布局内容,学以致用理解的时候到了。

第 123~130 行, 查找系统可用的串口, 并添加串口名到 comboBox0中。

第 132~144 行, 波特率初始化,预设常用的波特率, 115200 作为默认选项。 并添加波特率

到 comboBox1中。

第 146~155 行, 数据位项初始化,设置默认数据位为 8。

第 157~166 行, 校验位项初始化,默认无校验位。

第 168~177 行, 停止位项初始化,默认停止位为 1。

第 179~184 行, 发送数据,点击发送按钮时触发。

第 186~256 行, 打开或者关闭串口。以我们设置的项使用 Qt 串口提供的设置串口的方法

如 setDataBits(QSerialPort::DataBits)等,按第 188~239 行步骤设置完串口需要配置的参数就可以

打开或者关闭串口了。

第 258~263 行, 从缓冲区里读出数据,并显示到 textBrowser 里。

3. 程序运行效果

下面为 Ubuntu 上仿真界面的效果,请将程序交叉编译后到开发板运行,用串口线连接开发板的 UART3 到电脑串口,在电脑用正点原子的 XCOM 上位机软件(或者本程序亦可当上位机软件),设置相同的串口参数,选择串口号为 ttymxc2 (注意 ttymxc0 已经作为调试串口被使用了!),点击打开串口就可以进行消息收发了。默认参数为波特率为 115200,数据位为 8,校验为 None,停止位为 1,流控为关闭。

4. 串口测试

关于串口测试的硬件软件,请参考"01【正点原子】I.MX6U用户快速体验V2.6"

实际测试时,请参考Qt串口编程3-开发板验证,下图是实现了上位机与板卡连接的UI上位机的通讯。

对应地址:Qt串口编程1-上位机界面设计Qt串口编程2-编写程序功能Qt串口编程3-开发板验证

相关推荐
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner1 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术13 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript