当前位置: 首页 > news >正文

【嵌入式——QT】QT集成Ymodem协议使用UDP进行传输

【嵌入式——QT】QT集成Ymodem协议使用UDP进行传输

  • Ymodem协议
    • 帧的数据格式
      • 帧头
      • 包号
      • 校验
    • 通讯过程
      • 握手信号
      • 起始帧
      • 数据帧
      • 结束帧
      • 代码块
    • Ymodem命令
  • QT实现
    • YmodemFileTransmit.h
    • YmodemFileTransmit.cpp
    • BootLoader.h
    • BootLoader.cpp
    • Ymodem协议源码

Ymodem协议

帧的数据格式

帧头、包号、包号反码、数据、校验。

帧头包号包号反码数据校验高位校验低位
Soh/Stx0x000xFFDATACRC_HCRC_L

帧头

以Soh(0x01)开始的数据包,信息块是128字节,该帧类型总长度为133字节。
以Stx(0x02)开始的数据包,信息块是1024字节,该帧类型总长度为1029字节。

包号

包号是为数据块的编号,将要传送的数据进行分块编号,只有一个字节,范围为0~255。大于255的则归零重复计算。

校验

Ymodem采用的是CRC16校验算法,校验值为2字节。

uint16_t Ymodem::crc16(uint8_t *buff, uint32_t len)
{uint16_t crc = 0;while(len--){crc ^= (uint16_t)(*(buff++)) << 8;for(int i = 0; i < 8; i++){if(crc & 0x8000){crc = (crc << 1) ^ 0x1021;}else{crc = crc << 1;}}}return crc;
}

通讯过程

握手信号

发送方收到接收方发送的CodeC(0x43)命令后,才可以开始发送起始帧。

起始帧

帧头包号包号反码文件名称文件大小填充区校验高位校验低位
CodeSoh0x000xFFFileName+0x00FileSize+0x00NULL(0x00)CRC_HCRC_L

文件名称后必须添加0x00作为结束,文件大小值后必须加0x00作为结束,余下的位置以0x00填充。

数据帧

帧头包号包号反码有效数据校验高位校验低位
CodeSoh/CodeStx0x000xFFDATACRC_HCRC_L

对于SOH帧,若余下数据小于128字节,则以0x1A填充,该帧长度仍为133字节;
对于STX帧需考虑几种情况:

  • 余下数据等于1024字节,以1029长度帧发送;
  • 余下数据小于1024字节,但大于128字节,以1029字节帧长度发送,无效数据以0x1A填充;
  • 余下数据等于128字节,以133字节帧长度发送;
  • 余下数据小于128字节,以133字节帧长度发送,无效数据以0x1A填充;

结束帧

帧头包号包号反码数据区校验高位校验低位
CodeSoh0x000xFFDATACRC_HCRC_L
数据区,校验都以0x00填充。

代码块

void Ymodem::transmit()
{switch(stage){case StageNone:{transmitStageNone();break;}case StageEstablishing:{transmitStageEstablishing();break;}case StageEstablished:{transmitStageEstablished();break;}case StageTransmitting:{transmitStageTransmitting();break;}case StageFinishing:{transmitStageFinishing();break;}default:{transmitStageFinished();}}
}

Ymodem命令

CodeEot、CodeCan由发送端发送;
CodeAck、CodeNak、CodeC由接收端发送;

命令命令码备注
CodeNone0x00
CodeSoh0x01133字节长度
CodeStx0x021024字节长度
CodeEot0x04文件传输结束指令
CodeAck0x06接收正确指令
CodeNak0x15重传当前数据包请求指令
CodeCan0x18取消传输指令,连续发送5个该命令,终止传输
CodeC0x43字符C
CodeA10x41
CodeA20x61

QT实现

YmodemFileTransmit.h

