YMODEM协议串口文件传输

YMODEM协议串口文件传输

GitHub仓库:https://github.com/XinLiGitHub/SerialPortYmodem

PS:博文不再更新,后续更新会在GitHub仓库进行。


    串口通过YMODEM协议进行文件传输。程序中涉及到YMODEM协议知识,详细介绍见维基百科[YMODEM](https://en.wikipedia.org/wiki/YMODEM)。


1,开发环境

      1,框架:Qt 5.7.1

      2,编译器:MSVC2015_64bit

      3,IDE:Qt Creator 4.2.0 社区版

      4,操作系统:Windows 10 专业版


2,程序源码

      Ymodem.h文件

/**
  ******************************************************************************
  * @file    Ymodem.h
  * @author  XinLi
  * @version v1.0
  * @date    21-January-2018
  * @brief   Header file for Ymodem.cpp module.
  ******************************************************************************
  * @attention
  *
  * <h2><center>Copyright &copy; 2018 XinLi</center></h2>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  *
  ******************************************************************************
  */

#ifndef __YMODEM_H
#define __YMODEM_H

/* Header includes -----------------------------------------------------------*/
#include <stdint.h>

/* Macro definitions ---------------------------------------------------------*/
#define YMODEM_PACKET_HEADER    (3)
#define YMODEM_PACKET_TRAILER   (2)
#define YMODEM_PACKET_OVERHEAD  (YMODEM_PACKET_HEADER + YMODEM_PACKET_TRAILER)
#define YMODEM_PACKET_SIZE      (128)
#define YMODEM_PACKET_1K_SIZE   (1024)

#define YMODEM_CODE_CAN_NUMBER  (5)

/* Type definitions ----------------------------------------------------------*/
class Ymodem
{
public:
  enum Code
  {
    CodeNone = 0x00,
    CodeSoh  = 0x01,
    CodeStx  = 0x02,
    CodeEot  = 0x04,
    CodeAck  = 0x06,
    CodeNak  = 0x15,
    CodeCan  = 0x18,
    CodeC    = 0x43,
    CodeA1   = 0x41,
    CodeA2   = 0x61
  };

  enum Stage
  {
    StageNone,
    StageEstablishing,
    StageEstablished,
    StageTransmitting,
    StageFinishing,
    StageFinished
  };

  enum Status
  {
    StatusEstablish,
    StatusTransmit,
    StatusFinish,
    StatusAbort,
    StatusTimeout,
    StatusError
  };

  Ymodem(uint32_t timeDivide = 499, uint32_t timeMax = 5, uint32_t errorMax = 999);

  void setTimeDivide(uint32_t timeDivide);
  uint32_t getTimeDivide();

  void setTimeMax(uint32_t timeMax);
  uint32_t getTimeMax();

  void setErrorMax(uint32_t errorMax);
  uint32_t getErrorMax();

  void receive();
  void transmit();
  void abort();

private:
  Code receivePacket();

  void receiveStageNone();
  void receiveStageEstablishing();
  void receiveStageEstablished();
  void receiveStageTransmitting();
  void receiveStageFinishing();
  void receiveStageFinished();

  void transmitStageNone();
  void transmitStageEstablishing();
  void transmitStageEstablished();
  void transmitStageTransmitting();
  void transmitStageFinishing();
  void transmitStageFinished();

  uint16_t crc16(uint8_t *buff, uint32_t len);

  virtual Code callback(Status status, uint8_t *buff, uint32_t *len) = 0;

  virtual uint32_t read(uint8_t *buff, uint32_t len)  = 0;
  virtual uint32_t write(uint8_t *buff, uint32_t len) = 0;

  uint32_t timeDivide;
  uint32_t timeMax;
  uint32_t errorMax;

  uint32_t timeCount;
  uint32_t errorCount;
  uint8_t  dataCount;

  Code  code;
  Stage stage;

  uint8_t  rxBuffer[YMODEM_PACKET_1K_SIZE + YMODEM_PACKET_OVERHEAD];
  uint8_t  txBuffer[YMODEM_PACKET_1K_SIZE + YMODEM_PACKET_OVERHEAD];
  uint32_t rxLength;
  uint32_t txLength;
};

/* Variable declarations -----------------------------------------------------*/
/* Variable definitions ------------------------------------------------------*/
/* Function declarations -----------------------------------------------------*/
/* Function definitions ------------------------------------------------------*/

#endif /* __YMODEM_H */

      Ymodem.cpp文件

/**
  ******************************************************************************
  * @file    Ymodem.cpp
  * @author  XinLi
  * @version v1.0
  * @date    21-January-2018
  * @brief   Ymodem protocol module source file.
  ******************************************************************************
  * @attention
  *
  * <h2><center>Copyright &copy; 2018 XinLi</center></h2>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  *
  ******************************************************************************
  */

/* Header includes -----------------------------------------------------------*/
#include "Ymodem.h"
#include <string.h>

/* Macro definitions ---------------------------------------------------------*/
/* Type definitions ----------------------------------------------------------*/
/* Variable declarations -----------------------------------------------------*/
/* Variable definitions ------------------------------------------------------*/
/* Function declarations -----------------------------------------------------*/
/* Function definitions ------------------------------------------------------*/

/**
  * @brief  Ymodem constructor.
  * @param  [in] timeDivide: The fractional factor of the time the ymodem is called.
  * @param  [in] timeMax:    The maximum time when calling the ymodem.
  * @param  [in] errorMax:   The maximum error count when calling the ymodem.
  * @note   The longest waiting time = call time / (@timeDivide + 1) * (@timeMax + 1).
  * @return None.
  */
Ymodem::Ymodem(uint32_t timeDivide, uint32_t timeMax, uint32_t errorMax)
{
  this->timeDivide = timeDivide;
  this->timeMax    = timeMax;
  this->errorMax   = errorMax;

  this->timeCount  = 0;
  this->errorCount = 0;
  this->dataCount  = 0;

  this->code       = CodeNone;
  this->stage      = StageNone;
}

/**
  * @brief  Set the fractional factor of the time the ymodem is called.
  * @param  [in] timeDivide: The fractional factor of the time the ymodem is called.
  * @return None.
  */
void Ymodem::setTimeDivide(uint32_t timeDivide)
{
  this->timeDivide = timeDivide;
}

/**
  * @brief  Get the fractional factor of the time the ymodem is called.
  * @param  None.
  * @return The fractional factor of the time the ymodem is called.
  */
uint32_t Ymodem::getTimeDivide()
{
  return timeDivide;
}

/**
  * @brief  Set the maximum time when calling the ymodem.
  * @param  [in] timeMax: The maximum time when calling the ymodem.
  * @return None.
  */
void Ymodem::setTimeMax(uint32_t timeMax)
{
  this->timeMax = timeMax;
}

/**
  * @brief  Get the maximum time when calling the ymodem.
  * @param  None.
  * @return The maximum time when calling the ymodem.
  */
uint32_t Ymodem::getTimeMax()
{
  return timeMax;
}

/**
  * @brief  Set the maximum error count when calling the ymodem.
  * @param  [in] errorMax: The maximum error count when calling the ymodem.
  * @return None.
  */
void Ymodem::setErrorMax(uint32_t errorMax)
{
  this->errorMax = errorMax;
}

/**
  * @brief  Get the maximum error count when calling the ymodem.
  * @param  None.
  * @return The maximum error count when calling the ymodem.
  */
uint32_t Ymodem::getErrorMax()
{
  return errorMax;
}

/**
  * @brief  Ymodem receive.
  * @param  None.
  * @return None.
  */
void Ymodem::receive()
{
  switch(stage)
  {
    case StageNone:
    {
      receiveStageNone();

      break;
    }

    case StageEstablishing:
    {
      receiveStageEstablishing();

      break;
    }

    case StageEstablished:
    {
      receiveStageEstablished();

      break;
    }

    case StageTransmitting:
    {
      receiveStageTransmitting();

      break;
    }

    case StageFinishing:
    {
      receiveStageFinishing();

      break;
    }

    default:
    {
      receiveStageFinished();
    }
  }
}

/**
  * @brief  Ymodem transmit.
  * @param  None.
  * @return None.
  */
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();
    }
  }
}

