1、什么是Xmodem协议
Xmodem协议是串口通信中广泛使用到的异步文件传输协议。以128字节块的形式传输数据,并且每个块都使用一个校验过程来进行错误检测。在校验过程中如果接收方关于一个块的检验和与它在发送方的检验相同时,接收方就向发送方发送一个确认字节<ACK>。如果有错则发送一个字节<NAK>要求重发。以保证传输过程中的正确性,但是由于需要对每个块都要进行检验,显得效率比较低。
2、Xmodem协议相关控制字符
SOH 0x01 //Xmodem数据头
STX 0x02 //1K-Xmodem数据头
EOT 0x04 //发送结束
ACK 0x06 //认可响应
NAK 0x15 //不认可响应
CAN 0x18 //撤销传送
CTRLZ 0x1A //填充数据包
3、标准Xmodem协议(每个数据包含有128字节数据)帧格
Xmodem包格式
Byte1 Byte2 Byte3 Byte4~131 Byte132~133
Start Of Header Packet Number ~(Packet Number) Packet Data 16-Bit CRC
Xmodem协议的传输数据单位为信息包,包含一个标题开始字符<SOH>或者<STX>,一个单字节包序号,一个单字节包包序号的补码,128个字节数据和一个双字节的CRC16校验
4、数据包说明
对于标准Xmodem协议来说,如果传送的文件不是128的整数倍,那么最后一个数据包的有效内容肯定小于帧长,不足的部分需要用CTRL-Z(0x1A)来填充
5、如何启动传输
Xmodem协议的传输由接收方启动,接收方向发送方发送"C"或者NAK(这里的NAK是用来启动传输的。下面我们用到的NAK是用来对数据产生重传机制)。其中接收方发送NAK信号表示接收方打算用累加和校验;发送字符"C"则表示接收方打算使用CRC校验。
6、传输过程
当接收方发送的第一个"C"或者NAK到达发送方,发送方认为可以发送第一个数据包了,传输启动。发送方接着接着应该将数据以每次128字节的数据加上包头,包号,包号补码,末尾加上校验和,打包成帧格式传送。发送方发了第一个包后就等待接收方的确认字节<ACK>,收到接收方传来的<ACK>确认,就认为数据包被接收方正确接收,并且接收方要求发送方继续发送下一个包;如果发送方收到接收方传来的<NAK>(这里的表示重发),则表示接收方请求重发刚才的数据包;如果发送方收到接收方传来的<CAN>字节,则表示接收方请求无条件停止传输。
7、结束传输
如果发送方正常传输完全部数据,需要结束传输,正常结束需要发送方发送<EOT>通知接收方。接收方回以<ACK>进行确认。如果接收方发送<CAN>给发送方也可以强制停止传输,发送方受到<CAN>后不需要发送<EOT>确认,此时传输已经结束。
8、Xmodem协议代码:
————————————————
版权声明:本文为CSDN博主「ZhbTy」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_37756916/article/details/76064727
/*
author:[email protected]
*/
/* this code needs standard functions memcpy() and memset()
and input/output functions _inbyte() and _outbyte().
the prototypes of the input/output functions are:
int _inbyte(unsigned short timeout); // msec timeout
void _outbyte(int c);
*/
#include "crc16.h"
#include<stlib.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#define SOH 0x01
#define STX 0x02
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define CAN 0x18
#define CTRLZ 0x1A
#define DLY_1S 1000
#define MAXRETRANS 25
#define TRANSMIT_XMODEM_1K
#define TEST_XMODEM_SEND
static int check(int crc, const unsigned char *buf, int sz)
{
if (crc) {
unsigned short crc = crc16_ccitt(buf, sz);
unsigned short tcrc = (buf[sz]<<8)+buf[sz+1];
if (crc == tcrc)
return 1;
}
else {
int i;
unsigned char cks = 0;
for (i = 0; i < sz; ++i) {
cks += buf[i];
}
if (cks == buf[sz])
return 1;
}
return 0;
}
static void flushinput(void)
{
// while (_inbyte(((DLY_1S)*3)>>1) >= 0)
sleep(3) ;
}
char _inbyte(int daly)
{
char ret=-1,temp;
do
{
usleep(daly);
temp=read(uart_fd,&ret,1);
}while(temp==1 && daly --);
}
int _outbyte(char src)
{
return write(uart_fd,src,1);
}
int _outBuf(char * buf,int bufLen)
{
return write(uart_fd,buf,bufLen);
}
int xmodemTransmit(int fd, FILE * fp)
{
unsigned char xbuff[1030]; /* 1024 for XModem 1k + 3 head chars + 2 crc + nul */
int bufsz, crc = -1;
unsigned char packetno = 1;
int i, c, len = 0;
int retry;
int ret,srcsz;
struct stat st;
ret=fstat(fileno(fp),&st);
if(ret<0)
{
perror("fstat \n");
return -1;
}
if(S_ISDIR(st.mode))
{
return -2;
}
srcsz=st.st_size;
for(;;) {
for( retry = 0; retry < 16; ++retry) {
if ((c = _inbyte((DLY_1S))) >= 0) {
switch (c) {
case 'C':
crc = 1;
goto start_trans;
case NAK:
crc = 0;
goto start_trans;
case CAN:
if ((c = _inbyte(DLY_1S)) == CAN) {
_outbyte(ACK);
flushinput();
return -1; /* canceled by remote */
}
break;
default:
break;
}
}
}
_outbyte(CAN);
_outbyte(CAN);
_outbyte(CAN);
flushinput();
return -2; /* no sync */
for(;;) {
start_trans:
#ifdef TRANSMIT_XMODEM_1K
xbuff[0] = STX; bufsz = 1024;
#else
xbuff[0] = SOH; bufsz = 128;
#endif
xbuff[1] = packetno;
xbuff[2] = ~packetno;
c = srcsz - len;
if (c > bufsz) c = bufsz;
if (c > 0) {
memset (&xbuff[3], 0, bufsz);
// memcpy (&xbuff[3], &src[len], c);
fread(&xbuff[3],c,1,fp);
if (c < bufsz) xbuff[3+c] = CTRLZ;
if (crc) {
unsigned short ccrc = crc16_ccitt(&xbuff[3], bufsz);
xbuff[bufsz+3] = (ccrc>>8) & 0xFF;
xbuff[bufsz+4] = ccrc & 0xFF;
}
else {
unsigned char ccks = 0;
for (i = 3; i < bufsz+3; ++i) {
ccks += xbuff[i];
}
xbuff[bufsz+3] = ccks;
}
for (retry = 0; retry < MAXRETRANS; ++retry) {
for (i = 0; i < bufsz+4+(crc?1:0); ++i) {
//_outbyte(xbuff[i]);
_outBuf(xbuff,bufsz+4+(crc?1:0));
}
if ((c = _inbyte(DLY_1S)) >= 0 ) {
switch (c) {
case ACK:
++packetno;
len += bufsz;
goto start_trans;
case CAN:
if ((c = _inbyte(DLY_1S)) == CAN) {
_outbyte(ACK);
flushinput();
return -1; /* canceled by remote */
}
break;
case NAK:
default:
break;
}
}
}
_outbyte(CAN);
_outbyte(CAN);
_outbyte(CAN);
flushinput();
return -4; /* xmit error */
}
else {
for (retry = 0; retry < 10; ++retry) {
_outbyte(EOT);
if ((c = _inbyte((DLY_1S))) == ACK) break;
}
flushinput();
return (c == ACK)?len:-5;
}
}
}
}
#ifdef TEST_XMODEM_SEND
int main(void * argx,char*argv[])
{
int st;
int fd;FILE *fp;
printf ("Prepare your terminal emulator to receive data now...\n");
/* the following should be changed for your environment:
fp file Handl
fd uart Handl
*/
fp=fopen(argv[1],"rb");
if(fp==null)
{
perror("fopen:\r\n");
}
fd=open_uart(115200);
uart_fd=fd;
st = xmodemTransmit(fd, fp);
if (st < 0) {
printf ("Xmodem transmit error: status: %d\n", st);
}
else {
printf ("Xmodem successfully transmitted %d bytes\n", st);
}
return 0;
}
#endif