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