/**
  * @brief  Ymodem abort.
  * @param  None.
  * @return None.
  */
void Ymodem::abort()
{
  timeCount  = 0;
  errorCount = 0;
  dataCount  = 0;
  code       = CodeNone;
  stage      = StageNone;

  for(txLength = 0; txLength < YMODEM_CODE_CAN_NUMBER; txLength++)
  {
    txBuffer[txLength] = CodeCan;
  }

  write(txBuffer, txLength);
}

/**
  * @brief  Receives a packet of data.
  * @param  None.
  * @return Packet type.
  */
Ymodem::Code Ymodem::receivePacket()
{
  if(code == CodeNone)
  {
    if(read(&(rxBuffer[0]), 1) > 0)
    {
      if(rxBuffer[0] == CodeSoh)
      {
        uint32_t len = read(&(rxBuffer[1]), YMODEM_PACKET_SIZE + YMODEM_PACKET_OVERHEAD - 1);

        if(len < (YMODEM_PACKET_SIZE + YMODEM_PACKET_OVERHEAD - 1))
        {
          rxLength = len + 1;
          code     = CodeSoh;

          return CodeNone;
        }
        else
        {
          return CodeSoh;
        }
      }
      else if(rxBuffer[0] == CodeStx)
      {
        uint32_t len = read(&(rxBuffer[1]), YMODEM_PACKET_1K_SIZE + YMODEM_PACKET_OVERHEAD - 1);

        if(len < (YMODEM_PACKET_1K_SIZE + YMODEM_PACKET_OVERHEAD - 1))
        {
          rxLength = len + 1;
          code     = CodeStx;

          return CodeNone;
        }
        else
        {
          return CodeStx;
        }
      }
      else
      {
        return (Code)(rxBuffer[0]);
      }
    }
    else
    {
      return CodeNone;
    }
  }
  else
  {
    if(code == CodeSoh)
    {
      uint32_t len = read(&(rxBuffer[rxLength]), YMODEM_PACKET_SIZE + YMODEM_PACKET_OVERHEAD - rxLength);

      if(len < (YMODEM_PACKET_SIZE + YMODEM_PACKET_OVERHEAD - rxLength))
      {
        rxLength += len;

        return CodeNone;
      }
      else
      {
        code = CodeNone;

        return CodeSoh;
      }
    }
    else if(code == CodeStx)
    {
      uint32_t len = read(&(rxBuffer[rxLength]), YMODEM_PACKET_1K_SIZE + YMODEM_PACKET_OVERHEAD - rxLength);

      if(len < (YMODEM_PACKET_1K_SIZE + YMODEM_PACKET_OVERHEAD - rxLength))
      {
        rxLength += len;

        return CodeNone;
      }
      else
      {
        code = CodeNone;

        return CodeStx;
      }
    }
    else
    {
      code = CodeNone;

      return CodeNone;
    }
  }
}

