在工作時串口通信的過程中需要傳輸文件,這里就就需要使用通信協議,此時選擇的是Xmodem協議作簡要研究
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協議代碼:
#include "BA_UART_CONFIG.h" #include "BA_XModem.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 static int last_error = 0; void out_buff(unsigned char *buff, int size) { int arg = 0; UART_HANDLER uart = getUartHandler(5); writeSysUart(uart, buff, size, &arg); } struct sysUartWaitArgStruct sysXmodemUartArg = { OS_OPT_PEND_BLOCKING, 1000, NULL, }; int in_buff(unsigned char *buff, int time_out) { int arg = 0; int qSize = 0; int readSize = 0; UART_HANDLER uart = getUartHandler(5); last_error = 0; sysXmodemUartArg.timeout = time_out; if(RETURN_RESULT_ERROR_NOERR == ctrlSysUart(uart, DEVICE_CONTROL_WAIT_EVENT, (UART_ARG)(&sysXmodemUartArg))) { qSize = uart->recvDQ.q.curSize; if(qSize > 0) { readSize = readSysUart(uart, buff, qSize, &arg); } } if(readSize == 0) last_error = 1; return (readSize); } int calcrc(const unsigned char *ptr, int count) { int crc; char i; crc = 0; while (--count >= 0) { crc = crc ^ (int) *ptr++ << 8; i = 8; do { if (crc & 0x8000) crc = crc << 1 ^ 0x1021; else crc = crc << 1; } while (--i); } return (crc); } static int check(int crc, const unsigned char *buf, int sz) { if(crc) { unsigned short crc = calcrc(buf, sz); unsigned short tcrc = (buf[sz]<<8)+buf[sz+1]; if (crc == tcrc) return 1; } else { int i = 0; unsigned char cks = 0; for(i = 0; i < sz; i ++) { cks += buf[i]; } if (cks == buf[sz]) return 1; } return 0; } //recv_buff_size == 384 int xmodemReceive(unsigned char *dest, int destsz) { unsigned char xbuff[140]; int bufsz = 0; int crc = 0; unsigned char trychar = 'C'; unsigned char packetno = 1; int c = 0; int len = 0; int retry = 0; int retrans = MAXRETRANS; int recvSize = 0; for(;;) { for(retry = 0; retry < 16; retry ++) { if(trychar) { xbuff[0] = trychar; out_buff(xbuff, 1); } recvSize = in_buff(xbuff, (DLY_1S)<<1); c = xbuff[0]; if (last_error == 0) { switch(c) { case SOH: bufsz = 128; goto start_recv; case STX: { xbuff[0] = CAN; out_buff(xbuff, 1); } return -1; case EOT: { xbuff[0] = ACK; out_buff(xbuff, 1); } return len; case CAN: in_buff(xbuff, DLY_1S); c = xbuff[0]; if(c == CAN) { { xbuff[0] = ACK; out_buff(xbuff, 1); } return -1; } break; default: break; } } } if (trychar == 'C') { trychar = NAK; continue; } { xbuff[0] = CAN; out_buff(xbuff, 1); out_buff(xbuff, 1); out_buff(xbuff, 1); } return -2; start_recv: if(trychar == 'C') crc = 1; trychar = 0; if(recvSize != (bufsz + (crc ? 1 : 0) + 4)) goto reject; if(xbuff[1] == (unsigned char)(~xbuff[2]) && (xbuff[1] == packetno || xbuff[1] == (unsigned char)packetno - 1) && check(crc, &xbuff[3], bufsz)) { if(xbuff[1] == packetno) { int count = destsz - len; if (count > bufsz) count = bufsz; if (count > 0) { memcpy(&dest[len], &xbuff[3], count); len += count; } packetno ++; retrans = MAXRETRANS+1; } if(-- retrans <= 0) { { xbuff[0] = CAN; out_buff(xbuff, 1); out_buff(xbuff, 1); out_buff(xbuff, 1); } return -3; } { xbuff[0] = ACK; out_buff(xbuff, 1); } continue; } reject: { xbuff[0] = NAK; out_buff(xbuff, 1); } } } //send_buff_size == 140 int xmodemTransmit(unsigned char *src, int srcsz) { unsigned char xbuff[140]; int bufsz = 0; int crc = -1; unsigned char packetno = 1; int i = 0; int c = 0; int len = 0; int retry = 0; for(;;) { for( retry = 0; retry < 16; ++retry) { in_buff(xbuff, (DLY_1S)<<1); c = xbuff[0]; if(last_error == 0) { switch(c) { case 'C': crc = 1; goto start_trans; case NAK: crc = 0; goto start_trans; case CAN: in_buff(xbuff, DLY_1S); c = xbuff[0]; if(c == CAN) { { xbuff[0] = ACK; out_buff(xbuff, 1); } return -1; } break; default: break; } } } { xbuff[0] = CAN; out_buff(xbuff, 1); out_buff(xbuff, 1); out_buff(xbuff, 1); } return -2; for(;;) { start_trans: xbuff[0] = SOH; bufsz = 128; xbuff[1] = packetno; xbuff[2] = ~packetno; c = srcsz - len; if(c > bufsz) c = bufsz; if(c >= 0) { memset(&xbuff[3], 0, bufsz); if (c == 0) { xbuff[3] = CTRLZ; } else { memcpy(&xbuff[3], &src[len], c); if (c < bufsz) xbuff[3 + c] = CTRLZ; } if(crc) { unsigned short ccrc = calcrc(&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 ++) { out_buff(xbuff, bufsz + 4 + (crc ? 1 : 0)); in_buff(xbuff, DLY_1S); c = xbuff[0]; if(last_error == 0) { switch(c) { case ACK: packetno ++; len += bufsz; goto start_trans; case CAN: in_buff(xbuff, DLY_1S); c = xbuff[0]; if(c == CAN) { { xbuff[0] = ACK; out_buff(xbuff, 1); } return -1; } break; case NAK: break; default: break; } } } { xbuff[0] = CAN; out_buff(xbuff, 1); out_buff(xbuff, 1); out_buff(xbuff, 1); } return -4; } else { for(retry = 0; retry < 10; retry ++) { { xbuff[0] = EOT; out_buff(xbuff, 1); } in_buff(xbuff, (DLY_1S)<<1); c = xbuff[0]; if(c == ACK) break; } return ((c == ACK) ? len : -5); } } } }