Ymodem 驱动
Ymodem 驱动

Ymodem 驱动

使用 C 语言编写的 Ymodem 驱动。

发送驱动

头文件如下所示。

/*********************************************************************************************************
* 模块名称: YmodemSender.h
* 摘    要: Ymodem 协议发送方
* 当前版本: 1.0.0
* 作    者: SZLY(COPYRIGHT 2018 - 2020 SZLY. All rights reserved.)
* 完成日期: 2023年11月16日 
* 内    容:
* 注    意:
**********************************************************************************************************
* 取代版本:
* 作    者:
* 完成日期:
* 修改内容:
* 修改文件:
*********************************************************************************************************/
#ifndef _YMODEM_SENDER_H_
#define _YMODEM_SENDER_H_

/*********************************************************************************************************
*                                              包含头文件
*********************************************************************************************************/

/*********************************************************************************************************
*                                              宏定义
*********************************************************************************************************/
//协议中用到的符号
#define YMODEM_SENDER_SOH 0x01 //Ymodem 短帧数据头
#define YMODEM_SENDER_STX 0x02 //Ymodem 长帧数据头
#define YMODEM_SENDER_EOT 0x04 //发送结束
#define YMODEM_SENDER_ACK 0x06 //应答
#define YMODEM_SENDER_NAK 0x15 //非应答
#define YMODEM_SENDER_CAN 0x18 //取消发送
#define YMODEM_SENDER_C   0x43 //请求数据

//状态定义
#define YMODEM_SENDER_STATE_IDLE   0x00 //空闲状态
#define YMODEM_SENDER_STATE_BUSY   0x01 //正在发送
#define YMODEM_SENDER_STATE_FINISH 0x02 //已发送完成
#define YMODEM_SENDER_STATE_CANCEL 0x03 //取消发送
#define YMODEM_SENDER_STATE_ERROR  0x04 //出错

//超时时长配置,单位是 ms
#define YMODEM_SENDER_TIMEOUT      3500

//最大重发次数配置
#define YMODEM_SENDER_MAX_RETRY    10

//错误定义
#define YMODEM_SENDER_INFO_NULL      0x00 //无错误
#define YMODEM_SENDER_INFO_TIMEOUT   0x01 //接收方回应超时
#define YMODEM_SENDER_INFO_RETRY_OUT 0x02 //重新发送次数超过了最大值
#define YMODEM_SENDER_INFO_FINISH    0x03 //传输完成
#define YMODEM_SENDER_INFO_CANCEL    0x04 //检测到用户发起了取消传输

/*********************************************************************************************************
*                                              枚举结构体定义
*********************************************************************************************************/

/*********************************************************************************************************
*                                              API函数声明
*********************************************************************************************************/
void         InitYmodemSender(void);                                                  //初始化
void         YmodemSenderPoll(unsigned int period);                                   //轮询
unsigned int YmodemSenderStart(char* fileName, unsigned char* buf, unsigned int len); //开始传输
unsigned int YmodemSenderGetStaus(void);                                              //获取当前状态
unsigned int YmodemSenderGetAmountOfSended(void);                                     //获取已经发送的数据量
void         YmodemSenderCancel(void);                                                //取消发送

//需要移植的函数
unsigned int YmodemSenderWrite(unsigned char* buf, unsigned int len);                 //发送文件数据
unsigned int YmodemSenderRead(unsigned char* buf, unsigned int len);                  //接收回应
void         YmodemSenderInfo(unsigned int info);                                     //上报信息

#endif

源文件如下所示。

/*********************************************************************************************************
* 模块名称: YmodemSender.c
* 摘    要: Ymodem 协议发送方
* 当前版本: 1.0.0
* 作    者: SZLY(COPYRIGHT 2018 - 2020 SZLY. All rights reserved.)
* 完成日期: 2023年11月16日 
* 内    容: 
* 注    意: 
**********************************************************************************************************
* 取代版本:
* 作    者:
* 完成日期:
* 修改内容:
* 修改文件:
*********************************************************************************************************/

/*********************************************************************************************************
*                                              包含头文件
*********************************************************************************************************/
#include "YmodemSender.h"

/*********************************************************************************************************
*                                              宏定义
*********************************************************************************************************/
#define MAX_STRING_CONVER_LEN 64 //字符串转换最大长度

/*********************************************************************************************************
*                                              枚举结构体定义
*********************************************************************************************************/
//发送状态机定义
typedef enum
{
  STAET_INIT_START,                  //初始化,准备传输
  STATE_WAIT_RECEIVER_REQUEST_START, //等待接收方发起请求起始帧
  STATE_SEND_START_PACK,             //发送起始帧
  STATE_WAIT_START_PACK_ACK,         //等待起始帧的回应
  STATE_WAIT_RECEIVER_REQUEST_DATA,  //等待接收方发起请求数据帧
  STATE_SEND_DATA_PACK,              //发送数据帧
  STATE_WAIT_DATA_PACK_ACK,          //等待数据帧的回应(重发处理在这里完成)
  STATE_SEND_EOT0_PACK,              //第一次发送传输结束
  STATE_WAIT_EOT0_ACK,               //等待 NAK(响应结束命令)回应
  STATE_SEND_EOT1_PACK,              //第二次发送传输结束
  STATE_WAIT_EOT1_ACK,               //等待 ACK 回应
  STATE_WAIT_EOT1_C,                 //等待 C(请求结束帧)回应
  STATE_SEND_END_PACK,               //发送传输结束帧
  STATE_WAIT_END_ACK,                //等待传输结束帧回应
}EnumYmodemState;

/*********************************************************************************************************
*                                              内部变量
*********************************************************************************************************/
//状态
static unsigned int s_iSenderState = YMODEM_SENDER_STATE_IDLE;

//文件信息记录
static char* s_pFileName;
static unsigned char* s_pFileSource;
static unsigned int s_iFileSize;

//已经发送的数据量计数
static unsigned int s_iDataCnt = 0;

//当前数据包记录
static unsigned int s_iPackNumCnt = 0;

//数据包
static unsigned char s_arrCurrentPack[1029];
static unsigned char s_arrCurrentPack2[128];
static unsigned int s_iCurrentPackSize;

//字符串转换缓冲区
static const char s_arrNumTable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
static char s_arrStringBuf[MAX_STRING_CONVER_LEN];

//发送状态机
static EnumYmodemState s_enumYmodemState = STAET_INIT_START;

//发送时需要使用到的静态变量
static unsigned int s_iTimeOutCnt = 0;
static unsigned int s_iRetryCnt = 0;