/**
  * @brief  Receive none stage.
  * @param  None.
  * @return None.
  */
void Ymodem::receiveStageNone()
{
  timeCount   = 0;
  errorCount  = 0;
  dataCount   = 0;
  code        = CodeNone;
  stage       = StageEstablishing;
  txBuffer[0] = CodeC;
  txLength    = 1;
  write(txBuffer, txLength);
}

/**
  * @brief  Receive establishing stage.
  * @param  None.
  * @return None.
  */
void Ymodem::receiveStageEstablishing()
{
  switch(receivePacket())
  {
    case CodeSoh:
    {
      uint16_t crc = ((uint16_t)(rxBuffer[YMODEM_PACKET_SIZE + YMODEM_PACKET_OVERHEAD - 2]) << 8) |
                     ((uint16_t)(rxBuffer[YMODEM_PACKET_SIZE + YMODEM_PACKET_OVERHEAD - 1]) << 0);

      if((rxBuffer[1] == 0x00) && (rxBuffer[2] == 0xFF) &&
         (crc == crc16(&(rxBuffer[YMODEM_PACKET_HEADER]), YMODEM_PACKET_SIZE)))
      {
        uint32_t dataLength = YMODEM_PACKET_SIZE;

        if(callback(StatusEstablish, &(rxBuffer[YMODEM_PACKET_HEADER]), &dataLength) == CodeAck)
        {
          timeCount   = 0;
          errorCount  = 0;
          dataCount   = 0;
          code        = CodeNone;
          stage       = StageEstablished;
          txBuffer[0] = CodeAck;
          txBuffer[1] = CodeC;
          txLength    = 2;
          write(txBuffer, txLength);
        }
        else
        {
          timeCount  = 0;
          errorCount = 0;
          dataCount  = 0;
          code       = CodeNone;
          stage      = StageNone;

          for(txLength = 0; txLength < YMODEM_CODE_CAN_NUMBER; txLength++)
          {
            txBuffer[txLength] = CodeCan;
          }

          write(txBuffer, txLength);
        }
      }
      else
      {
        errorCount++;

        if(errorCount > errorMax)
        {
          timeCount  = 0;
          errorCount = 0;
          dataCount  = 0;
          code       = CodeNone;
          stage      = StageNone;

          for(txLength = 0; txLength < YMODEM_CODE_CAN_NUMBER; txLength++)
          {
            txBuffer[txLength] = CodeCan;
          }

          write(txBuffer, txLength);
          callback(StatusError, NULL, NULL);
        }
        else
        {
          txBuffer[0] = CodeC;
          txLength    = 1;
          write(txBuffer, txLength);
        }
      }

      break;
    }

    case CodeA1:
    case CodeA2:
    case CodeCan:
    {
      timeCount  = 0;
      errorCount = 0;
      dataCount  = 0;
      code       = CodeNone;
      stage      = StageNone;
      callback(StatusAbort, NULL, NULL);

      break;
    }

    default:
    {
      timeCount++;

      if((timeCount / (timeDivide + 1)) > timeMax)
      {
        timeCount  = 0;
        errorCount = 0;
        dataCount  = 0;
        code       = CodeNone;
        stage      = StageNone;

        for(txLength = 0; txLength < YMODEM_CODE_CAN_NUMBER; txLength++)
        {
          txBuffer[txLength] = CodeCan;
        }

        write(txBuffer, txLength);
        callback(StatusTimeout, NULL, NULL);
      }
      else if((timeCount % (timeDivide + 1)) == 0)
      {
        txBuffer[0] = CodeC;
        txLength    = 1;
        write(txBuffer, txLength);
      }
    }
  }
}

