zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Xmodem通信协议实例

实例 通信协议
2023-09-27 14:21:53 时间

在工作时串口通信的过程中需要传输文件,这里就就需要使用通信协议,此时选择的是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);  
            }  
        }  
    }  
}