/*********************************************************************************************************
*                                              内部函数声明
*********************************************************************************************************/
static void           PutDecUint(unsigned int num, unsigned int width);                              //整型转字符串
static unsigned short CalCRC(unsigned char* data, unsigned int size);                                //计算 CRC 校验
static void           SendPack128(unsigned char header, unsigned char oder, unsigned char* buf);     //发送短包
static void           SendPack1024(unsigned char header, unsigned char oder, unsigned char* buf);    //发送长包
static void           SendBeginPack(void);                                                           //发送起始帧
static void           SendEndPack(void);                                                             //发送结束帧
static void           SendFileLastPack(unsigned char oder, unsigned char* buf, unsigned int remain); //发送文件不足 128 字节部分数据
static unsigned int   WaitForAck(unsigned int period);                                               //等待 ACK 回应
static unsigned int   WaitForC(unsigned int period);                                                 //等待 C 回应

/*********************************************************************************************************
*                                              内部函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:PutUint
* 函数功能:整型转字符串
* 输入参数:num:显示数值,width:显示宽度,不足补零
* 输出参数:void
* 返 回 值:void
* 创建日期:2021年07月01日
* 注    意:
*********************************************************************************************************/
void PutDecUint(unsigned int num, unsigned int width)
{
  unsigned int divisor;      //除数1、10、100、1000...
  unsigned int widthDivisor; //显示完整宽度需要的除数大小
  unsigned int needDivisor;  //显示完整数字需要的除数大小
  unsigned int stringCnt;    //字符串计数
  unsigned int digital;      //单个位数据

  //计算显示位宽下需要的除数大小
  widthDivisor = 1;
  while (width)
  {
    width--;
    widthDivisor = widthDivisor * 10;
  }
  widthDivisor = widthDivisor / 10;

  //计算显示完整数字需要的除数大小
  needDivisor = 1;
  while (num >= needDivisor)
  {
    needDivisor = needDivisor * 10;
  }
  needDivisor = needDivisor / 10;

  //确定除数初值
  if (widthDivisor > needDivisor)
  {
    divisor = widthDivisor;
  }
  else
  {
    divisor = needDivisor;
  }

  //字符串转换
  stringCnt = 0;
  while (1)
  {
    //已经到个位了
    if (1 == divisor)
    {
      digital = num;
      s_arrStringBuf[stringCnt++] = s_arrNumTable[digital];
      break;
    }
    else
    {
      digital = num / divisor; //取出高位
      num = num % divisor; //将高位剔除
      divisor = divisor / 10;  //更新除数
      s_arrStringBuf[stringCnt++] = s_arrNumTable[digital]; //数值转ASCII码
    }
  }
  s_arrStringBuf[stringCnt] = 0;
}

/*********************************************************************************************************
* 函数名称: CalCRC
* 函数功能: 计算 CRC 校验
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 16 位 CRC 校验的除数多项式为 X^16 + X^12 + X^5 + 1
*********************************************************************************************************/
static unsigned short UpdateCRC16(unsigned short crcIn, unsigned char byte)
{
  unsigned int crc = crcIn;
  unsigned int in = byte | 0x100;
  do
  {
    crc <<= 1;
    in <<= 1;
    if (in & 0x100)
    {
      ++crc;
    }
    if (crc & 0x10000)
    {
      crc ^= 0x1021;
    }
  } while (!(in & 0x10000));
  return crc & 0xffffu;
}
static unsigned short CalCRC(unsigned char* data, unsigned int size)
{
  unsigned int crc = 0;
  unsigned char* dataEnd = data + size;
  while (data < dataEnd)
  {
    crc = UpdateCRC16(crc, *data++);
  }
  crc = UpdateCRC16(crc, 0);
  crc = UpdateCRC16(crc, 0);
  return crc & 0xffffu;
}

/*********************************************************************************************************
* 函数名称: SendPack128
* 函数功能: 发送短包
* 输入参数: header:帧头
*            oder  :帧序号
*            buf   :数据缓冲区,长度为 128 字节
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 禁止用 s_arrCurrentPack 做为缓冲区
*********************************************************************************************************/
static void SendPack128(unsigned char header, unsigned char oder, unsigned char* buf)
{
  unsigned int i, j;
  unsigned short crc16;

  //设置帧头
  s_arrCurrentPack[0] = header;

  //设置帧序号
  s_arrCurrentPack[1] = oder;

  //设置帧序号的取反
  s_arrCurrentPack[2] = ~oder;

  //拷贝原始数据
  i = 0; j = 3;
  while (i < 128)
  {
    s_arrCurrentPack[j] = buf[i];
    j++; i++;
  }

  //计算校验和
  crc16 = CalCRC(buf, 128);

  //填充 CRC 高位
  s_arrCurrentPack[131] = (crc16 >> 8) & 0xFF;
  s_arrCurrentPack[132] = (crc16 >> 0) & 0xFF;

  //发送
  s_iCurrentPackSize = 133;
  YmodemSenderWrite(s_arrCurrentPack, s_iCurrentPackSize);
}

/*********************************************************************************************************
* 函数名称: SendPack1024
* 函数功能: 发送长包
* 输入参数: header:帧头
*            oder  :帧序号
*            buf   :数据缓冲区,长度为 1024 字节
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 禁止用 s_arrCurrentPack 做为缓冲区
*********************************************************************************************************/
static void SendPack1024(unsigned char header, unsigned char oder, unsigned char* buf)
{
  unsigned int i, j;
  unsigned short crc16;

  //设置帧头
  s_arrCurrentPack[0] = header;

  //设置帧序号
  s_arrCurrentPack[1] = oder;

  //设置帧序号的取反
  s_arrCurrentPack[2] = ~oder;

  //拷贝原始数据
  i = 0; j = 3;
  while (i < 1024)
  {
    s_arrCurrentPack[j] = buf[i];
    j++; i++;
  }

  //计算校验和
  crc16 = CalCRC(buf, 1024);

  //填充 CRC 高位
  s_arrCurrentPack[1027] = (crc16 >> 8) & 0xFF;
  s_arrCurrentPack[1028] = (crc16 >> 0) & 0xFF;

  //发送
  s_iCurrentPackSize = 1029;
  YmodemSenderWrite(s_arrCurrentPack, s_iCurrentPackSize);
}