#ifndef YMODEMFILETRANSMIT_H
#define YMODEMFILETRANSMIT_H#include <QFile>
#include <QTimer>
#include <QObject>
#include "Ymodem.h"
#include <QUdpSocket>class YmodemFileTransmit : public QObject, public Ymodem
{Q_OBJECTpublic:explicit YmodemFileTransmit(QObject* parent = nullptr);~YmodemFileTransmit();void setFileName(const QString& name);void setIpAddress(const QString& ip);void setPortNumber(quint16 port);bool startTransmit();void stopTransmit();int getTransmitProgress();Status getTransmitStatus();signals:void transmitProgress(int progress);void transmitStatus(YmodemFileTransmit::Status status);public slots:void readTimeOut();void writeTimeOut();private:Code callback(Status status, uint8_t* buff, uint32_t* len);uint32_t read(uint8_t* buff, uint32_t len);uint32_t write(uint8_t* buff, uint32_t len);QFile*       file;QTimer*      readTimer;QTimer*      writeTimer;QUdpSocket* udpClient;int      progress;Status   status;uint64_t fileSize;uint64_t fileCount;QString  serverIp;uint16_t serverPort;
};#endif // YMODEMFILETRANSMIT_H

YmodemFileTransmit.cpp

#include "YmodemFileTransmit.h"
#include <QFileInfo>
#include <QNetworkDatagram>
#include <QThread>#define READ_TIME_OUT   (10)
#define WRITE_TIME_OUT  (1000)YmodemFileTransmit::YmodemFileTransmit(QObject* parent) :QObject(parent),file(new QFile),readTimer(new QTimer),writeTimer(new QTimer),udpClient(new QUdpSocket)
{setTimeDivide(499);setTimeMax(5);setErrorMax(999);connect(readTimer, SIGNAL(timeout()), this, SLOT(readTimeOut()));connect(writeTimer, SIGNAL(timeout()), this, SLOT(writeTimeOut()));
}YmodemFileTransmit::~YmodemFileTransmit()
{delete file;delete readTimer;delete writeTimer;delete udpClient;
}void YmodemFileTransmit::setFileName(const QString& name)
{file->setFileName(name);
}void YmodemFileTransmit::setIpAddress(const QString& ip)
{serverIp = ip;
}void YmodemFileTransmit::setPortNumber(quint16 port)
{serverPort = port;
}bool YmodemFileTransmit::startTransmit()
{progress = 0;status   = StatusEstablish;QByteArray array;array.append(0x02);array.append(0x01);array.append(0xFF);array.append(0x07);array.append(0x01);array.append(0x09);array.append(0x03);QHostAddress targetAddr(serverIp);int ret = udpClient->writeDatagram(array, targetAddr, serverPort);if(ret > 0) {QThread::msleep(50);readTimer->start(READ_TIME_OUT);return true;} else {return false;}
}void YmodemFileTransmit::stopTransmit()
{file->close();abort();status = StatusAbort;writeTimer->start(WRITE_TIME_OUT);
}int YmodemFileTransmit::getTransmitProgress()
{return progress;
}Ymodem::Status YmodemFileTransmit::getTransmitStatus()
{return status;
}void YmodemFileTransmit::readTimeOut()
{readTimer->stop();transmit();if((status == StatusEstablish) || (status == StatusTransmit)) {readTimer->start(READ_TIME_OUT);}
}void YmodemFileTransmit::writeTimeOut()
{writeTimer->stop();transmitStatus(status);
}Ymodem::Code YmodemFileTransmit::callback(Status status, uint8_t* buff, uint32_t* len)
{switch(status) {case StatusEstablish:if(file->open(QFile::ReadOnly) == true) {QFileInfo fileInfo(*file);fileSize  = fileInfo.size();fileCount = 0;strcpy((char*)buff, fileInfo.fileName().toLocal8Bit().data());strcpy((char*)buff + fileInfo.fileName().toLocal8Bit().size() + 1, QByteArray::number(fileInfo.size()).data());*len = YMODEM_PACKET_SIZE;YmodemFileTransmit::status = StatusEstablish;transmitStatus(StatusEstablish);return CodeAck;} else {YmodemFileTransmit::status = StatusError;writeTimer->start(WRITE_TIME_OUT);return CodeCan;}case StatusTransmit:if(fileSize != fileCount) {if((fileSize - fileCount) > YMODEM_PACKET_SIZE) {fileCount += file->read((char*)buff, YMODEM_PACKET_1K_SIZE);*len = YMODEM_PACKET_1K_SIZE;} else {fileCount += file->read((char*)buff, YMODEM_PACKET_SIZE);*len = YMODEM_PACKET_SIZE;}progress = (int)(fileCount * 100 / fileSize);YmodemFileTransmit::status = StatusTransmit;transmitProgress(progress);transmitStatus(StatusTransmit);return CodeAck;} else {YmodemFileTransmit::status = StatusTransmit;transmitStatus(StatusTransmit);return CodeEot;}case StatusFinish:file->close();YmodemFileTransmit::status = StatusFinish;writeTimer->start(WRITE_TIME_OUT);return CodeAck;case StatusAbort:file->close();YmodemFileTransmit::status = StatusAbort;writeTimer->start(WRITE_TIME_OUT);return CodeCan;case StatusTimeout:YmodemFileTransmit::status = StatusTimeout;writeTimer->start(WRITE_TIME_OUT);return CodeCan;default:file->close();YmodemFileTransmit::status = StatusError;writeTimer->start(WRITE_TIME_OUT);return CodeCan;}
}uint32_t YmodemFileTransmit::read(uint8_t* buff, uint32_t len)
{QNetworkDatagram datagram =udpClient->receiveDatagram(len);QByteArray array = datagram.data();uint32_t lenArray = array.size();uint32_t lenBuff  = len;uint32_t length = qMin(lenArray, lenBuff);memcpy(buff, array, length);return length;
}uint32_t YmodemFileTransmit::write(uint8_t* buff, uint32_t len)
{QHostAddress targetAddr(serverIp);int ret = udpClient->writeDatagram((char*)buff, len, targetAddr, serverPort);return ret;
}

