Android 固件升級之 Xmodem

1,簡介
2,傳輸過程
3,代碼實現(xiàn) (我們是使用 android 設(shè)備USB 和主板通訊,當(dāng)然串口也可以.基礎(chǔ)知識自行百度)

Xmodem 協(xié)議簡介:

??在串口通信中廣泛使用的異步文件傳輸協(xié)議有Xmodem,Ymodem,Zmodem.本文只介紹 Xmodem,其他兩種,未用過,沒研究.Xmodem協(xié)議分為兩種,一種是標(biāo)準(zhǔn)的 Xmode和Xmodem-1k 兩個版本.
??Xmodem內(nèi)容固定長度為 128 個字節(jié),格式如下:

Byte0 Byte1 Byte2 Byte3~Byte130 (Byte131~Byte132)/(Byte131)
SOH 數(shù)據(jù)包序號 數(shù)據(jù)包序號補(bǔ)碼 數(shù)據(jù)包內(nèi)容(128字節(jié)) 數(shù)據(jù)校驗

??Xmodem-1k內(nèi)容固定長度為 1024個字節(jié),格式如下:

Byte0 Byte1 Byte2 Byte3~Byte1026 (Byte1027~Byte1028)/(Byte 1027)
STX 數(shù)據(jù)包序號 數(shù)據(jù)包序號補(bǔ)碼 數(shù)據(jù)包內(nèi)容(1024字節(jié)) 數(shù)據(jù)校驗

控制符定義:

控制符 含義
SOH 0X01 Xmodme 數(shù)據(jù)頭
STX 0X02 Xmodme -1k 數(shù)據(jù)頭
EOT 0X04 發(fā)送結(jié)束/數(shù)據(jù)發(fā)送完畢
ACK 0X06 應(yīng)答標(biāo)志/數(shù)據(jù)正確
NAK 0X15 非應(yīng)答標(biāo)志,重傳(使用 CheckSum 數(shù)據(jù)校驗)
CAN 0X18 取消發(fā)送/終止發(fā)送
CRC16 0X43 使用 CRC16 數(shù)據(jù)校驗

數(shù)據(jù)包序號:
??據(jù)包序號下標(biāo)是從0x01開始向上遞增的,累加到0XFF后將循環(huán)反復(fù). (0~255)
數(shù)據(jù)包序號補(bǔ)碼:
??數(shù)據(jù)包序號按位取反,即 ~數(shù)據(jù)包序號
校驗和的方式有CRC校驗和 CheckSum校驗:
??使用不同的校驗方式,占的字節(jié)數(shù)不一樣哦~
??CRC校驗:
????占 2 個字節(jié),,CRC多項式公式為X16+X12+X5+1然后取低16位數(shù)據(jù).(網(wǎng)上說的,我也不理解,沒研究)

    @JvmStatic
    fun calc16(bytes: ByteArray): ByteArray? {
        var crc = 0x0000.toChar()
        for (b in bytes) {
            crc = (crc.toInt() shl 8 xor crctable[crc.toInt() shr 8 xor b.toInt() and 0x00ff].toInt()).toChar()
        }
        return intToByteArray(crc.toInt() and 0x00ffff)
    }

    @JvmStatic
    fun intToByteArray(i: Int): ByteArray? {
        val result = ByteArray(2)
        result[0] = (i shr 8 and 0xFF).toByte()
        result[1] = (i and 0xFF).toByte()
        return result
    }

??CheckSum校驗:
????占1 個字節(jié),對傳輸?shù)臄?shù)據(jù)進(jìn)行累加和,然后轉(zhuǎn)成16 進(jìn)制,用兩個字節(jié)的來表示,取第 1 位.

    @JvmStatic
    fun getCheckSum(bytes: ByteArray): Byte {
        var checkSum = 0
        for (aByte in bytes) {
            checkSum += aByte.toInt()
        }
        val bytes1 = intToByteArray(checkSum.toLong())
        return bytes1!![1]
    }

    //將 int 轉(zhuǎn)成 byte 數(shù)組,文件大小,用兩個字節(jié)表示
    @JvmStatic
    fun intToByteArray(i: Int): ByteArray? {
        val result = ByteArray(2)
        result[0] = (i shr 8 and 0xFF).toByte()
        result[1] = (i and 0xFF).toByte()
        return result
    }

Xmodem 傳輸過程:

android 程序 Flow 主板
<— CRC16 (0X43)/NAK (0X15)
SOH+0x01+0xFE+Data[0-127]+數(shù)據(jù)校驗 —> Packet ok
<— ACK(0X06)
SOH+0x02+ 0xFD+Data[0-127]+數(shù)據(jù)校驗 —> Packet ok
<— ACK(0X06)
SOH+0x03+0xFC+Data[0-127]+數(shù)據(jù)校驗 —> Packet ok
<— ACK(0X06)
.... .... ....
EOT —> Packet ok
Finished <— ACK

1、當(dāng)主板給 Android 設(shè)備發(fā)送的C或者NAK后,android設(shè)備則知道主板已開啟 Xmodem 協(xié)議傳輸,然后開始發(fā)送數(shù)據(jù):SOH/STX+0x01+0xFE+Data[0-127]/Data[0-1024]+數(shù)據(jù)校驗
2、主板接收到數(shù)據(jù)后,對數(shù)據(jù)進(jìn)行校驗,數(shù)據(jù)正確則回復(fù)ACK確認(rèn)字節(jié),android 設(shè)備接收到ACK確認(rèn)后,就認(rèn)為數(shù)據(jù)包被接收方正確接收了,接著發(fā)送下一包數(shù)據(jù).
3、如果android設(shè)備NAK字節(jié),則表示需要重新傳輸剛才的數(shù)據(jù)包;如果android 設(shè)備收到的CAN字節(jié),則表示無條件停止傳輸。
4、傳輸完畢后,等待主板喚醒 android 設(shè)備,判斷固件是否更新成功.(這部分跟主板開發(fā)進(jìn)行協(xié)商吧~~)