/*********************************************************************************************************
* 函数名称: SendBeginPack
* 函数功能: 发送起始帧
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意:
*********************************************************************************************************/
static void SendBeginPack(void)
{
  unsigned int i, j;

  //保存文件名
  i = 0; j = 0;
  while (0 != s_pFileName[i]) { s_arrCurrentPack2[j] = s_pFileName[i]; i++; j++; }
  s_arrCurrentPack2[j] = 0;

  //保存文件大小
  PutDecUint(s_iFileSize, 1);
  i = 0; j++;
  while (0 != s_arrStringBuf[i]) { s_arrCurrentPack2[j] = s_arrStringBuf[i]; i++; j++; }
  s_arrCurrentPack2[j] = 0;

  //剩余直接填零
  j++; while (j < 128) { s_arrCurrentPack2[j] = 0; j++; }

  //发送
  SendPack128(YMODEM_SENDER_SOH, 0, s_arrCurrentPack2);
}

/*********************************************************************************************************
* 函数名称: SendEndPack
* 函数功能: 发送结束帧
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意:
*********************************************************************************************************/
static void SendEndPack(void)
{
  unsigned int j;

  //缓冲区填零
  j = 0; while (j < 128) { s_arrCurrentPack2[j] = 0; j++; }

  //发送
  SendPack128(YMODEM_SENDER_SOH, 0, s_arrCurrentPack2);
}

/*********************************************************************************************************
* 函数名称: SendFileLastPack
* 函数功能: 发送文件不足 128 字节部分数据
* 输入参数: order :包序号
*            buf   :缓冲区首地址
*            remain:还剩余的数据量
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意:
*********************************************************************************************************/
static void SendFileLastPack(unsigned char oder, unsigned char* buf, unsigned int remain)
{
  unsigned int i;

  //数据量为零,直接返回
  if(0 == remain) { return; }

  //填充缓冲区
  i = 0;
  while (i < remain)
  {
    s_arrCurrentPack2[i] = buf[i];
    i++;
  }
  
  //剩余部分填充 0x1A
  while (i < 128)
  {
    s_arrCurrentPack2[i] = 0x1A;
    i++;
  }

  //发送
  SendPack128(YMODEM_SENDER_SOH, oder, s_arrCurrentPack2);
}

/*********************************************************************************************************
* 函数名称: WaitForC
* 函数功能: 等待 C 回应
* 输入参数: period:轮询函数周期
* 输出参数: void
* 返 回 值: 0-获取 C 失败,1-获取 C 成功,可以进入下一个步骤
* 创建日期: 2023年11月16日
* 注    意:
*********************************************************************************************************/
static unsigned int WaitForC(unsigned int period)
{
  unsigned char uData;
  if (YmodemSenderRead(&uData, 1))
  {
    if (YMODEM_SENDER_C == uData)
    {
      s_iTimeOutCnt = 0;
      return 1;
    }
    else if (YMODEM_SENDER_CAN == uData)
    {
      s_iSenderState = YMODEM_SENDER_STATE_CANCEL;
      YmodemSenderInfo(YMODEM_SENDER_INFO_CANCEL);
    }
  }
  s_iTimeOutCnt = s_iTimeOutCnt + period;
  if (s_iTimeOutCnt >= YMODEM_SENDER_TIMEOUT)
  {
    s_iSenderState = YMODEM_SENDER_STATE_ERROR;
    YmodemSenderInfo(YMODEM_SENDER_INFO_TIMEOUT);
  }
  return 0;
}

/*********************************************************************************************************
* 函数名称: WaitForAck
* 函数功能: 等待 ACK 回应
* 输入参数: period:轮询函数周期
* 输出参数: void
* 返 回 值: 0-获取 ACK 失败,1-获取 ACK 成功,可以进入下一个步骤
* 创建日期: 2023年11月16日
* 注    意: 
*********************************************************************************************************/
static unsigned int WaitForAck(unsigned int period)
{
  unsigned char uData;
  if (YmodemSenderRead(&uData, 1))
  {
    if (YMODEM_SENDER_ACK == uData) //收到了 ACK
    {
      s_iTimeOutCnt = 0;
      return 1;
    }
    else if (YMODEM_SENDER_NAK == uData) //收到了 NACK
    {
      YmodemSenderWrite(s_arrCurrentPack, s_iCurrentPackSize);
      s_iRetryCnt++;
      if (s_iRetryCnt >= YMODEM_SENDER_MAX_RETRY)
      {
        s_iSenderState = YMODEM_SENDER_STATE_ERROR;
        YmodemSenderInfo(YMODEM_SENDER_INFO_RETRY_OUT);
      }
    }
    else if ((YMODEM_SENDER_CAN == uData) && (STATE_WAIT_END_ACK != s_enumYmodemState)) //收到了 CANCEL
    {
      s_iSenderState = YMODEM_SENDER_STATE_CANCEL;
      YmodemSenderInfo(YMODEM_SENDER_INFO_CANCEL);
    }
  }
  s_iTimeOutCnt = s_iTimeOutCnt + period;
  if (s_iTimeOutCnt >= YMODEM_SENDER_TIMEOUT)
  {
    s_iSenderState = YMODEM_SENDER_STATE_ERROR;
    YmodemSenderInfo(YMODEM_SENDER_INFO_TIMEOUT);
  }
  return 0;
}

/*********************************************************************************************************
*                                              API函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称: InitYmodemSender
* 函数功能: 初始化 Ymodem 发送方
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 
*********************************************************************************************************/
void InitYmodemSender(void)
{
  s_iSenderState = YMODEM_SENDER_STATE_IDLE;
  s_enumYmodemState = STAET_INIT_START;
}

