未完成部分:显示发送、自动保存、自动重发、设置背景、将之前发送内容再次显示到发送框等
一、创建SerialTool.pro项目
窗口类MainWindow
在SerialTool.pr中添加QT += serialport
二、UI界面制作
三、创建资源文件(.qrc)——暂时添加一张图,用于串口调试助手的图标
项目右击——Add new...——Qt——Qt Resource File——命名为res(添加目录一定要在项目目录下,默认是)——完成——Add Prefix——前缀本文改成/res——Add Files——在项目目录或项目目录的子文件夹中选取图片(可逐个添加,也可选取多张一起添加)——点击打开——运行则可看见所添加的资源
四、serialtool.h代码
#ifndef SERIALTOOL_H
#define SERIALTOOL_H
#include <QMainWindow>
#include <QPaintEvent>
#include <QPainter>
#include <QPixmap>
#include <QIcon>
#include <QAction>
#include <QTime>
#include <QTimer>
#include <QDateTime>
#include <QMessageBox>
#include <QSerialPort> //提供访问串行端口的功能
#include <QSerialPortInfo>
#include <QByteArray>
QT_BEGIN_NAMESPACE
namespace Ui { class SerialTool; }
QT_END_NAMESPACE
class SerialTool : public QMainWindow
{
Q_OBJECT
public:
SerialTool(QWidget *parent = nullptr);
~SerialTool();
private:
Ui::SerialTool *ui;
private:
void paintEvent(QPaintEvent *);//添加背景等,暂时未编辑
void systeminit();//初始化
void updataserial();//串口更新
char convertHexFromChar(char ch);//字符转HEX(16进制)
QSerialPort * Gangge_port=nullptr;//所用串口
QTimer * timer;//定时器,用于更新串口
QStringList portStringList;
QString currentCOM = "";//更新串口用到
int send_buf_len=0;//用于统计发送字节数
int receive_buf_len=0;//用于统计接收字节数
private slots:
void on_openbutton_clicked();//UI文件中所在按钮右击所添加的槽函数
void on_SendButton_clicked();//UI文件中所在按钮右击所添加的槽函数
void Receive_data();
//自定义槽函数,需连接信号与槽(前面不能如前加on_,否则
//会有提示::connectSlotsByName: No matching signal for on_Receive_data()
//原因可上网查找
//添加on_,在UI文件绘制到屏幕时就调用on_Receive_data(),
//而此时串口信号和槽函数的代码还没有被执行,所以槽函数此时还连接不到对象,故弹出警告。
};
#endif // SERIALTOOL_H
五、serialtool.c代码
#include "serialtool.h"
#include "ui_serialtool.h"
SerialTool::SerialTool(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::SerialTool)
{
ui->setupUi(this);
systeminit();
}
SerialTool::~SerialTool()
{
delete ui;
}
void SerialTool::systeminit()
{
setWindowIcon(QIcon(":/res/res/default.ico"));//图标
ui->SendButton->setEnabled(false);//发送按钮默认为非
connect(ui->actionquit,&QAction::triggered,this,[=](){
this->close();
});//退出按钮实现
connect(ui->actionsd,&QAction::triggered,this,[=](){
ui->receiveview->clear();
});//清屏按钮实现
//获取串口号
ui->portname->clear();
portStringList.clear();
Gangge_port=new QSerialPort();
//foreach用于遍历,不能修改,把availablePorts中的内容依次赋给info
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) //扫描可用串口
{
portStringList << info.portName();
}
ui->portname->addItems(portStringList);//显示可用端口
timer=new QTimer(this);
connect(timer,&QTimer::timeout,this,&SerialTool::updataserial);//定时更新端口
timer->start(1000);
//初始化配置串口
Gangge_port->setParity(QSerialPort::NoParity); //校验位
Gangge_port->setDataBits(QSerialPort::Data8);//数据位
Gangge_port->setStopBits(QSerialPort::OneStop);//停止位
Gangge_port->setFlowControl(QSerialPort::NoFlowControl);//流控制一般没用
}
//更新并检测串口函数实现
void SerialTool::updataserial()
{
QStringList newPortStringList;
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
newPortStringList += info.portName();
}
if(newPortStringList.size() != portStringList.size())
{
portStringList = newPortStringList;
ui->portname->clear();
ui->portname->addItems(portStringList);
}
if(currentCOM != ui->portname->currentText()) //串口突然断开连接了
{
currentCOM = ui->portname->currentText();
if("关闭串口" == ui->openbutton->text())
{
on_openbutton_clicked();
}
}
}
void SerialTool::paintEvent(QPaintEvent *)
{
}
void SerialTool::on_openbutton_clicked()
{
if(ui->openbutton->text()=="打开串口")
{
if(ui->portname->currentText()=="")
{
return;
}
Gangge_port->setPortName(ui->portname->currentText());//设置串口
Gangge_port->setBaudRate(ui->boundrate->currentText().toInt());//设置波特率
//bool bOK = Gangge_port->open(QIODevice::ReadWrite);
//QIODevice类是输入/输出设备的基类,QSerialPort是QIODevice类型的对象
bool bOK = Gangge_port->open(QSerialPort::ReadWrite);//用ReadWrite的模式尝试打开串口
if(!bOK)
{
QMessageBox::critical(this,"提示","无法打开串口,请检查是否被占用!",QMessageBox::Yes,QMessageBox::Yes);
return;
}
switch(ui->partity->currentIndex())//设置数据校验形式
{
case 0:
Gangge_port->setParity(QSerialPort::NoParity);//无校验,没有奇偶校验位发送,Value=0
break;
case 1:
Gangge_port->setParity(QSerialPort::EvenParity);//偶校验,如果字符数据位中"1"的数目是偶数,则校验位应为"0",如果是奇数则为"1"。,Value=2
break;
case 2:
Gangge_port->setParity(QSerialPort::OddParity);//奇校验,如果字符数据位中"1"的数目是偶数,校验位为"1",如果"1"的数目是奇数,校验位应为"0",Value=3
break;
case 3:
Gangge_port->setParity(QSerialPort::MarkParity);//校验位始终为1,Value=4
break;
case 4:
Gangge_port->setParity(QSerialPort::SpaceParity);//校验位始终为0,Value=5
break;
default:
Gangge_port->setParity(QSerialPort::NoParity);//没有奇偶校验位发送
break;
}
switch(ui->databits->currentIndex())//设置数据位
{
case 0:
Gangge_port->setDataBits(QSerialPort::Data8);
break;
case 1:
Gangge_port->setDataBits(QSerialPort::Data7);
break;
case 2:
Gangge_port->setDataBits(QSerialPort::Data6);
break;
case 3:
Gangge_port->setDataBits(QSerialPort::Data5);
break;
default:
Gangge_port->setDataBits(QSerialPort::Data8);
break;
}
switch (ui->stopbits->currentIndex()) //设置停止位
{
case 0:
Gangge_port->setStopBits(QSerialPort::OneStop);
break;
case 1:
Gangge_port->setStopBits(QSerialPort::OneAndHalfStop);
break;
case 2:
Gangge_port->setStopBits(QSerialPort::TwoStop);
break;
default:
Gangge_port->setStopBits(QSerialPort::OneStop);
break;
}
Gangge_port->setFlowControl(QSerialPort::NoFlowControl); //设置无流控
Gangge_port->setReadBufferSize(8192); //数据缓冲区长度
connect(Gangge_port,&QSerialPort::readyRead,this,&SerialTool::Receive_data,Qt::DirectConnection);//连接串口接收信号与槽函数
//第5个参数一般不填,为默认值 Qt::AutoConnection
ui->portname->setEnabled(false);
ui->boundrate->setEnabled(false);
ui->partity->setEnabled(false);
ui->databits->setEnabled(false);
ui->stopbits->setEnabled(false);
ui->openbutton->setText("关闭串口");
ui->SendButton->setEnabled(true);//打开发送按钮
}
else
{
//如果串口已经打开了 先给他关闭了
if(Gangge_port->isOpen())
{
ui->openbutton->setText("打开串口");
ui->SendButton->setEnabled(false);//发送按钮
Gangge_port->close();
delete Gangge_port;//不加以下三行,每次开关都会多加一行空白行
Gangge_port=nullptr;
systeminit();//不加这一行会闪退,错误:The process was ended forcefully
ui->portname->setEnabled(true);
ui->databits->setEnabled(true);
ui->partity->setEnabled(true);
ui->stopbits->setEnabled(true);
ui->boundrate->setEnabled(true);
}
}
}
//发送按钮按下
void SerialTool::on_SendButton_clicked()
{
if(ui->openbutton->text()=="打开串口")
return;
QString sendstring = ui->senddata->text();
ui->sendcomboBox->setMaxVisibleItems(20);//设置最大显示下列项 超过要使用滚动条拖拉
ui->sendcomboBox->setMaxCount(20);//设置最大下拉项 超过将不显示
ui->sendcomboBox->insertItem(0,sendstring);//指定位置插入单个item(最前面插入)
ui->sendcomboBox->setCurrentIndex(0);//默认显示当前插入的值
char cDataHigh,cDataLow;//每个字节的高位、低位
int isize=ui->senddata->text().size();
//是否HEX发送(分成字节,每两位一个字节)
if(ui->sendHex->isChecked())
{
int iSendDataSize = 0;
QByteArray sendData;
for(int i=0;i<isize-1;)
{
char tempbyte_h=ui->senddata->text().at(i).toLatin1();//转换成ASCII码,Latin1代表ASCII,Local8Bit代表unicode
if(tempbyte_h==' ')
{
i+=1;//遇到空格跳过
}
else
{
char tempByte_l = ui->senddata->text().at(i+1).toLatin1();//转化成ASCII码的形式,便于符号之间的运算
i+=2;//i<isize-1,避免错误 ASSERT: "uint(i) < uint(size())" in file
cDataHigh = SerialTool::convertHexFromChar(tempbyte_h);
cDataLow = SerialTool::convertHexFromChar(tempByte_l);
char sendChar =(char)((cDataHigh<<4)+cDataLow);//一个字符一个字节,八位一个字节(十六进制)
sendData[iSendDataSize]=sendChar;
iSendDataSize++;
}
}
if(isize%2==1)//输入senddata为奇数位,在下一位补零
{
char tempbyte_h=ui->senddata->text().at(isize-1).toLatin1();
cDataHigh = SerialTool::convertHexFromChar(tempbyte_h);
cDataLow = SerialTool::convertHexFromChar(0);
char sendChar =(char)((cDataHigh<<4)+cDataLow);
sendData[iSendDataSize]=sendChar;
iSendDataSize++;
}
send_buf_len+=iSendDataSize;
ui->TXLenLabel->setText(QString::number(send_buf_len)+" bytes");//显示发送byte
Gangge_port->write(sendData);
}
else if(ui->sendASCII->isChecked())
{
//toLatin1、toLocal8Bit都是QString转QByteArray的方法,Latin1代表ASCII,Local8Bit代表unicode
Gangge_port->write((sendstring).toLatin1());//以字符形式发送
//Gangge_port->write(sendstring.toLocal8Bit().data());//另一种写法
}
}
//字符转换成HEX码,不在0~F之内的转换成0
char SerialTool::convertHexFromChar(char ch)
{
if((ch>='0')&&(ch<='9'))
{
return(ch-0x30);
}
else if((ch>='A')&&(ch<='F'))
{
return(ch-'A'+10);
}
else if((ch>='a')&&(ch<='f'))
{
return(ch-'a'+10);
}
else
{
return(0);
}
}
void SerialTool::Receive_data()
{
QByteArray array=Gangge_port->readAll();//读取数据
QString revstring;
//自动换行
QTextCursor txtcur= ui->receiveview->textCursor();
txtcur.setPosition(0);
txtcur.movePosition(QTextCursor::EndOfLine,QTextCursor::KeepAnchor);
QString qstr= txtcur.selectedText();//读取首行
if(ui->receiveenter->isChecked()&&qstr!="")//除去首行外,点击自动换行则在数据显示之前换行
{
ui->receiveview->insertPlainText("\n");
}
//显示时间
if(ui->timebox->isChecked())
{
QDateTime time = QDateTime::currentDateTime();
QString str = time.toString("yyyy-MM-dd hh:mm:ss dddd");
ui->receiveview->insertPlainText(str+": ");
}
//数据转换
if(ui->receiveHex->isChecked())
{
//第一种写法,直接把串口接收到的数据转换为HEX码
//QByteArray byteArray;
// ui->receivewidget->addItem(byteArray.append(array).toHex().toUpper());//接收串口数据十六进制数据显示
//第二种写法,每一个字节后加一个空格
QDataStream outArray(&array,QIODevice::ReadOnly);
qint8 outChar = 0;
QString str;
while(!outArray.atEnd())
{
outArray >> outChar;
revstring = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0'));
str+=revstring+' ';
}
ui->receiveview->insertPlainText(str.toUpper());
}
else if(ui->receiveASCII->isChecked())
{
revstring = QString::fromLocal8Bit(array);
ui->receiveview->insertPlainText(revstring);
}
receive_buf_len += array.length();
ui->RXLenLabel->setText(QString::number(receive_buf_len)+" bytes");//显示接收byte
ui->receiveview->insertPlainText(" ");
ui->receiveview->moveCursor(QTextCursor::End);//自动滚动到底部
}
六、运行(只是演示)
1、所用硬件,两块zigbee,一个是协调器,一个是终端
目的1(测试HEX)
(1)终端连接上位机串口显示终端短地址+长地址
(2)上位机串口调试助手给协调器发送短地址+命令,协调器返回发送内容并发送命令给所指定终端,终端执行操作并返回数据,协调器显示
目的2:测试ASCII
数据显示有时有问题,暂未处理