#include "uart.h"
#include "unistd.h"
#include <QtSerialPort/QtSerialPort>
#include <QTimer>
#include <QByteArray>
#include <QThread>
#include <QDebug>
std::vector<QSerialPortInfo> Uart::serialPorts_;
Uart::Uart(const QString& uartName, uint baudRate, int ms_rcv/*=-1*/)
: uart_(nullptr)
, uartName_(uartName)
, baudRate_(baudRate)
, serialConnect_(false)
, thread_(nullptr)
, timer_(nullptr)
, enWork_(false)
{
uart_ = new QSerialPort;
Q_CHECK_PTR(uart_);
if (ms_rcv > 0)
{
qDebug("== Use a separate thread to receive serial:%s data. ==", uartName.toLatin1().constData());
thread_ = new QThread;
Q_CHECK_PTR(thread_);
timer_ = new QTimer(this);
Q_CHECK_PTR(timer_);
timer_->setInterval(ms_rcv/2);
connect(timer_, &QTimer::timeout, this, [=](){
timer_->stop();
if (buf_.length()>0)
{
recvData(buf_);
buf_.clear();
}
});
connect(uart_, &QSerialPort::readyRead, this, &Uart::onReadyRead);
this->moveToThread(thread_);
uart_->moveToThread(thread_);
connect(thread_, &QThread::started, this, [=](){
createUartIntance(uartName_, baudRate_);
if (!serialConnect_)
{
QTimer::singleShot(30*1000, this, SLOT(onReconnect()));
}
});
thread_->start();
}
else
{
qDebug("** Use GUI-thread to receive serial:%s data. **", uartName.toLatin1().constData());
createUartIntance(uartName_, baudRate_);
if (!serialConnect_)
{
QTimer::singleShot(30*1000, this, SLOT(onReconnect()));
}
}
}
Uart::~Uart()
{
if (timer_ != nullptr)
{
timer_->stop();
delete timer_;
timer_ = nullptr;
}
uart_->close();
delete uart_;
if (thread_ != nullptr)
{
if (thread_->isRunning())
{
thread_->quit();
thread_->wait(500);
}
}
}
void Uart::onReadyRead()
{
QByteArray by = uart_->readAll();
if (by.size() <= 0) return;
if (!enWork_)
{
if (!buf_.isEmpty())
{
buf_.clear();
}
return;
}
buf_.append(by);
if (buf_.size() >= 56)
{
timer_->start();
}
}
int Uart::InitAvaliablePorts()
{
qDebug("Avaliable SerialPorts: ");
serialPorts_.clear();
int i = 0;
foreach (const QSerialPortInfo & info, QSerialPortInfo::availablePorts())
{
qDebug("- [%d] %s, Desc:%s\t", i,
info.portName().toLatin1().data(),
info.description().toLatin1().data());
serialPorts_.push_back(info);
++i;
}
return serialPorts_.size();
}
bool Uart::GetSerialPortInfo(QSerialPortInfo& serialChosen, const QString& uartName)
{
int portIndex = -1;
for (size_t i=0; i<Uart::serialPorts_.size(); ++i)
{
if (serialPorts_[i].portName() == uartName)
{
portIndex = i;
break;
}
}
if (portIndex == -1) return false;
serialChosen = Uart::serialPorts_[portIndex];
return true;
}
void Uart::onReconnect()
{
serialConnect_ = (ConnectUart(serialChosen_, baudRate_)>=0);
if (!serialConnect_)
{
qWarning() << QString("### Err: !! Serial %1 Not Connect !! ###").arg(serialChosen_.portName()).toLatin1().constData();
QTimer::singleShot(30*1000, this, SLOT(onReconnect()));
}
}
void Uart::createUartIntance(const QString& uartName, uint baudRate)
{
int count = 0;
int portIndex = -1;
do {
for (size_t i=0; i<Uart::serialPorts_.size(); ++i)
{
if (Uart::serialPorts_[i].portName() == uartName)
{
portIndex = i;
break;
}
}
if (portIndex != -1) break;
usleep(500*1000);
count++;
} while (count<6);
if (portIndex == -1)
{
portIndex = 0;
return;
}
serialChosen_ = Uart::serialPorts_[portIndex];
serialConnect_ = (ConnectUart(serialChosen_, baudRate)>=0);
if (!serialConnect_)
{
qWarning() << "Err: !! Serial Not Connect !!";
qWarning() << "Err: !! Serial Not Connect !!";
qWarning() << "Err: !! Serial Not Connect !!";
}
}
int Uart::ConnectUart(const QSerialPortInfo& serialChosen, uint baudRate)
{
uart_->setPort( serialChosen );
if (!uart_->open(QIODevice::ReadWrite))
{
int retries = 10; //30
while (retries-- > 0 && uart_->error() == QSerialPort::PermissionError)
{
qDebug() << QString("Cannot open %1").arg(uart_->portName()).toLatin1().data();
usleep(500*1000);
if (uart_->open(QIODevice::ReadWrite))
{
uart_->clearError();
break;
}
}
if (uart_->error() != QSerialPort::NoError)
{
qWarning() << QString("Failed to open Serial Device: %1 (E%2)").arg(uart_->portName()).arg(uart_->error());
return -1;
}
}
uart_->setBaudRate(QSerialPort::BaudRate(38400));
uart_->setParity(QSerialPort::NoParity);
uart_->setDataBits(QSerialPort::Data8);
uart_->setStopBits(QSerialPort::OneStop);
uart_->setFlowControl(QSerialPort::NoFlowControl);
uart_->clearError();
uart_->clear();
if (timer_ == nullptr)
{
connect(uart_, &QSerialPort::readyRead, this, [=](){
QByteArray by = uart_->readAll();
if (by.size() <= 0) return;
if (!enWork_) {
return;
}
recvData( by );
});
}
qDebug() << QString("%1 is ready.").arg(uart_->portName()).toLatin1().data();
return 0;
}
void Uart::recvData(QByteArray by)
{
if (!_yReadHead)
{
if (!_recvData.isEmpty())
_recvData.clear();
}
_recvData.append(by);
checkData(_recvData, _yReadHead); //virtual
}
void Uart::checkData(QByteArray &by, bool &hasReadHead)
{
static int s_iErr = 0;
int firstHeaderIndex = 0;
while (1)
{
firstHeaderIndex = by.indexOf("!AIV");
if (firstHeaderIndex < 0)
{
// qDebug() << "!! Cannot find Head.";
hasReadHead = false;
break;
}
hasReadHead = true;
if (firstHeaderIndex > 0)
{
by.remove(0, firstHeaderIndex);
}
int index = by.indexOf("\r\n");
if (index == -1)
{
if (++s_iErr >= 3)
{
qDebug() << "!! MS Err, no end-char, delete.";
hasReadHead = false;
}
break;
}
s_iErr = 0;
if (index < 20)
{
qDebug() << "!! MS Err, delete.";
hasReadHead = false;
break;
}
int len = index + 2; //20220103
if (len >= 120)
{
qDebug() << "!! Ms Too long, delete.";
hasReadHead = false;
break;
}
QByteArray data = by.mid(0, len);
index = data.indexOf("\r\n");
resolverFrame(data.mid(0,index));
by.remove(0, len);
if (by.isEmpty())
{
hasReadHead = false;
break;
}
}
}
void Uart::resolverFrame(const QByteArray &by)
{
//数据解析
qDebug()<<by;
}
void Uart::onSendData(const unsigned char* ch, int count, int retry/*=3*/)
{
int ret = justSend(ch, count, retry);
if (ret == 0) //<0: not connect
{
qDebug("Err: Uart Send fail (%d).", ret);
}
}
int Uart::justSend(const uchar* ch, int count, int retry)
{
if (!serialConnect_) return -1;
int i = 0;
bool success = true;
while (1)
{
if (uart_->write((const char*)ch, count) != count)
{
if (++i >= retry)
{
串口数据解析,将自动拆分的数据整合成完整的一帧
需积分: 0 195 浏览量
更新于2023-04-03
1
收藏 1.27MB ZIP 举报
在IT领域,串口通信是设备间数据交换的重要方式,特别是在嵌入式系统、工业自动化以及物联网设备中广泛应用。本文将深入探讨串口数据解析,特别是如何将被自动拆分的数据整合成完整的一帧,同时关注如何在Qt环境中实现这一过程。
串口通信遵循特定的协议,如RS-232、RS-485等,这些协议定义了数据传输的速度、校验方式、数据位、停止位等参数。当数据帧较长,超过串口一次发送的最大字节数时,数据会被自动拆分成多个部分进行传输。因此,接收端需要有能力识别并重组这些分片,确保数据的完整性。
在Qt框架中,`QSerialPort`类提供了串口通信的基础功能,包括打开、关闭串口,设置波特率、数据位、奇偶校验、停止位等参数,以及读取和写入数据。对于串口数据的解析,我们通常会自定义一个数据解析函数,这个函数负责接收来自串口的原始字节流,并将其重组为完整的数据帧。
在描述中提到的“处理和不处理拆分数据”两种模式,可能指的是是否立即处理接收到的每个数据块,或者先缓存起来,等到收到完整帧的标志或校验和后才进行处理。这种策略对于处理不同类型的串口协议至关重要,因为某些协议可能包含帧起始和结束标记,或者使用校验和来确认数据的正确性。
在处理拆分数据时,我们可能需要实现以下步骤:
1. **接收数据**:使用`QSerialPort::readyRead()`信号监听串口上的数据可用事件,然后通过`QSerialPort::readAll()`或`QSerialPort::read()`获取数据。
2. **数据缓冲**:将接收到的数据存储到缓冲区,等待后续处理。
3. **帧边界检测**:根据协议定义,查找帧的起始和结束标志,或者通过计数已接收的字节数判断是否构成一个完整帧。
4. **校验和计算**:如果协议包含校验和,如CRC或简单的奇偶校验,需要计算接收到的数据的校验值。
5. **数据重组**:当发现一个完整的帧后,从缓冲区提取该帧的数据并进行解析。
6. **错误检查与重传**:如果校验失败,可能需要请求重发,或者根据协议规则处理错误。
在实现上述功能时,可以考虑使用状态机模型,创建不同的状态(如等待帧头、接收数据、校验、等待帧尾等),根据接收到的数据和当前状态更新状态机,这有助于保持代码的清晰和模块化。
串口数据解析是一项涉及协议理解、数据处理和错误控制的关键任务。在Qt环境下,利用`QSerialPort`类提供的功能,结合自定义的解析逻辑,我们可以有效地处理被自动拆分的数据,确保数据帧的完整性和准确性。实际应用中,可能还需要考虑到线程安全、并发读写等问题,以适应多线程或并发操作的环境。
东方忘忧
- 粉丝: 1w+
- 资源: 38
最新资源
- Python实现HTML压缩功能
- 完结26章Java主流分布式解决方案多场景设计与实战
- ECSHOP模板堂最新2017仿E宠物模板 整合ECTouch微分销商城
- Pear Admin 是 一 款 开 箱 即 用 的 前 端 开 发 模 板,提供便捷快速的开发方式,延续 Admin 的设计规范
- 51单片机仿真摇号抽奖机源程序12864液晶显示仿真+程序
- 家庭用具检测21-YOLO(v5至v11)、COCO、Paligemma、TFRecord、VOC数据集合集.rar
- Intel-633246-eASIC-PB-006-N5X-Product-Brief .pdf
- Avue.js是基于现有的element-plus库进行的二次封装,简化一些繁琐的操作,核心理念为数据驱动视图,主要的组件库针对table表格和form表单场景,同时衍生出更多企业常用的组件,达到高复
- STM32F401,使用ST-link时候,不能识别,显示ST-LINK USB communication error
- 快速排序算法Python实现:详解分治法原理与高效排序步骤