作者: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;
}