/*********************************************************************************************************
* 函数名称: YmodemSenderPoll
* 函数功能: 轮询
* 输入参数: period:执行周期,例如每隔 10ms 执行一次,period 输入 10,以此类推
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 
*********************************************************************************************************/
void YmodemSenderPoll(unsigned int period)
{
  unsigned char uData;
  unsigned int remain;

  //非忙碌,直接返回
  if(YMODEM_SENDER_STATE_BUSY != s_iSenderState) { return; }

  //准备传输
  if (STAET_INIT_START == s_enumYmodemState)
  {
    while (YmodemSenderRead(&uData, 1)) {}
    s_iTimeOutCnt = 0;
    s_iRetryCnt = 0;
    s_iPackNumCnt = 0;
    s_iDataCnt = 0; 
    s_enumYmodemState = STATE_WAIT_RECEIVER_REQUEST_START;
  }

  //等待接收方发起请求起始帧
  if (STATE_WAIT_RECEIVER_REQUEST_START == s_enumYmodemState)
  {
    if(1 == WaitForC(period)) { s_enumYmodemState = STATE_SEND_START_PACK; }
  }

  //发送起始帧
  if (STATE_SEND_START_PACK == s_enumYmodemState)
  {
    SendBeginPack();
    s_iTimeOutCnt = 0;
    s_iRetryCnt = 0;
    s_enumYmodemState = STATE_WAIT_START_PACK_ACK;
  }

  //等待起始帧的回应
  if (STATE_WAIT_START_PACK_ACK == s_enumYmodemState)
  {
    if (1 == WaitForAck(period)) { s_enumYmodemState = STATE_WAIT_RECEIVER_REQUEST_DATA; }
  }

  //等待接收方发起请求数据帧
  if (STATE_WAIT_RECEIVER_REQUEST_DATA == s_enumYmodemState)
  {
    if (1 == WaitForC(period)) { s_enumYmodemState = STATE_SEND_DATA_PACK; }
  }

  //发送数据帧
  if (STATE_SEND_DATA_PACK == s_enumYmodemState)
  {
    //更新数据包序号
    s_iPackNumCnt++;

    //统计剩余数据量
    remain = s_iFileSize - s_iDataCnt;

    //剩余数据量为零,直接跳转到第一次发送传输结束
    if (0 == remain)
    {
      s_enumYmodemState = STATE_SEND_EOT0_PACK;
      goto STATE_SEND_EOT0_PACK_MARK;
    }

    //数据包长度大于等于 1024
    if (remain >= 1024)
    {
      SendPack1024(YMODEM_SENDER_STX, s_iPackNumCnt, s_pFileSource + s_iDataCnt);
      s_iDataCnt = s_iDataCnt + 1024;
      s_iTimeOutCnt = 0;
      s_iRetryCnt = 0;
      s_enumYmodemState = STATE_WAIT_DATA_PACK_ACK;
    }

    //包长度大于等于 128 小于 1024
    else if (remain >= 128)
    {
      SendPack128(YMODEM_SENDER_SOH, s_iPackNumCnt, s_pFileSource + s_iDataCnt);
      s_iDataCnt = s_iDataCnt + 128;
      s_iTimeOutCnt = 0;
      s_iRetryCnt = 0;
      s_enumYmodemState = STATE_WAIT_DATA_PACK_ACK;
    }

    //包长度小于 128
    else
    {
      SendFileLastPack(s_iPackNumCnt, s_pFileSource + s_iDataCnt, remain);
      s_iDataCnt = s_iDataCnt + remain;
      s_iTimeOutCnt = 0;
      s_iRetryCnt = 0;
      s_enumYmodemState = STATE_WAIT_DATA_PACK_ACK;
    }
  }

  //等待数据帧的回应(重发处理在这里完成)
  if (STATE_WAIT_DATA_PACK_ACK == s_enumYmodemState)
  {
    if (1 == WaitForAck(period)) { s_enumYmodemState = STATE_SEND_DATA_PACK; }
  }

  //第一次发送传输结束
STATE_SEND_EOT0_PACK_MARK:
  if (STATE_SEND_EOT0_PACK == s_enumYmodemState)
  {
    uData = YMODEM_SENDER_EOT;
    YmodemSenderWrite(&uData, 1);
    s_iTimeOutCnt = 0;
    s_enumYmodemState = STATE_WAIT_EOT0_ACK;
  }

  //等待 NAK(响应结束命令)回应
  if (STATE_WAIT_EOT0_ACK == s_enumYmodemState)
  {
    if (YmodemSenderRead(&uData, 1))
    {
      if (YMODEM_SENDER_NAK == uData)
      {
        s_iTimeOutCnt = 0;
        s_enumYmodemState = STATE_SEND_EOT1_PACK;
      }
      else if (YMODEM_SENDER_CAN == uData)
      {
        s_iSenderState = YMODEM_SENDER_STATE_CANCEL;
        YmodemSenderInfo(YMODEM_SENDER_INFO_CANCEL);
        return;
      }
    }
    s_iTimeOutCnt = s_iTimeOutCnt + period;
    if (s_iTimeOutCnt >= YMODEM_SENDER_TIMEOUT)
    {
      s_iSenderState = YMODEM_SENDER_STATE_ERROR;
      YmodemSenderInfo(YMODEM_SENDER_INFO_TIMEOUT);
    }
  }

  //第二次发送传输结束
  if (STATE_SEND_EOT1_PACK == s_enumYmodemState)
  {
    uData = YMODEM_SENDER_EOT;
    YmodemSenderWrite(&uData, 1);
    s_iTimeOutCnt = 0;
    s_enumYmodemState = STATE_WAIT_EOT1_ACK;
  }

  //等待 ACK 回应
  if (STATE_WAIT_EOT1_ACK == s_enumYmodemState)
  {
    if (1 == WaitForAck(period)) { s_enumYmodemState = STATE_WAIT_EOT1_C; }
  }

  //等待 C(请求结束帧)回应
  if (STATE_WAIT_EOT1_C == s_enumYmodemState)
  {
    if (YmodemSenderRead(&uData, 1))
    {
      if (YMODEM_SENDER_C == uData)
      {
        s_iTimeOutCnt = 0;
        s_enumYmodemState = STATE_SEND_END_PACK;
      }
      else if (YMODEM_SENDER_CAN == uData)
      {
        s_iSenderState = YMODEM_SENDER_STATE_CANCEL;
        YmodemSenderInfo(YMODEM_SENDER_INFO_CANCEL);
        return;
      }
    }
    s_iTimeOutCnt = s_iTimeOutCnt + period;
    if (s_iTimeOutCnt >= YMODEM_SENDER_TIMEOUT)
    {
      s_iSenderState = YMODEM_SENDER_STATE_ERROR;
      YmodemSenderInfo(YMODEM_SENDER_INFO_TIMEOUT);
    }
  }

  //发送传输结束帧
  if (STATE_SEND_END_PACK == s_enumYmodemState)
  {
    SendEndPack();
    s_iTimeOutCnt = 0;
    s_iRetryCnt = 0;
    s_enumYmodemState = STATE_WAIT_END_ACK;
  }

  //等待传输结束帧回应
  if (STATE_WAIT_END_ACK == s_enumYmodemState)
  {
    if (1 == WaitForAck(period)) 
    { 
      s_enumYmodemState = STAET_INIT_START;
      s_iSenderState = YMODEM_SENDER_STATE_FINISH;
      YmodemSenderInfo(YMODEM_SENDER_INFO_FINISH);
    }
  }
}

