프로젝트 소스 코드 : https://github.com/zhangfls/QT_UartAnalysisTool
이전:
하나, 라이브러리 가져 오기
1. 프로젝트 .pro 파일에 직렬 포트 추가
QT += core gui
QT += serialport
2. qt에서 직렬 통신에 필요한 헤더 파일을 소개합니다.
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
둘째, 직렬 포트 초기화 구성
1. 사용 가능한 직렬 포트를 검색합니다.
comobox를 생성하면 사용 가능한 직렬 포트 목록이 표시되고 구성 중에 연결할 직렬 포트를 선택하는 데 사용됩니다.
//查找可用串口,刷新串口信息
void MainWindow::GetAveriablePort()
{
ui->uartReadPlain->insertPlainText("串口初始化:\r\n");
//先清除所有串口列表
ui->portBox->clear();
foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
{
QSerialPort serial;
serial.setPort(info);
if(serial.open(QIODevice::ReadWrite))
{
ui->uartReadPlain->insertPlainText("可用:"+serial.portName()+"\r\n");
ui->portBox->addItem(serial.portName());
serial.close();
}
else
{
ui->uartReadPlain->insertPlainText("不可用:"+serial.portName()+"\r\n");
}
}
}
2. 직렬 포트를 구성합니다.
(1) 직렬 포트의 구성에는 적어도 직렬 포트 번호, 전송 속도, 데이터 비트, 정지 비트, 패리티 비트 및 흐름 제어가 포함되어야하며, 모두 직렬 포트 인스턴스의 기능을 호출하여 구성 할 수 있습니다. 선택할 여러 comobox 또는 텍스트 상자를 추가하거나 기본적으로 초기화 할 때 구성 할 수 있습니다.
//配置串口初始化
void MainWindow::PortConfigureInit()
{
//填入串口选项
ui->rateBox->addItem("115200","115200");
ui->rateBox->addItem("38400","38400");
ui->rateBox->addItem("19200","19200");
ui->rateBox->addItem("9600","9600");
ui->dataBox->addItem("8",8);
ui->dataBox->addItem("7",7);
ui->checkBox->addItem("无校验",0);
ui->stopBox->addItem("1位",1);
ui->stopBox->addItem("2位",2);
}
(2) 시리얼 포트를 열고 닫는 버튼을 추가하고, "open serial port"라고 표시되면 클릭하여 시리얼 포트를 닫습니다. 텍스트 디스플레이 "Close serial port"는 반대입니다.
(3) 직렬 포트를 열 때 구성 항목의 모든 상자를 비활성화하여 수정할 수 없도록하고 닫았을 때 복원합니다.
//串口开关按钮
void MainWindow::on_openSerialButton_clicked()
{
//尝试打开串口
if(ui->openSerialButton->text() == tr("打开串口"))
{
if(ui->portBox->currentText() == "" )
{
QMessageBox::warning(NULL, "警告", "无可开启串口!\r\n\r\n");
return;
}
serial = new QSerialPort;
//设置串口名
serial->setPortName(ui->portBox->currentText());
//打开串口
serial->open(QIODevice::ReadWrite);
//设置波特率
serial->setBaudRate(ui->rateBox->currentText().toInt());
//设置数据位
switch (ui->dataBox->currentData().toInt())
{
case 8:
serial->setDataBits(QSerialPort::Data8);
break;
case 7:
serial->setDataBits(QSerialPort::Data7);
break;
default:
break;
}
//设置校验位
switch (ui->checkBox->currentIndex())
{
case 0:
serial->setParity(QSerialPort::NoParity);
break;
default:
break;
}
//设置停止位
switch(ui->stopBox->currentIndex())
{
case 0:
serial->setStopBits(QSerialPort::OneStop);
break;
case 1:
serial->setStopBits(QSerialPort::TwoStop);
break;
default:
break;
}
//设置流控制
serial->setFlowControl(QSerialPort::NoFlowControl); //设置为无流控制
//关闭设置菜单使能
ui->portBox->setEnabled(false);
ui->dataBox->setEnabled(false);
ui->checkBox->setEnabled(false);
ui->stopBox->setEnabled(false);
ui->rateBox->setEnabled(false);
ui->openSerialButton->setText("关闭串口");
fTimeCounter.restart(); //计时器重新计数
//连接信号和槽函数,串口有数据可读时,调用ReadData()函数读取数据并处理。
QObject::connect(serial,&QSerialPort::readyRead,this,&MainWindow::ReadData);
}
else
{
uartRecDataTimer->stop () ; //定时器停止
if(serial->isOpen()) //原先串口打开,则关闭串口
{
serial->close();
}
//释放串口
delete serial;
serial = NULL;
//恢复使能
ui->portBox->setEnabled(true);
ui->rateBox->setEnabled(true);
ui->dataBox->setEnabled(true);
ui->checkBox->setEnabled(true);
ui->stopBox->setEnabled(true);
ui->openSerialButton->setText("打开串口");
}
}
셋, 직렬 포트 데이터 읽기
1. 데이터를 읽기 위해 타이머와 타이머를 생성합니다. 해결해야 할 두 가지 문제가 있기 때문에 하나는 시리얼 포트가 일정 시간 동안 데이터를 수신하지 못한 경우 하나의 수신 완료를 판단하고 데이터를 처리하고 버프를 제거하는 데 사용되는 타임 아웃 간격이 필요하다는 것입니다. . 둘째, 시리얼 포트가 지속적으로 수신 된 시간을 세는 카운트가 필요합니다. 데이터가 연속적이라하더라도 고정 된 시점에 수신 완료를 강제로 판단하고 데이터를 처리하고 버프를 제거해야합니다. 데이터는 처리되지 않을 수 있습니다.
1. 타이머 초기화
//设置uart接收缓冲超时定时器
uartRecDataTimer = new QTimer(this);
uartRecDataTimer->stop();
uartRecDataTimer->setInterval(uartRecOvertimeCount*1000); //设置定时周期,单位:毫秒
uartRecDataTimer->setSingleShot(true); //设置为单次触发
connect(uartRecDataTimer,SIGNAL(timeout()),this,SLOT(uartRec_timeout())); //设置槽
2. ReadData를 구현합니다. 타이머가 지정된 간격을 초과하면 수신 된 버프 버퍼를 강제로 처리하고 나머지 시간 동안 데이터를 버퍼에 넣고 타이머를 다시 시작합니다.
//读取串口接收消息
void MainWindow::ReadData()
{
//串口可读数据长度
int byteLen = serial->bytesAvailable();
if(byteLen < 0)
{
return;
}
rec_buf_len += byteLen;
uart_rec_ss.append(serial->readAll()); //读取数据
//计时器超过最大间隔仍未填入数据,强制填入
if(fTimeCounter.elapsed() >2000 && uart_rec_ss.size()>0)
{
ui->uartReadPlain->moveCursor(QTextCursor::End); //光标移动到结尾
ui->uartReadPlain->insertPlainText(uart_rec_ss);
ui->uartReadPlain->moveCursor(QTextCursor::End); //光标移动到结尾
uart_rec_ss.clear();
}
//定时器开始工作、定时器重启
uartRecDataTimer->start();
}
3. 타이머 수신 완료 (일정 시간 동안 데이터 수신이 없으면 타이머 만료)
타임 스탬프 선택 여부에 따라 데이터 내용을 입력하고 데이터가 저장된 텍스트 상자에 삽입합니다.
//定时器触发打印串口数据
void MainWindow::uartRec_timeout()
{
if(!uart_rec_ss.isEmpty())
{
curDateTime = QDateTime::currentDateTime();
ui->uartReadPlain->moveCursor(QTextCursor::End); //光标移动到结尾
if(ui->timeZoneCheckBox->isChecked())
{
ui->uartReadPlain->insertPlainText("\r\n"+curDateTime.toString("[yyyy-MM-dd hh:mm:ss]")+"R:");
ui->uartReadPlain->moveCursor(QTextCursor::End); //光标移动到结尾
ui->uartReadPlain->insertPlainText(uart_rec_ss);
}
else
{
ui->uartReadPlain->insertPlainText(uart_rec_ss);
}
ui->uartReadPlain->moveCursor(QTextCursor::End); //光标移动到结尾
uart_rec_ss.clear();
fTimeCounter.restart();
ui->RXLenLabel->setText(QString::number(rec_buf_len)+"bytes");
}
}
4. 동시에 시간 제한 간격을 구성하는 옵션이 필요합니다.
(1) 초기화 중 구성 상자 추가
//设置时间输入框只允许使用数字
ui->overTimeRecEdit->setValidator(new QRegExpValidator(QRegExp("^([0-9]{1,4}(.[0-9]{1,3})?)$")));
ui->overTimeRecEdit->setText(QString::number(uartRecOvertimeCount));
(2) 작동 중 타임 아웃 간격 설정
//超时间隔设置
void MainWindow::on_overTimeRecEdit_returnPressed()
{
if(ui->overTimeRecEdit->text().toFloat()>60)
{
QMessageBox::warning(NULL,"警告","超时时间不要超过1分钟");
ui->overTimeRecEdit->setText("0.1");
return;
}
uartRecOvertimeCount = ui->overTimeRecEdit->text().toFloat();
ui->uartReadPlain->insertPlainText("设置超时时间为:"+QString::number(uartRecOvertimeCount*1000)+"ms");
uartRecDataTimer->setInterval(uartRecOvertimeCount*1000); //设置定时周期,单位:毫秒
fTimeCounter.restart();
uartRecDataTimer->start();
}
넷, 데이터 보내기
추가 구성없이 데이터를 전송하고 쓰기 기능을 호출하기 만하면 실제 상황에 따라 일부 구성 또는 검증 처리를 수행 할 수 있습니다. 캐리지 리턴 및 줄 바꿈 추가 등
//发送串口数据
void MainWindow::on_sendDataButton_clicked()
{
//未打开串口则不准发送
if(ui->openSerialButton->text() == "打开串口")
{
QMessageBox::warning(NULL, "警告", "未打开可用串口,无法发送数据!\r\n\r\n");
return;
}
//获取发送的命令,并选择在结尾加上换行,AT的命令结尾必须有回车换行
QString command = ui->uartWritePlain->toPlainText();
if(ui->changeLineCheckBox->isChecked())
{
command += "\r\n";
}
if(ui->timeZoneCheckBox->isChecked())
{
curDateTime = QDateTime::currentDateTime();
ui->uartReadPlain->insertPlainText("\r\n"+curDateTime.toString("[yyyy-MM-dd hh:mm:ss]")+"SEND:"+command);
}
send_buf_len += command.length();
ui->TXLenLabel->setText(QString::number(send_buf_len)+"bytes");
serial->write(command.toLatin1());
}
이 시점에서 가장 기본적인 직렬 포트 디버깅 도구가 완성되었으며 다음은 여기에 기능과 최적화를 추가하는 것입니다.
다음: