使用 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”文件,直接修改配置。
小工具如下所示。
源码如下所示。