/*********************************************************************************************************
* 函数名称: YmodemSenderStart
* 函数功能: 开始发送
* 输入参数: fileName:文件名
*            buf     :文件数据首地址
*            len     :文件大小,单位是字节
* 输出参数: void
* 返 回 值: 0-成功。其它-失败,因为正在发送
* 创建日期: 2023年11月16日
* 注    意: 
*            1、启动发送后,每隔一段时间调用 YmodemSenderPoll 函数
*            2、fileName 和 buf 所指向的内存空间,在整个发送过程中,都不允许发生变动
*********************************************************************************************************/
unsigned int YmodemSenderStart(char* fileName, unsigned char* buf, unsigned int len)
{
  //正在发送,直接返回
  if (YMODEM_SENDER_STATE_BUSY == s_iSenderState)
  {
    return 1;
  }

  //记录文件名
  s_pFileName = fileName;

  //记录文件资源首地址
  s_pFileSource = buf;

  //记录文件大小
  s_iFileSize = len;

  //标记状态为开始发送
  s_iSenderState = YMODEM_SENDER_STATE_BUSY;
  s_enumYmodemState = STAET_INIT_START;

  //成功
  return 0;
}

/*********************************************************************************************************
* 函数名称: YmodemSenderGetStaus
* 函数功能: 获取当前状态
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 
*********************************************************************************************************/
unsigned int YmodemSenderGetStaus(void)
{
  return s_iSenderState;
}

/*********************************************************************************************************
* 函数名称: YmodemSenderGetAmountOfSended
* 函数功能: 获取已经发送的数据量
* 输入参数: void
* 输出参数: void
* 返 回 值: 已经发送的数据量
* 创建日期: 2023年11月16日
* 注    意:
*********************************************************************************************************/
unsigned int YmodemSenderGetAmountOfSended(void)
{
  return s_iDataCnt;
}

/*********************************************************************************************************
* 函数名称: YmodemSenderCancel
* 函数功能: 取消发送
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 
*********************************************************************************************************/
void YmodemSenderCancel(void)
{
  s_iSenderState = YMODEM_SENDER_STATE_IDLE;
}

/*********************************************************************************************************
* 函数名称: YmodemSenderWrite
* 函数功能: 发送文件数据
* 输入参数: buf:缓冲区首地址。len:读写长度
* 输出参数: void
* 返 回 值: 成功读写的数据量
* 创建日期: 2023年11月16日
* 注    意: 
*            1、需要移植
*            2、离开此函数时,默认数据已经发送完毕
*********************************************************************************************************/
unsigned int YmodemSenderWrite(unsigned char* buf, unsigned int len)
{
  unsigned int WriteUART(unsigned char* buf, unsigned int len);
  return WriteUART(buf, len);
}

/*********************************************************************************************************
* 函数名称: YmodemSenderRead
* 函数功能: 接收回应
* 输入参数: buf:缓冲区首地址。len:读写长度
* 输出参数: void
* 返 回 值: 成功读写的数据量
* 创建日期: 2023年11月16日
* 注    意: 
*            1、需要移植
*            2、这里接收的是从机的回应,而不是需要发送的文件的数据
*********************************************************************************************************/
unsigned int YmodemSenderRead(unsigned char* buf, unsigned int len)
{
  unsigned int ReadUART(unsigned char* buf, unsigned int len);
  return ReadUART(buf, len);
}

/*********************************************************************************************************
* 函数名称: YmodemSenderInfo
* 函数功能: 上报信息
* 输入参数: info:信息
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 需要移植
*********************************************************************************************************/
void YmodemSenderInfo(unsigned int info)
{
  void SenderInfo(unsigned int info);
  SenderInfo(info);
}

接收驱动

头文件如下所示。

/*********************************************************************************************************
* 模块名称: YmodemReceiver.h
* 摘    要: Ymodem 协议接收方
* 当前版本: 1.0.0
* 作    者: SZLY(COPYRIGHT 2018 - 2020 SZLY. All rights reserved.)
* 完成日期: 2023年11月16日 
* 内    容:
* 注    意:
**********************************************************************************************************
* 取代版本:
* 作    者:
* 完成日期:
* 修改内容:
* 修改文件:
*********************************************************************************************************/
#ifndef _YMODEM_RECEIVER_H_
#define _YMODEM_RECEIVER_H_

/*********************************************************************************************************
*                                              包含头文件
*********************************************************************************************************/
//协议中用到的符号
#define YMODEM_RECEIVER_SOH    0x01 //Ymodem 短帧数据头
#define YMODEM_RECEIVER_STX    0x02 //Ymodem 长帧数据头
#define YMODEM_RECEIVER_EOT    0x04 //发送结束
#define YMODEM_RECEIVER_ACK    0x06 //应答
#define YMODEM_RECEIVER_NAK    0x15 //非应答
#define YMODEM_RECEIVER_CAN    0x18 //取消发送
#define YMODEM_RECEIVER_C      0x43 //请求数据

//其它包长的帧头
#define YMODEM_RECEIVER_STX_8B   0xA1
#define YMODEM_RECEIVER_STX_16B  0xA2
#define YMODEM_RECEIVER_STX_32B  0xA3
#define YMODEM_RECEIVER_STX_64B  0xA4
#define YMODEM_RECEIVER_STX_128B 0xA5
#define YMODEM_RECEIVER_STX_256B 0xA6
#define YMODEM_RECEIVER_STX_512B 0xA7
#define YMODEM_RECEIVER_STX_1KB  0xA8
#define YMODEM_RECEIVER_STX_2KB  0XA9

//超时时间配置,接收到包头后,超过此时间未收到新的字节则认为有丢包事件
//0 表示不设置超时时间
#define YMODEM_RECEIVER_MAX_TIMEOUT 100

//发送请求数据帧的时间频率,避免发送太多的请求给上位机
//0 表示不设置,以最高速度(轮询函数执行的顺序)发送
#define YMODEM_RECEIVER_REQUEST_TIME 3000

//状态
#define YMODEM_RECEIVER_IDLE   0x00 //空闲状态
#define YMODEM_RECEIVER_BUSY   0x01 //忙碌
#define YMODEM_RECEIVER_FINISH 0x02 //传输完成

//信息
#define YMODEM_RECEIVER_INFO_START  0x01 //收到了起始帧
#define YMODEM_RECEIVER_INFO_FINISH 0x02 //传输完成

/*********************************************************************************************************
*                                              宏定义
*********************************************************************************************************/