/**
  * @brief  Receive established stage.
  * @param  None.
  * @return None.
  */
void Ymodem::receiveStageEstablished()
{
  switch(receivePacket())
  {
    case CodeSoh:
    {
      uint16_t crc = ((uint16_t)(rxBuffer[YMODEM_PACKET_SIZE + YMODEM_PACKET_OVERHEAD - 2]) << 8) |
                     ((uint16_t)(rxBuffer[YMODEM_PACKET_SIZE + YMODEM_PACKET_OVERHEAD - 1]) << 0);

      if((rxBuffer[1] == 0x00) && (rxBuffer[2] == 0xFF) &&
         (crc == crc16(&(rxBuffer[YMODEM_PACKET_HEADER]), YMODEM_PACKET_SIZE)))
      {
        errorCount++;

        if(errorCount > errorMax)
        {
          timeCount  = 0;
          errorCount = 0;
          dataCount  = 0;
          code       = CodeNone;
          stage      = StageNone;

          for(txLength = 0; txLength < YMODEM_CODE_CAN_NUMBER; txLength++)
          {
            txBuffer[txLength] = CodeCan;
          }

          write(txBuffer, txLength);
          callback(StatusError, NULL, NULL);
        }
        else
        {
          txBuffer[0] = CodeAck;
          txBuffer[1] = CodeC;
          txLength    = 2;
          write(txBuffer, txLength);
        }
      }
      else if((rxBuffer[1] == 0x01) && (rxBuffer[2] == 0xFE) &&
  
  • 18
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
### 回答1: LabVIEW是一种图形化编程环境和开发平台,用于与各种硬件设备和传感器进行交互和控制。Ymodem是一种通信协议,用于在串行通信中传输文件。 在LabVIEW中,可以使用内置的函数和工具箱来实现Ymodem协议的功能。首先,需要通过串行通信接口与外部设备进行通信。可以使用LabVIEW的VISA(Virtual Instrument Software Architecture)函数来建立串行连接和通信通道。 一旦建立了通信连接,就可以使用LabVIEW中的Ymodem协议相关函数来实现文件的传输。可以使用VISA函数读取外部设备发送的Ymodem协议数据包,并将其解析为文件。同时,也可以使用VISA函数将文件数据打包成Ymodem数据包发送给外部设备。 LabVIEW提供了一系列的函数和工具箱,用于处理文件传输过程中可能遇到的错误或异常情况。例如,可以使用相关函数来检测丢失的数据包、修复错误的数据包、实现数据的重传等。 此外,LabVIEW还可以使用其图形化界面来构建用户友好的界面,以便用户可以直观地操作和监控Ymodem文件传输过程。可以通过面板和控件的设计来显示传输进度、传输速度以及传输结果等信息。 总之,LabVIEW提供了丰富的功能和工具来实现Ymodem协议文件传输。使用LabVIEW,可以方便地通过图形化编程来构建实时、高效且稳定的Ymodem协议通信系统。 ### 回答2: LabVIEW是一种图形化编程语言和开发平台,通常用于控制、测量和数据采集等应用。Ymodem协议是一种常用的通信协议,通常用于在计算机之间传输文件。下面我将用300字来解释一下LabVIEW和Ymodem协议。 LabVIEW是由美国国家仪器公司(National Instruments)开发的一种图形化编程语言和开发平台。它采用了数据流图的形式,用户可以通过将不同的功能模块拖拽在一个主程序框图中来设计程序。这些功能模块可以代表不同的传感器、仪器或控制设备,通过连接线将它们连接在一起,形成一个完整的程序。 而Ymodem协议是一种常用的通信协议,它主要用于在计算机之间可靠地传输文件。Ymodem协议通过发送和确认数据包的方式来实现文件传输。发送端将要传输的文件分成一个个固定大小的数据包,并逐个发送给接收端。接收端在接收到一个数据包后,会向发送端发送一个确认信号,表示已经成功接收。如果发送端在规定的时间内没有收到确认信号,就会重新发送该数据包。 在LabVIEW中使用Ymodem协议进行文件传输是非常方便的。LabVIEW提供了丰富的通信库函数和工具箱,用户可以直接调用这些函数来实现Ymodem协议的功能。用户可以通过编写LabVIEW程序来实现发送文件和接收文件的功能。通过使用LabVIEW的图形化编程界面,用户可以直观地设计程序,而不需要编写繁琐的代码。 总之,LabVIEW是一种图形化编程语言和开发平台,用于控制、测量和数据采集等应用。Ymodem协议是一种常用的通信协议,用于可靠地在计算机之间传输文件。在LabVIEW中使用Ymodem协议是非常方便的,只需要调用相应的函数和工具箱即可实现文件传输的功能。 ### 回答3: LabVIEW是一种由National Instruments开发的图形化编程语言和开发环境。而Ymodem协议则是一种文件传输协议,主要用于在计算机之间进行文件传输。 LabVIEW中可以通过使用串口通信模块来实现通过Ymodem协议进行文件传输。首先,需要使用LabVIEW内置的串口通信函数来建立与目标设备之间的连接。然后,可以使用LabVIEW提供的功能来实现Ymodem文件传输方法。 Ymodem协议文件传输主要分为三个阶段:文件名发送、文件数据发送和文件信息确认。在LabVIEW中,可以通过序列的方式逐一发送这些阶段所需的信息。 首先,在文件传输之前,发送端需要先发送文件的名称和大小等信息给接收端。发送端可以通过LabVIEW中的串口通信函数来发送这些文件信息。 接着,在文件数据传输阶段,通过LabVIEW中的循环结构来不断发送数据帧给接收端。可以使用LabVIEW提供的文件读取功能来读取本地文件的数据,然后按照Ymodem协议的格式发送给接收端。 最后,在接收端接收到数据后,需要发送确认信号给发送端以告知已成功接收到数据。在LabVIEW中,可以使用条件语句来判断接收到的数据是否正确,若正确则发送确认信号。而在发送端,使用循环结构来等待接收端的确认信号,确保数据传输的完整性。 总之,通过LabVIEW可以实现Ymodem协议文件传输。利用LabVIEW的串口通信功能和文件处理功能,可以方便地实现在计算机之间进行文件的可靠传输。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值