Xmodem 代碼實現(xiàn):

隨意寫寫,可自行進(jìn)行封裝~~~

public class Xmodem2 {

    public static int checkType = 1;   //校驗類型 1是csc/2是check-sum,由主板回復(fù)值,判斷賦值
    public static int transType = 2;   //傳輸類型 Xmodem/1K-Xmodem ,android 設(shè)備主動觸發(fā)
    public static final byte SOH = 0x01;    // 開始 Xmodem數(shù)據(jù)頭
    public static final byte STX = 0x02;    // 開始 1K-Xmodem數(shù)據(jù)頭
    public static final byte EOT = 0x04;    // 結(jié)束
    public static final byte ACK = 0x06;    // 應(yīng)答
    public static final byte NAK = 0x15;    // 重傳
    public static final byte CAN = 0x18;     // 無條件結(jié)束
    public static final byte C = 0x43;      // csc校驗
    public static int SECTOR_SIZE = 0;// 以128字節(jié)塊的形式傳輸數(shù)據(jù)
    public static final int MAX_ERRORS = 10; // 最大錯誤(無應(yīng)答)包數(shù)
    public static UsbSerialPort port;   //寫入數(shù)據(jù)
    public static String filePath;    //文件路徑
    public static byte responseACK; //由主板是否回復(fù) ACK,由主板回復(fù)值,判斷賦值
    public static boolean finishAck = false;  //數(shù)據(jù)傳輸結(jié)束ACK
    private final ExecutorService executor = Executors.newFixedThreadPool(5);

    public Xmodem2(String path, UsbSerialPort usbSerialPort) {
        filePath = path;
        port = usbSerialPort;
        if (transType == 1)
            SECTOR_SIZE = 128;
        else
            SECTOR_SIZE = 1024;

    }

    public void send() {
        executor.submit(() -> {
            try {
                int errorCount;// 錯誤包數(shù)
                byte blockNumber = 0x01;// 包序號
                int nbytes; // 讀取到緩沖區(qū)的字節(jié)數(shù)量
                byte[] sector = new byte[SECTOR_SIZE];  // 初始化數(shù)據(jù)緩沖區(qū)
                // 讀取文件初始化
                DataInputStream inputStream = new DataInputStream(new FileInputStream(filePath));
                while ((nbytes = inputStream.read(sector)) > 0) {
                    // 如果最后一包數(shù)據(jù)小于128個字節(jié),以0xff補(bǔ)齊
                    if (nbytes < SECTOR_SIZE) {
                        for (int i = nbytes; i < SECTOR_SIZE; i++) {
                            sector[i] = (byte) 0X1A;
                        }
                    }

                    // 同一包數(shù)據(jù)最多發(fā)送10次
                    errorCount = 0;
                    while (errorCount < MAX_ERRORS) {
                        // 組包
                        // 控制字符1 + 包序號1 + 包序號的反碼1 + 數(shù)據(jù)128 + 校驗 1->crc/2-> check-sum
                        byte[] sendData = new byte[SECTOR_SIZE + (checkType == 1 ? 5 : 4)];
                        sendData[0] = SECTOR_SIZE == 128 ? SOH : STX;
                        sendData[1] = blockNumber;
                        sendData[2] = (byte) ~blockNumber;
                        System.arraycopy(sector, 0, sendData, 3, sector.length);
                        if (checkType == 1) {   //CRC
                            byte[] crc16 = CRC16.calc16(sector);
                            if (transType == 1) { 
                                System.arraycopy(crc16, 0, sendData, 131, crc16.length);    //C+128+ CRC
                            } else {
                                System.arraycopy(crc16, 0, sendData, 1027, crc16.length); //C+1014+ CRC
                            }
                        } else {  // checkSum
                            if (transType == 1) {
                                sendData[131] = CRC16.getCheckSum(sector);         //15+128+ Check-Sum
                            } else {
                                sendData[1027] = CRC16.getCheckSum(sector);    //15+1014+ Check-Sum
                            }
                        }

                        //==============================================
                        String s = Utils.INSTANCE.toHexString(sendData);
                        Log.e("11111", "傳輸?shù)臄?shù)據(jù): " + s);
                        //===============================================
                        port.write(sendData, 5000);
                        // 獲取應(yīng)答數(shù)據(jù)
                        // 如果收到應(yīng)答數(shù)據(jù)則跳出循環(huán),發(fā)送下一包數(shù)據(jù)
                        // 未收到應(yīng)答,錯誤包數(shù)+1,繼續(xù)重發(fā)
                        Thread.sleep(50);
                        if (responseACK == ACK) {
                            responseACK = 0;
                            break;
                        } else {
                            errorCount++;
                        }
                    }
                    // 包序號自增
                    blockNumber = (byte) ((++blockNumber) % 256);
                }
                // 所有數(shù)據(jù)發(fā)送完成后,發(fā)送結(jié)束標(biāo)識
                errorCount = 0;
                while (errorCount < MAX_ERRORS) {
                    Log.e("11111", "發(fā)送結(jié)束標(biāo)志啦");
                    port.write(new byte[]{EOT}, 5000);
                    Thread.sleep(50);
                    if (responseACK == ACK) {
                        finishAck = true;
                        break;
                    } else {
                        errorCount++;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
}

使用:

  Xmodem2(path, UsbHelper.INSTANCE.port).send()

---------------------------END-------------------------

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容