/*********************************************************************************************************
*                                              枚举结构体定义
*********************************************************************************************************/

/*********************************************************************************************************
*                                              API函数声明
*********************************************************************************************************/
void         InitYmodemReceiver(void);                //初始化
void         YmodemReceiverPoll(unsigned int period); //轮询
void         YmodemReceiverStart(void);               //开始接收文件
unsigned int YmodemReceiverGetState(void);            //获取状态
char*        YmodemReceiverGetFileName(void);         //获取文件名
unsigned int YmodemReceiverGetFileSize(void);         //获取文件大小
unsigned int YmodemReceiverGetReadSize(void);         //获取已经接收的字节数

//需要移植的函数
unsigned int YmodemReceiverWrite(unsigned char* buf, unsigned int len);                       //发送回应
unsigned int YmodemReceiverRead(unsigned char* buf, unsigned int len);                        //接收数据
void         YmodemReceiverInfo(unsigned int info);                                           //上报信息
void         YmodemReceiverOutput(unsigned int offset, unsigned char* buf, unsigned int len); //数据输出

#endif

源文件如下所示。

/*********************************************************************************************************
* 模块名称: YmodemReceiver.c
* 摘    要: Ymodem 协议接收方
* 当前版本: 1.0.0
* 作    者: SZLY(COPYRIGHT 2018 - 2020 SZLY. All rights reserved.)
* 完成日期: 2023年11月16日 
* 内    容:
* 注    意:
**********************************************************************************************************
* 取代版本:
* 作    者:
* 完成日期:
* 修改内容:
* 修改文件:
*********************************************************************************************************/

/*********************************************************************************************************
*                                              包含头文件
*********************************************************************************************************/
#include "YmodemReceiver.h"

/*********************************************************************************************************
*                                              宏定义
*********************************************************************************************************/
#ifndef NULL
  #define NULL 0
#endif

/*********************************************************************************************************
*                                              枚举结构体定义
*********************************************************************************************************/

/*********************************************************************************************************
*                                              内部变量
*********************************************************************************************************/
//系统状态
static unsigned int s_iReceiverState = YMODEM_RECEIVER_IDLE;

//传输标志位
static unsigned char s_iBeginFlag = 0;
static unsigned char s_iGetStartPackFlag = 0;
static unsigned char s_iInitFlag = 1;
static unsigned char s_iEndCnt;

//数据帧缓冲区
static unsigned char s_arrFrameBuf[2048 + 5];
static unsigned int s_iFrameCnt;

//文件信息
static char s_arrFileName[64];
static unsigned int s_iFileSize;
static unsigned int s_iReadByteCnt;

/*********************************************************************************************************
*                                              内部函数声明
*********************************************************************************************************/
static int            StringToInt(char* string);                      //10 进制字符串转整型
static unsigned short CalCRC(unsigned char* data, unsigned int size); //计算 CRC 校验

/*********************************************************************************************************
*                                              内部函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称: StringToInt
* 函数功能: 10 进制字符串转整型
* 输入参数: string:字符串形式的数字
* 输出参数: void
* 返 回 值: 转换结果
* 创建日期: 2023年02月10日
* 注    意: 不能含有数字、正负号等其它符号
*********************************************************************************************************/
static int StringToInt(char* string)
{
  int sum;
  int negative;
  unsigned int i;
  sum = 0;
  i = 0;
  negative = 0;
  while (('+' == string[i]) || ('-' == string[i]) || ((string[i] >= '0') && (string[i] <= '9')))
  {
    //正号
    if ('+' == string[i])
    {
      negative = 0;
    }

    //负号
    else if ('-' == string[i])
    {
      negative = 1;
    }

    //空格
    else if (' ' == string[i])
    {
      //不做处理
    }

    //数字
    else if ((string[i] >= '0') && (string[i] <= '9'))
    {
      sum = (sum * 10) + (string[i] - '0');
    }

    //循环变量加一
    i++;
  }

  //负号处理
  if (0 != negative)
  {
    sum = -sum;
  }

  //返回转换结果
  return sum;
}

/*********************************************************************************************************
* 函数名称: CalCRC
* 函数功能: 计算 CRC 校验
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 16 位 CRC 校验的除数多项式为 X^16 + X^12 + X^5 + 1
*********************************************************************************************************/
static unsigned short UpdateCRC16(unsigned short crcIn, unsigned char byte)
{
  unsigned int crc = crcIn;
  unsigned int in = byte | 0x100;
  do
  {
    crc <<= 1;
    in <<= 1;
    if(in & 0x100)
    {
      ++crc;
    }
    if(crc & 0x10000)
    {
      crc ^= 0x1021;
    }
  } while(!(in & 0x10000));
  return crc & 0xffffu;
}
static unsigned short CalCRC(unsigned char* data, unsigned int size)
{
  unsigned int crc = 0;
  unsigned char* dataEnd = data+size;
  while(data < dataEnd)
  {
    crc = UpdateCRC16(crc, *data++);
  }
  crc = UpdateCRC16(crc, 0);
  crc = UpdateCRC16(crc, 0);
  return crc & 0xffffu;
}

/*********************************************************************************************************
*                                              API函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称: InitYmodemReceiver
* 函数功能: 初始化
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 
*********************************************************************************************************/
void InitYmodemReceiver(void)
{
  s_iBeginFlag = 0;
  s_iReceiverState = YMODEM_RECEIVER_IDLE;
}