BootLoader.h

#ifndef BOOTLOADER_H
#define BOOTLOADER_H#include <QWidget>
#include <QUdpSocket>
#include <YmodemFileTransmit.h>QT_BEGIN_NAMESPACE
namespace Ui
{class BootLoader;
}
QT_END_NAMESPACEclass BootLoader : public QWidget
{Q_OBJECTpublic:BootLoader(QWidget* parent = nullptr);~BootLoader();YmodemFileTransmit* ymodemFileTransmit;public slots:void on_pushButtonConnect_clicked();void on_pushButtonBrowse_clicked();void on_pushButtonSend_clicked();void readData();void transmitProgress(int progress);void transmitStatus(YmodemFileTransmit::Status status);private:Ui::BootLoader* ui;bool firemwareTransmitStatus;QUdpSocket* udpClient;
};
#endif // BOOTLOADER_H

BootLoader.cpp

#include "BootLoader.h"
#include "ui_BootLoader.h"
#include <QByteArray>
#include <QDebug>
#include <QNetworkDatagram>
#include <QFileDialog>
#include <QMessageBox>#define SERVER_ADDR "192.168.xxx.xxx"
#define SERVER_PORT 4002BootLoader::BootLoader(QWidget* parent): QWidget(parent), ui(new Ui::BootLoader)
{ui->setupUi(this);this->setWindowTitle(tr("EthernetYmodem"));this->setWindowIcon(QIcon(":/images/main.ico"));ymodemFileTransmit = new YmodemFileTransmit();connect(ymodemFileTransmit, SIGNAL(transmitProgress(int)), this, SLOT(transmitProgress(int)));connect(ymodemFileTransmit, SIGNAL(transmitStatus(YmodemFileTransmit::Status)), this, SLOT(transmitStatus(YmodemFileTransmit::Status)));udpClient = new QUdpSocket(this);connect(udpClient, &QUdpSocket::readyRead, this, &BootLoader::readData);firemwareTransmitStatus = false;ui->pushButtonSend->setEnabled(false);ui->pushButtonConnect->setEnabled(true);
}BootLoader::~BootLoader()
{delete ui;
}void BootLoader::on_pushButtonConnect_clicked()
{QByteArray array;array.append(0x02);array.append(0x01);array.append(0xFF);array.append(0x07);array.append(0x01);array.append(0x09);array.append(0x03);QHostAddress targetAddr(SERVER_ADDR);int ret = udpClient->writeDatagram(array, targetAddr, SERVER_PORT);qDebug()<<"ret"<<ret;
}void BootLoader::on_pushButtonBrowse_clicked()
{QString curPath = QDir::currentPath();ui->lineEditFilePath->setText(QFileDialog::getOpenFileName(this, u8"打开文件", curPath, u8"任意文件 (*.*)"));
}void BootLoader::on_pushButtonSend_clicked()
{udpClient->abort();if(ui->lineEditFilePath->text().isEmpty()) {QMessageBox::warning(this, tr("!!!"), tr("Please select a file!"));return;}if(firemwareTransmitStatus == false) {ymodemFileTransmit->setFileName(ui->lineEditFilePath->text());ymodemFileTransmit->setIpAddress(SERVER_ADDR);ymodemFileTransmit->setPortNumber(SERVER_PORT);if(ymodemFileTransmit->startTransmit() == true) {firemwareTransmitStatus = true;ui->progressBar->setValue(0);ui->pushButtonSend->setText(tr("Cancel"));ui->pushButtonConnect->setEnabled(false);} else {QMessageBox::warning(this, tr("Failure"), tr("File failed to send!"), tr("Closed"));ui->pushButtonSend->setText(tr("Send"));ui->pushButtonConnect->setEnabled(true);}} else {ymodemFileTransmit->stopTransmit();ui->pushButtonSend->setText(tr("Send"));ui->pushButtonConnect->setEnabled(true);}
}void BootLoader::readData()
{while(udpClient->hasPendingDatagrams()) {QNetworkDatagram datagram = udpClient->receiveDatagram();QByteArray receivedData = datagram.data();qDebug() << "Received data:" << receivedData;if(receivedData.size() > 0 && receivedData[0] == (char)0x43) {ui->pushButtonSend->setEnabled(true);ui->pushButtonConnect->setEnabled(false);}}
}void BootLoader::transmitProgress(int progress)
{ui->progressBar->setValue(progress);
}void BootLoader::transmitStatus(Ymodem::Status status)
{switch(status) {case YmodemFileTransmit::StatusEstablish:break;case YmodemFileTransmit::StatusTransmit:break;case YmodemFileTransmit::StatusFinish:firemwareTransmitStatus = false;QMessageBox::information(this, tr("OK"), tr("Upgrade successed!"), QMessageBox::Yes);ui->pushButtonSend->setText(tr("Send"));ui->pushButtonSend->setEnabled(false);ui->pushButtonConnect->setEnabled(true);break;case YmodemFileTransmit::StatusAbort:firemwareTransmitStatus = false;QMessageBox::warning(this, tr("failure"), tr("File failed to send!"), tr("Closed"));break;case YmodemFileTransmit::StatusTimeout:firemwareTransmitStatus = false;QMessageBox::warning(this, tr("failure"), tr("File failed to send!"), tr("Closed"));break;default:firemwareTransmitStatus = false;QMessageBox::warning(this, tr("failure"), tr("File failed to send!"), tr("Closed"));}
}

