Ymodem傳輸協議


作者:zzssdd2

E-mail:zzssdd2@foxmail.com

〇、前言

近段時間做的項目涉及到設備固件OTA升級相關工作,其中有用到Ymodem協議傳輸數據,故整理一下Ymodem協議的知識。一是為寫上位機/下位機做准備,二是做個備忘便於以后用時查閱。

一、符號說明

協議中用到的符號及其對應的數值和含義說明

符號 數值 含義
SOH 0x01 128字節數據包
STX 0x02 1024字節數據包
EOT 0x04 結束傳輸
ACK 0x06 正確接收回應
NAK 0x15 錯誤接收回應
CAN 0x18 傳輸中止
C 0x43 請求數據

二、傳輸起始幀

接收方發起傳輸請求后由發送方發送的第一包數據

幀頭 幀序 幀序反碼 文件名 文件大小 NULL CRC-H CRC-L
SOH 00 FF n Bytes m Bytes x Bytes 1 Byte 1 Byte
  • SOH:表示這個數據幀中包含着128個字節的數據段
  • 00:表示數據幀序號,初始是0,依次遞增(滿255從0開始)
  • FF:是幀序號的取反(提供一種數據是否正確的判斷依據)
  • 數據段(128byte):文件名 + 文件大小 + NULL
    • 文件名:例如文件名是foo.c,ACSII字符轉換為HEX字符就是66 6F 6F 2E 63。它在數據幀中存放格式為66 6F 6F 2E 63 00 ,最后加一個00表示文件名字段結束。
    • 文件大小:假如上面的foo.c文件大小為1024字節,轉化成16進制即0x400。它在數據幀中存放格式為34 30 30 00,即ASCII格式的400,最后加一個00表示文件大小字段結束
    • NULL:數據部分大小為128字節,除去文件名與文件大小占用的空間外,剩余的x Bytes全部用00填充
  • CRC-H和CRC-L分別表示16位CRC校驗碼的高8位與低8位,校驗只針對數據段。

三、傳輸數據幀

文件內容數據包

幀頭 幀序 幀序反碼 數據 CRC-H CRC-L
STX/SOH num ~num 1024/128 Bytes 1 Byte 1 Byte
  • STX 表示這幀數據的數據段為1024字節,SOH 表示這幀數據的數據段為128字節

  • 第一包幀序為01,幀序反碼為FE,第二包幀序為02,其反碼為FD......以此類推

  • CRC-H和CRC-L分別表示16位CRC校驗碼的高8位與低8位,校驗只針對數據段。

  • 如果文件最后一幀數據在128~1024之間,則使用STX的1024字節傳輸,但是剩余空間全部用0x1A填充,如下結構:

    -------------------------------------------------------
    | STX | num | ~num | data[ ] | 1A ...1A | CRCH | CRCL |
    -------------------------------------------------------
    
  • 如果文件最后一幀數據小於128字節,則選擇SOH的128字節來傳輸,但是剩余空間全部用0x1A填充,如下結構:

    ------------------------------------------------------
    | SOH | num | ~num | data[ ] | 1A...1A | CRCH | CRCL |
    ------------------------------------------------------
    

四、傳輸結束幀

發送方發送的最后一包數據

幀頭 幀序 幀序反碼 數據 CRC-H CRC-L
SOH 00 FF NUL[128] 00 00
  • 結束幀同樣以SOH開頭,表示后面跟着128字節的數據,結束幀的幀序也認為是00 FF
  • 結束幀的128字節的數據部分不存放任何信息,即NUL[128]全部用00填充
  • 因為數據段全為00,故校驗碼也為00 00

五、傳輸流程

文件的傳輸分為如下幾個階段進行

  • 階段1:發起傳輸請求

接收方給發送方不斷地發送字符'C',以期望收到發送方的數據響應。

接收方->>發送方: Char 'C'
接收方->>發送方: Char 'C'
......
  • 階段2:起始幀的發送及確認