/*********************************************************************************************************
* 函数名称: YmodemReceiverPoll
* 函数功能: 轮询
* 输入参数: period:执行周期,例如每隔 10ms 执行一次,period 输入 10,以此类推
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 推荐 10ms 调用一次
*********************************************************************************************************/
void YmodemReceiverPoll(unsigned int period)
{
#if (YMODEM_RECEIVER_MAX_TIMEOUT | YMODEM_RECEIVER_REQUEST_TIME)
  static unsigned int s_iTimeCnt;
#endif
  static unsigned int s_iPackOrderCnt;
  unsigned char uData;
  unsigned int packLen, i, j, outputNum;
  unsigned short crcInpack, crcCalc;

  //非忙碌状态直接返回
  if(YMODEM_RECEIVER_BUSY != s_iReceiverState) { return; }

  //初始化
  if (0 != s_iInitFlag)
  {
    s_iInitFlag = 0;
    s_iBeginFlag = 0;
    s_iGetStartPackFlag = 1;
    s_iFrameCnt = 0;
    s_iEndCnt = 0;
    s_iReadByteCnt = 0;
#if (YMODEM_RECEIVER_MAX_TIMEOUT | YMODEM_RECEIVER_REQUEST_TIME)
    s_iTimeCnt = 0;
#endif
    s_iPackOrderCnt = 0;
    while (YmodemReceiverRead(&uData, 1)){}
    uData = YMODEM_RECEIVER_C;
    YmodemReceiverWrite(&uData, 1);
  }

  //尚未收到起始帧,连续发送 “C”
  if (0 == s_iBeginFlag)
  {
    if (YmodemReceiverRead(&uData, 1))
    {
      if (YMODEM_RECEIVER_SOH == uData) //开始传输
      {
        s_iBeginFlag = 1;
        goto RECEIVER_READ_LOOP;
      }
    }
#if (YMODEM_RECEIVER_REQUEST_TIME)
    s_iTimeCnt = s_iTimeCnt + period;
    if (s_iTimeCnt >= YMODEM_RECEIVER_REQUEST_TIME)
    {
      s_iTimeCnt = 0;
      uData = YMODEM_RECEIVER_C;
      YmodemReceiverWrite(&uData, 1);
    }
#else
    uData = YMODEM_RECEIVER_C;
    YmodemReceiverWrite(&uData, 1);
#endif
    return;
  }
  
  //获取数据
  while (YmodemReceiverRead(&uData, 1))
  {
RECEIVER_READ_LOOP:

#if (YMODEM_RECEIVER_MAX_TIMEOUT)
    //超时时间清零
    s_iTimeCnt = 0;
#endif

    //接收到结束命令
    if ((0 == s_iFrameCnt) && (YMODEM_RECEIVER_EOT == uData))
    {
      //第一次接收到结束命令,回应 NAK
      if (0 == s_iEndCnt)
      {
        uData = YMODEM_RECEIVER_NAK;
        YmodemReceiverWrite(&uData, 1);
        s_iEndCnt++;
      }

      //第二次接收到结束命令,回应 ACK + C
      else
      {
        uData = YMODEM_RECEIVER_ACK;
        YmodemReceiverWrite(&uData, 1);
        uData = YMODEM_RECEIVER_C;
        YmodemReceiverWrite(&uData, 1);
        s_iEndCnt++;
      }
    }

    //起始帧或正常的数据帧
    else
    {
      //异常的帧头
      if ((0 == s_iFrameCnt) && (YMODEM_RECEIVER_SOH != uData) && (YMODEM_RECEIVER_STX != uData) &&
          (YMODEM_RECEIVER_STX_8B != uData) && (YMODEM_RECEIVER_STX_16B != uData) && (YMODEM_RECEIVER_STX_32B != uData) &&
          (YMODEM_RECEIVER_STX_64B != uData) && (YMODEM_RECEIVER_STX_128B != uData) && (YMODEM_RECEIVER_STX_256B != uData) &&
          (YMODEM_RECEIVER_STX_512B != uData) && (YMODEM_RECEIVER_STX_1KB != uData) && (YMODEM_RECEIVER_STX_2KB != uData))
      {
        s_iFrameCnt = 0;
        //uData = YMODEM_RECEIVER_NAK;
        //YmodemReceiverWrite(&uData, 1);
        return;
      }

      //包序号不对(要排除结束帧的干扰)
      if ((0 == s_iEndCnt) && (1 == s_iFrameCnt))
      {
        if (uData != s_iPackOrderCnt)
        {
          s_iFrameCnt = 0;
          uData = YMODEM_RECEIVER_NAK;
          YmodemReceiverWrite(&uData, 1);
          return;
        }
      }

      //将数据填充到数据包
      if(s_iFrameCnt < 1029) { s_arrFrameBuf[s_iFrameCnt++] = uData; }
      
      //获取数据包长度
      if (YMODEM_RECEIVER_SOH == s_arrFrameBuf[0]) { packLen = 128 + 5; }
      else if (YMODEM_RECEIVER_STX == s_arrFrameBuf[0]) { packLen = 1024 + 5; }
      else if (YMODEM_RECEIVER_STX_8B == s_arrFrameBuf[0]) { packLen = 8 + 5; }
      else if (YMODEM_RECEIVER_STX_16B == s_arrFrameBuf[0]) { packLen = 16 + 5; }
      else if (YMODEM_RECEIVER_STX_32B == s_arrFrameBuf[0]) { packLen = 32 + 5; }
      else if (YMODEM_RECEIVER_STX_64B == s_arrFrameBuf[0]) { packLen = 64 + 5; }
      else if (YMODEM_RECEIVER_STX_128B == s_arrFrameBuf[0]) { packLen = 128 + 5; }
      else if (YMODEM_RECEIVER_STX_256B == s_arrFrameBuf[0]) { packLen = 256 + 5; }
      else if (YMODEM_RECEIVER_STX_512B == s_arrFrameBuf[0]) { packLen = 512 + 5; }
      else if (YMODEM_RECEIVER_STX_1KB == s_arrFrameBuf[0]) { packLen = 1024 + 5; }
      else { packLen = 2048 + 5; }

      //数据包接收完成
      if (s_iFrameCnt >= packLen)
      {
        //帧计数清零
        s_iFrameCnt = 0;

        //计算 CRC 校验
        crcCalc = CalCRC(s_arrFrameBuf + 3, packLen - 5);

        //获取数据包中的 CRC 校验
        crcInpack = s_arrFrameBuf[packLen - 2] & 0xFF; crcInpack = ((crcInpack << 8) & 0xFF00) | (s_arrFrameBuf[packLen - 1] & 0xFF);

        //校验和不同
        if (crcInpack != crcCalc)
        {
          s_iFrameCnt = 0;
          uData = YMODEM_RECEIVER_NAK;
          YmodemReceiverWrite(&uData, 1);
          return;
        }

        //包计数加一
        s_iPackOrderCnt = (s_iPackOrderCnt + 1) % 256;

        //起始帧
        if (0 != s_iGetStartPackFlag)
        {
          //标记起始帧已经被获取到
          s_iGetStartPackFlag = 0;

          //解析文件名
          i = 0; j = 3;
          while (0 != s_arrFrameBuf[j]) { s_arrFileName[i] = s_arrFrameBuf[j]; i++; j++; }
          s_arrFileName[i] = 0;

          //解析文件大小
          while(0 == s_arrFrameBuf[j]) { j++; }
          s_iFileSize = StringToInt((char*)s_arrFrameBuf + j);

          //发送起始帧的回应
          uData = YMODEM_RECEIVER_ACK;
          YmodemReceiverWrite(&uData, 1);

          //请求数据帧
          uData = YMODEM_RECEIVER_C;
          YmodemReceiverWrite(&uData, 1);

          //上报接收到了起始帧
          YmodemReceiverInfo(YMODEM_RECEIVER_INFO_START);
        }

        //结束帧
        else if (0 != s_iEndCnt)
        {
          uData = YMODEM_RECEIVER_ACK;
          YmodemReceiverWrite(&uData, 1);
          uData = YMODEM_RECEIVER_CAN;
          YmodemReceiverWrite(&uData, 1);
          uData = YMODEM_RECEIVER_CAN;
          YmodemReceiverWrite(&uData, 1);
          YmodemReceiverInfo(YMODEM_RECEIVER_INFO_FINISH);
        }

        //数据帧
        else
        {
          //计算需要输出的数据量
          outputNum = s_iFileSize - s_iReadByteCnt;
          if (outputNum >= (packLen - 5))
          {
            outputNum = (packLen - 5);
          }

          //输出
          YmodemReceiverOutput(s_iReadByteCnt, s_arrFrameBuf + 3, outputNum);

          //更新记录
          s_iReadByteCnt = s_iReadByteCnt + outputNum;

          //发送回应信号
          uData = YMODEM_RECEIVER_ACK;
          YmodemReceiverWrite(&uData, 1);
        }
      }
    }
  }

  //超时,请求重新发送。收到帧头之后才开始统计超时
#if (YMODEM_RECEIVER_MAX_TIMEOUT)
  if (0 != s_iFrameCnt)
  {
    s_iTimeCnt = s_iTimeCnt + period;
    if (s_iTimeCnt >= YMODEM_RECEIVER_MAX_TIMEOUT)
    {
      s_iTimeCnt = 0;
      s_iFrameCnt = 0;
      uData = YMODEM_RECEIVER_NAK;
      YmodemReceiverWrite(&uData, 1);
    }
  }
#endif

}

