Ymodem协议-接收
网上很多关于Ymodem协议的,有部分是错误的,以下是经过本人亲自编写的程序,测试可用。目前单片机作为接受端,用于IAP升级,发送还没写。另外对于终止传送也还没写,正常情况接收是完全没问题的。
划重点:《重复发送1024字节直到收到EOT开头数据,网上的错误点就在这里,搞了我很久,网上说的是不够128凑齐发SOH包,其实不是,是凑齐1024发STX包直到发EOT。》
平台:stm32f101v8 超级终端 keil
传输起来,效果是这样的!一般很快 如下图 2秒就7K了
首先超级终端中Ymodem协议是这样的,定义单片机位接受者,超级终端为发送者:
SOH开头: 128字节的数据,总包大小128+5,SOH即0x01
STX开头: 1024字节的数据,总包大小1024+5 ,STX即0x02
EOT开头: 单个字节,传输完成,总包大小1 ,EOT即0x04
/*YModem standard CMD*/
#define YMODEM_SOH (0x01)
#define YMODEM_STX (0x02)
#define YMODEM_EOT (0x04)
#define YMODEM_ACK (0x06)
#define YMODEM_NAK (0x15)
#define YMODEM_CAN (0x18)
#define YMODEM_C (0x43)
以下为交流顺序:
接受者:发送大写字母C,等待数据
发送者:发送ASCII中的SOH也就是01,后面是包序号00,包序号反码FF,文件名YG06_V0_1.bin,NULL,文件大小11212 bytes ,补齐111字节至128字节,CRC CRC是数据段的16位CRC校验码。总共此包是128+5=133字节。SOH
我的第一个包收到如下: 01 00 FF "YG06_V0_1.bin" NULL "11212" NULL[111] CRC CRC
接受者:发送大写字母ACK,发送大写字母C,等待数据
发送者:发送ASCII中的STX也就是02,后面是包序号01,包序号反码FE,1024字节数据,CRC CRC.总共此包是1024+5=1029字节。STX
我的第一个包收到如下: 02 01 FE DATA[1024] CRC CRC
接受者:ACK
发送者:STX包
接受者:ACK
发送者:STX包
.......
接受者:ACK
发送者:EOT
重复发送1024字节直到收到EOT开头数据,网上的错误点就在这里,搞了我很久,网上说的是不够128凑齐发SOH包,其实不是,是凑齐1024发STX包直到发EOT。
接受者:NAK ( 第一次收到EOT发NAK )
发送者:EOT
接受者:ACK C( 第二次收到EOT发ACK 和大写 C )
发送者:SOH 00 FF NUL[128] CRC CRC (发送128字节空包)
接受者:ACK ( 传输结束 )、
简明如下:
发送端 接收端
#ifndef _YMODEM_H_ #define _YMODEM_H_ #include "stm32f10x.h" #define MAX_LEN_TO_FLASH (1026) #define MAX_CACHE_LEN (135) typedef struct { u8 Data[MAX_CACHE_LEN]; u16 cnt; }Cache_t; typedef struct { u8 Data[MAX_LEN_TO_FLASH]; u16 cnt; u8 x; }WriteToFlash_t; //extern Cache_t Cache; extern WriteToFlash_t WriteToFlash; /*YModem standard CMD*/ #define YMODEM_SOH (0x01) #define YMODEM_STX (0x02) #define YMODEM_EOT (0x04) #define YMODEM_ACK (0x06) #define YMODEM_NAK (0x15) #define YMODEM_CAN (0x18) #define YMODEM_C (0x43) /*Rcv_CMD define*/ #define IS_NOT_FIRST_PACKET (0x00) #define IS_FIRST_PACKET (0x01) #define IS_NORMAL_FILE_128 (0X02) #define IS_NORMAL_FILE_1024 (0X03) #define IS_END_OF_TRANSMIT (0X04) extern s8 YmodemSendACK(u8 byte); extern void RcvFile(u8 PacketReturn,u8 * IsFileEnd,u16* systickmark); extern s8 YmodemRcvPacket(u8 *rcvbuf,u16 buflen,u8 *result); u16 YModemCRC(u8 * data,u16 len); #endif
ymodem.c
#include "ymodem.h" #include "base_conf.h" #include "uart_dma.h" #include "crypt.h" #include "sysflash.h" #include "main.h" Cache_t Cache; WriteToFlash_t WriteToFlash; static u8 EOT_Flag = 0; s8 YmodemSendACK(u8 byte) { UART_PutChar(UART_DEBUG,byte); //Delay_ms(100); return 0; } void RcvFile(u8 PacketReturn,u8 * IsFileEnd,u16 * systickmark) { static u8 i=0; switch(PacketReturn) { case IS_FIRST_PACKET: { YmodemSendACK(YMODEM_ACK); YmodemSendACK(YMODEM_C); *systickmark = GetCurrentTime(); break; } case IS_NORMAL_FILE_128: { memcpy(&WriteToFlash.Data[i*128],Cache.Data,Cache.cnt); WriteToFlash.cnt += 128; i++; if(i==7)//write flash { i=0; Flash_Program_bytes(Page(WriteToFlash.x+APP_FLASH_OFFSET/1024),WriteToFlash.Data,WriteToFlash.cnt); WriteToFlash.x++; } YmodemSendACK(YMODEM_ACK); *systickmark = GetCurrentTime(); break; } case IS_NORMAL_FILE_1024: { Flash_Program_bytes((Page(WriteToFlash.x+APP_FLASH_OFFSET/1024)),WriteToFlash.Data,WriteToFlash.cnt); WriteToFlash.x++; memset(WriteToFlash.Data,0,WriteToFlash.cnt); YmodemSendACK(YMODEM_ACK); *systickmark = GetCurrentTime(); break ; } case IS_END_OF_TRANSMIT: { if(EOT_Flag==1) { //Flash_Program_bytes(Page(WriteToFlash.x+APP_FLASH_OFFSET/1024),WriteToFlash.Data,WriteToFlash.cnt); YmodemSendACK(YMODEM_NAK); } if(EOT_Flag==2) { YmodemSendACK(YMODEM_ACK); YmodemSendACK(YMODEM_C); } if(EOT_Flag==3) { Delay_ms(10); YmodemSendACK(YMODEM_ACK); EOT_Flag=0; *IsFileEnd =1; } break; } } } s8 YmodemRcvPacket(u8 *rcvbuf,u16 buflen,u8 *result) { u16 crc; u8 debug; u8 debug1; switch(*(rcvbuf)) { case YMODEM_EOT: { EOT_Flag++; *result =IS_END_OF_TRANSMIT ; break; } case YMODEM_SOH: { debug=((~(*(rcvbuf+1)))); debug1=((*(rcvbuf+2))); if(debug!=debug1)return -1; memset(Cache.Data,0,sizeof(Cache.Data)); Cache.cnt = 128; memcpy(Cache.Data,(rcvbuf+3),Cache.cnt); crc=*(rcvbuf+128+3)<<8 | *(rcvbuf+128+3+1); if(YModemCRC(Cache.Data,Cache.cnt)!=crc)return -1; if(*(rcvbuf+1)==0) { if(strstr((const char *)(Cache.Data),"YG06_V0_1.bin")!=NULL) {*result=IS_FIRST_PACKET;} else{ if(*result == IS_END_OF_TRANSMIT ) //end with some of file,NULL DATA { *result =IS_END_OF_TRANSMIT ;EOT_Flag++; } else {return -1;}//not the end of transmit,wrong file name } } else { *result =IS_NORMAL_FILE_128 ; } break; } case YMODEM_STX: { debug=((~(*(rcvbuf+1)))); debug1=((*(rcvbuf+2))); if(debug!=debug1)return -1; WriteToFlash.cnt = 1024; memcpy(WriteToFlash.Data,(rcvbuf+3),WriteToFlash.cnt); crc=*(rcvbuf+1024+3)<<8 | *(rcvbuf+1024+3+1); if(YModemCRC(WriteToFlash.Data,WriteToFlash.cnt)!=crc)return -1; *result =IS_NORMAL_FILE_1024 ; break; } default: { break; } } return 0; } u16 YModemCRC(u8 * data,u16 len) { int crc =0; int i,j; for(i=0;i<len;i++) { crc=crc^(data[i]<<8); for(j=0;j<8;j++) { if((crc &((int)0x8000))!=0) { crc = ((crc<<1) ^ 0x1021); } else { crc = crc << 1; } } } return (crc&0xFFFF); }