Ymodem协议源码

源码链接

http://www.lryc.cn/news/348963.html

相关文章:

  • python笔记(17)输入输出
  • 408数据结构总结复习笔记一:线性表
  • Docker——目录迁移
  • SpringAMQP-消息转换器
  • 轻松拿下指针(5)
  • Nginx反向代理配置
  • 突破编程界限:探索AI编程新境界
  • C语言(指针)2
  • go学习笔记
  • MacApp自动化测试之Automator初体验
  • Vue学习v-html
  • C++并发:锁
  • Git | git log 和 git status 的区别
  • Django 4.x 智能分页get_elided_page_range
  • java-spring 09 下.populateBean (方法成员变量的注入@Autowird,@Resource)
  • 赛氪网携手众机构助力第七届京津冀生态修复实践论坛圆满落幕
  • Naive RAG 、Advanced RAG 和 Modular RAG 简介
  • Python高级编程-DJango2
  • bash脚本 报错:/bin/bash^M:解释器错误: 没有那个文件或目录
  • win10专业版远程桌面连接不上,win10专业版远程桌面连接不上常见原因与解决方法
  • 前端 日期 new Date 少0 转换成 yyyy-MM-dd js vue
  • Linux中的磁盘分析工具ncdu
  • Angular入门
  • Java进阶11 IO流、功能流
  • windows 安装 Conda
  • IPsec VPN简介
  • 探索 Canva 的功能以及如何有效使用 Canva
  • python中匿名函数简单样例
  • 【SpringBoot】 什么是springboot(二)?springboot操作mybatisPlus、swagger、thymeleaf模板
  • 【JavaWeb】前后端分离SpringBoot项目快速排错指南