/*********************************************************************************************************
* 函数名称: YmodemReceiverStart
* 函数功能: 开始接收文件
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意:
*********************************************************************************************************/
void YmodemReceiverStart(void)
{
  s_iReceiverState = YMODEM_RECEIVER_BUSY;
  s_iBeginFlag = 0;
  s_iInitFlag = 1;
}

/*********************************************************************************************************
* 函数名称: YmodemReceiverGetState
* 函数功能: 获取状态
* 输入参数: void
* 输出参数: void
* 返 回 值: 当前状态
* 创建日期: 2023年11月16日
* 注    意:
*********************************************************************************************************/
unsigned int YmodemReceiverGetState(void)
{
  return s_iReceiverState;
}

/*********************************************************************************************************
* 函数名称: YmodemReceiverGetFileName
* 函数功能: 获取文件名
* 输入参数: void
* 输出参数: void
* 返 回 值: 文件名
* 创建日期: 2023年11月16日
* 注    意:
*********************************************************************************************************/
char* YmodemReceiverGetFileName(void)
{
  return s_arrFileName;
}

/*********************************************************************************************************
* 函数名称: YmodemReceiverGetFileSize
* 函数功能: 获取文件大小
* 输入参数: void
* 输出参数: void
* 返 回 值: 文件大小,单位是字节
* 创建日期: 2023年11月16日
* 注    意:
*********************************************************************************************************/
unsigned int YmodemReceiverGetFileSize(void)
{
  return s_iFileSize;
}

/*********************************************************************************************************
* 函数名称: YmodemReceiverGetReadSize
* 函数功能: 获取已经接收的字节数
* 输入参数: void
* 输出参数: void
* 返 回 值: 已经接收的字节数
* 创建日期: 2023年11月16日
* 注    意:
*********************************************************************************************************/
unsigned int YmodemReceiverGetReadSize(void)
{
  return s_iReadByteCnt;
}

/*********************************************************************************************************
* 函数名称: YmodemReceiverWrite
* 函数功能: 发送回应
* 输入参数: buf:缓冲区首地址。len:读写长度
* 输出参数: void
* 返 回 值: 成功读写的数据量
* 创建日期: 2023年11月16日
* 注    意:
*            1、需要移植
*            2、离开此函数时,默认数据已经发送完毕
*********************************************************************************************************/
unsigned int YmodemReceiverWrite(unsigned char* buf, unsigned int len)
{
  unsigned int ReceiverWrite(unsigned char* buf, unsigned int len);
  return ReceiverWrite(buf, len);
}

/*********************************************************************************************************
* 函数名称: YmodemReceiverRead
* 函数功能: 接收数据
* 输入参数: buf:缓冲区首地址。len:读写长度
* 输出参数: void
* 返 回 值: 成功读写的数据量
* 创建日期: 2023年11月16日
* 注    意:
*            1、需要移植
*            2、这里接收的是主机的数据包数据
*********************************************************************************************************/
unsigned int YmodemReceiverRead(unsigned char* buf, unsigned int len)
{
  unsigned int ReceiverRead(unsigned char* buf, unsigned int len);
  return ReceiverRead(buf, len);
}

/*********************************************************************************************************
* 函数名称: YmodemReceiverInfo
* 函数功能: 上报信息
* 输入参数: info:信息
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 需要移植
*********************************************************************************************************/
void YmodemReceiverInfo(unsigned int info)
{
  void ReceiverInfo(unsigned int info);
  ReceiverInfo(info);
}

/*********************************************************************************************************
* 函数名称: YmodemReceiverOutput
* 函数功能: 数据输出
* 输入参数: info:信息
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年11月16日
* 注    意: 需要移植
*********************************************************************************************************/
void YmodemReceiverOutput(unsigned int offset, unsigned char* buf, unsigned int len)
{
  void ReceiverOutput(unsigned int offset, unsigned char* buf, unsigned int len);
  ReceiverOutput(offset, buf, len);
}

基于串口传输的 Ymodem 发送小工具

基于上述代码,制作的 Ymodem 发送小工具如下所示。这个小工具可以用于通过串口向单片机发送文件,例如 IAP 升级,实测可以稳定更新 350kB 的程序文件。

第一次运行时,需要输入串口号、波特率以及文件名,注意文件名需要与小工具处于同一文件夹下,即同一路径。配置信息将会被保存到“Config.ini”文件中。后续需要修改串口号、波特率或文件名时,需要以文本文件形式打开“Config.ini”文件,直接修改配置。

小工具如下所示。

源码如下所示。