發送方收到接收方發來'C'字符,開始發送起始幀數據。等待接收方響應ACK標記,發送方收到ACK標記后等待接收方發送字符'C'則開始正式傳輸文件內容。

發送方->>接收方: Start packet
接收方->>發送方: ACK
接收方->>發送方: C
  • 階段3:文件內容的傳輸及確認

發送方每發一個文件內容數據包,就期待接收方響應一個ACK標記,以繼續發送下一個包。

發送方->>接收方: Packet 1
接收方->>發送方: ACK
發送方->>接收方: Packet 2
接收方->>發送方: ACK
......
  • 傳輸過程中的異常處理

若發送方發完數據包后收到了接收方NAK標記的響應,則重發此包,直到收到ACK響應或者超時退出。

發送方->>接收方: Packet n
接收方->>發送方: NAK
發送方->>接收方: Packet n
接收方->>發送方: ACK
發送方->>接收方: Packet n+1
......

若發送方發完數據包后收到了接收方CAN標記的響應,則停止數據包發送,結束傳輸。

......
發送方->>接收方: Packet n
接收方->>發送方: ACK
發送方->>接收方: Packet n+1
接收方->>發送方: CAN
中止傳輸
  • 階段4:數據傳輸結束

若發送方已將數據包全部發完,則發送EOT標記等待接收方的NAK響應,當發送方收到NAK后會再次發送EOT等待接收方的C標記來請求結束幀,發送結束幀后收到接收方的ACK標記則表示本次傳輸完成

發送方->>接收方: EOT
接收方->>發送方: NAK
發送方->>接收方: EOT
接收方->>發送方: C
發送方->>接收方: Over packet
接收方->>發送方: ACK
傳輸結束

六、實例說明

假設以foo.c,大小為4196Bytes(16進制為0x1064)的文件作為傳輸對象,則它的傳輸過程如下:

發送方 傳輸方向 接收方
<<<<<<<<<<<<<<<< C(請求起始幀)
SOH 00 FF 66 6F 6F 2E 63 00 31 30 36 34 00 NUL[117] CRCH CRCL >>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<< ACK
<<<<<<<<<<<<<<<< C(請求數據幀)
STX 01 FE data[1024] CRCH CRCL >>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<< ACK
STX 02 FD data[1024] CRCH CRCL >>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<< ACK
STX 03 FC data[1024] CRCH CRCL >>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<< NAK(接收錯誤請求重發)
STX 03 FC data[1024] CRCH CRCL >>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<< ACK
STX 04 FB data[1024] CRCH CRCL >>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<< ACK
SOH 05 FA data[100] 1A[28] CRCH CRCL >>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<< ACK
EOT >>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<< NAK(響應結束命令)
EOT >>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<< C(請求結束幀)
SOH 00 FF NUL[128] CRCH CRCL >>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<< ACK

七、CRC16校驗

數據幀中校驗碼的計算方式——C語言

/******************************************************************************************
*	函	數: CRC16_Xmodem
*	描	述: 計算CRC16-Xmodem
*		    多項式x16+x12+x5+1(0x1021) | 初始值0x0000 | 低位在后,高位在前 | 結果與0x0000異或
*	輸	入: pData : 數據指針
*			ulSize : 數據長度
*	輸	出: CRC16檢驗碼
******************************************************************************************/
uint16_t CRC16_Xmodem(uint8_t *pData, uint32_t ulSize)  
{  
	uint8_t i;
    uint16_t usCRC = 0x0000;  
    
    while (ulSize--)     
    {  
        usCRC ^= (*pData++ << 8);
        for(i = 0; i < 8; i++)  
        {  
            if(usCRC & 0x8000)  
            {
                usCRC = (CRCin << 1) ^ 0x1021;  
            }
            else
            {              
                usCRC = usCRC << 1;
            }
        }  
    }  
    
    return usCRC;  
}  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM