ZYNQ:移植ymodem协议传输文件
> SDK: V2014.4
协议: Ymodem
工具: USB转UART转接线、xshell6软件
可实现各种文件传输,大小不限,只是速度很慢
参考原代码如下:
/*******************************************************************************
* @函数名称 YmodemRecvData
* @函数说明 使用Ymodem协议接收数据
* @输入参数 data :数据
size :长度
* @输出参数 无
* @返回参数 数据包的总大小
*******************************************************************************/
int YmodemRecvData(void)
{
int i=0;
char ErrorNum=0; //错误计数
char YmodemState=0;
unsigned char TempChar=0; //接收数据
int crcvalue=0;
int len=0;
int count=0;
ErrorNum=0;
while(1)
{
switch(YmodemState)
{
case 0: //通信起始阶段
SerialPutChar(CRC16); //发起始信号
if(GetKeyC(1000000,&TempChar)==0) //每次等待0.2s钟,发生超时重发“C”
{
if(TempChar==SOH )
{
GetKey(&TempChar);
if(TempChar!=0x00)return ; //不是00序号。
GetKey(&TempChar);
if ( TempChar != 0xFF )return ; //不是00序号补码。
for ( i=0; i<128; i++ )
{ //接收数据包0,共128字节
GetKey((UserBuf+i));
}
GetKey(&TempChar);
GetKey(&TempChar); //丢弃CRC校验,暂时不想实现。
SerialPutChar(ACK); //发送确认信号。
YmodemState=1; //状态切换到数据传输状态
for ( i=0; i<128; i++ )
{ //接收数据包0,共128字节
file_name[i]=0;
}
for ( i=0; (i<128)&&(UserBuf[i]); i++ )
{ //接收数据包0,共128字节
file_name[i]=UserBuf[i];
}
Str2Int((UserBuf+i+1),&PackLen);
SerialPutChar (CRC16); //再发一个C,正式启动数据传输
}
}
break;
case 1: //数据传输阶段
GetKey(&TempChar);
switch(TempChar)
{
case EOT: YmodemState = EOT;
SerialPutChar ( ACK );
continue;
case SOH:
len = 128;
break;
case STX:
len = 1024;
break;
case ABORT1:
Serial_PutString("用户取消文件传输!\r\n");
return PackLen;
case ABORT2:
Serial_PutString("用户取消文件传输!\r\n");
return PackLen;
default :return PackLen;
} //end of switch (StartChar)
GetKey(&TempChar);
GetKey(&TempChar);
for(i=0;i<len;i++)
{ //接收整个数据包
GetKey((UserBuf+i));
}
//CRC桥验
GetKey(&TempChar);
crcvalue=TempChar;
crcvalue<<=8;
GetKey(&TempChar);
crcvalue|=TempChar;
if(Cal_CRC16(UserBuf,len)!=crcvalue)
{
ErrorNum+=1;
}
if(ErrorNum==0)
{
SerialPutChar(ACK);
count+=len;
}
else SerialPutChar(NAK); //接收发现错误,要求重发。
break;
case EOT: //结束传输阶段
SerialPutChar(CRC16);
GetKey(&TempChar); //接收起始字符。
if(TempChar==SOH)
{
GetKey(&TempChar);
if(TempChar!=0x00)return PackLen; //不是00序号。
GetKey(&TempChar);
if(TempChar!=0xFF)return PackLen; //不是00序号补码。
for(i=0;i<128;i++)
{
GetKey((UserBuf+i));
}
//CRC桥验
GetKey(&TempChar);
crcvalue=TempChar;
crcvalue<<=8;
GetKey(&TempChar);
crcvalue|=TempChar;
if(Cal_CRC16(UserBuf,128)!=crcvalue)
{
ErrorNum+=1;
}
if(ErrorNum==0)
{
SerialPutChar(ACK);
return PackLen;
}
else SerialPutChar(NAK); //接收发现错误,要求重发。
break;
}
default:
return PackLen;
}
}
return PackLen;
}
结合ZYNQ代码改写如下函数:
void SerialPutChar(uint8_t c)
{
volatile int i = 0;
XUartPs_SendByte(STDOUT_BASEADDRESS, c);
for(i=0; i<1500; i++)
{
;
}
}
unsigned char GetKeyC(unsigned int count,unsigned char *key)
{
volatile int i = 0;
volatile unsigned long temp;
for(i=0;i<count;i++)
{
}
for(i=0;i<1000;i++)
{
temp = MyUart_RecvByte();
if( (temp &0xFFFFFF00) == 0 )
{
*key = (unsigned char)(temp & 0x000000FF);
return 0;
}
}
return 1;
}
uint8_t GetKey(uint8_t *key)
{
volatile unsigned long temp;
volatile int i ;
for(i=0; i<1000;i++)
{
temp = MyUart_RecvByte();
if( (temp &0xFFFFFF00) == 0 )
{
*key = (unsigned char)(temp & 0x000000FF);
return (*key);
}
}
return 0;
}
可以通过Ymodem正确传输完成文件,但还存在的问题如下:
1、第一包数据:STX 00 FF .... 的CRC16校验码会变为0xFF或其他不对的值,导致校验失败。而除了第一包后面的校验都是正确的。
2、用xshell6才能正确传输,用超级终端或者secureCRT都会有几包错误数据。且每次都是错的那几包。
3、传输到一半点击取消后,代码不再运行,需要重启。
4、文件传输完成界面不动,必须按下回车键,才会结束传输过程。