• 正文
  • 相關推薦
申請入駐 產業(yè)圖譜

常見image格式(hex/s19)如何校驗?且看Checksum

2020/02/03
193
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

今天痞子衡繼續(xù)給大家介紹針對 packet 校驗的最簡單的校驗法 - 即和校驗法。

一、和校驗法基本原理

1.1 校驗依據

  

和校驗法的校驗依據就是判斷一次傳輸的 n bytes 組成的 packet 的所有 byte 累加和結果(僅截取低 byte)在傳輸前后是否一致。

1.2 和校驗位

  

為了實現(xiàn)和校驗,通常會在傳輸的這組 n bytes 數據最后插入一個額外的和校驗字節(jié)(byte),用它來記錄這組數據累加和的低 byte 結果。

1.3 校驗方法

  

前面講到和校驗位實際上是 n bytes 數據包的累加和,那么一包數據的長度 n 到底怎么確定呢?為了確定 n,我們通常會在一包數據開始的時候額外插入一個信息位標明當前數據包長度。
  

在實際應用中,數據包是一包一包連續(xù)發(fā)送的,如果傳輸過程中發(fā)生數據丟失,則會引起數據包的錯位導致接下來一連串數據包的解析錯誤,如何及時發(fā)現(xiàn)數據包錯位呢?我們通常還會在數據包最開始的時候再額外插入一個信息位標明一包數據的開始,這個信息位也叫作起始標志字節(jié)。
  

所以最終完整的數據包變成如下格式:

起始標志字節(jié)(1 byte)    長度字節(jié)(1-2 byte)    原始數據位(n bytes)    和校驗字節(jié)(1 byte)
  

有了上述前導信息位,我們便可以準確找到一包數據中的原始數據位進行累加計算得出和,然后與數據包中的校驗和字節(jié)進行比較驗證當前包數據的正確性。
  

需要注意的是,對于校驗和字節(jié),有時候并不一定是數據位所有字節(jié)之和結果的原碼,也有可能是反碼或補碼(關于三者區(qū)別,請參考痞子衡另一篇文章《整數在計算機中的表示》),需要結合不同校驗和應用標準區(qū)別對待,否則會導致驗證結果有誤。

1.4 C 代碼實現(xiàn)

實際中校驗和字節(jié)為數據之和 byte 結果(認定被截斷的 bit9 為 1)的補碼應用較多,因為在驗證數據包時,直接將所有數據連同校驗和字節(jié)直接相加得到 byte 結果為 0,即表示數據包正確。此處示例代碼以補碼校驗和為例:

安裝包:codeblocks-17.12mingw-setup.exe

集成環(huán)境:CodeBlocks 17.12 rev 11256

編譯器:GNU GCC 5.1.0

調試器:GNU gdb (GDB) 7.9.1

// checksum.c
//////////////////////////////////////////////////////////
#include <stdint.h>

enum _packet_constants
{
    kPacketStartByte = 0x5a
};

#pragma pack(1)
typedef struct _packet_header
{
    uint8_t startByte;
    uint8_t length;
} packet_header_t;
#pragma pack()

/*!
 * @brief 計算數據塊的 checksum(補碼)
 *
 * @param src, 待處理的數據塊 .
 * @param lenInBytes, 待處理的數據塊長度 .
 */
uint8_t get_checksum(uint8_t *src,
                     uint32_t lenInBytes)
{
    uint8_t checksum = 0;
    // 計算數據和,丟棄高 bytes
    while (lenInBytes--)
    {
        checksum += *src++;
    }
    // 轉換為補碼
    checksum = (~checksum) + 1;
    return checksum;
}

/*!
 * @brief 驗證數據包的 checksum
 *
 * @param src, 待處理的數據包 .
 * @retval 0, 數據包 checksum 校驗正確 .
 * @retval 1, 數據包起始標志字節(jié)錯誤 .
 * @retval 2, 數據包 checksum 校驗錯誤 .
 */
int32_t verify_packet(uint8_t *src)
{
    uint8_t sum = 0;
    packet_header_t *header = (packet_header_t *)src;
    // 校驗數據包頭
    if (header->startByte != kPacketStartByte)
    {
        return 1;
    }
    // 求所有數據及校驗字節(jié)之和
    for (uint32_t i = 0; i < header->length; i++)
    {
        sum += *(src + sizeof(packet_header_t) + i);
    }
    // 結果為非 0,則 checksum 錯誤
    if (sum)
    {
        return 2;
    }

    return 0;
}

// main.c
//////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include "checksum.h"

int main(void)
{
    uint8_t packet[16];
    packet_header_t *header = (packet_header_t *)packet;
    // 填充包頭
    header->startByte = kPacketStartByte;
    header->length = sizeof(packet) - sizeof(packet_header_t);
    // 填充數據
    for (uint32_t i = sizeof(packet_header_t); i < header->length - 1; i++)
    {
        packet[i] = rand();
    }
    // 填充 checksum
    packet[sizeof(packet) - 1] = get_checksum(&packet[sizeof(packet_header_t)], header->length - 1);
    // 顯示 packet
    for (uint32_t i = 0; i < sizeof(packet); i++)
    {
        printf("packet[%d] = 0x%xn", i, packet[i]);
    }

    // 校驗 checksum
    int32_t res = verify_packet(packet);
    printf("check res = %dn", res);

    return 0;
}

1.5 行業(yè)應用

和校驗由于實現(xiàn)簡單,檢錯性能也算理想,因此應用十分廣泛,就嵌入式而言,比較典型的應用是在各種 image 格式中。做過編程器或者下載器的朋友肯定會比較了解,常用的 image 格式有 hex、s19,這些 image 文件都是由多個數據包組成的,在下載 image 文件時需要對每一包進行和校驗。關于 image 文件詳情,可參考痞子衡的文章《ARM 開發(fā)中 image 文件詳解》。

二、和校驗法失效分析

在數據包傳輸中,如果只是單 byte 發(fā)生 bit 錯誤(無論多少個 bit 錯誤),和校驗法一定能夠識別出錯誤。即使有多個 byte 發(fā)生 bit 出錯,大部分情況下和校驗法也能正常檢出。但和校驗法有如下 3 個明顯的缺陷:

當多個 byte 發(fā)生的 bit 錯誤發(fā)生抵消現(xiàn)象(引起的增量和結果是 0x100 的倍數),無法識別錯誤。

當 packet 中 byte 數據順序發(fā)生調換時,無法識別錯誤。

不能糾錯,在發(fā)現(xiàn)錯誤后,只能要求重發(fā)。
  

和校驗法雖然能夠校驗 packet,且有一定的錯誤 bit 檢測能力,但其是把 packet 當做無序數據包來處理的,有沒有其他比和校驗法更好且能夠校驗數據次序的檢錯方法呢?痞子衡在下篇會繼續(xù)聊。

  

至此,嵌入式里數據差錯控制技術之和校驗痞子衡便介紹完畢了,掌聲在哪里~~~

相關推薦

登錄即可解鎖
  • 海量技術文章
  • 設計資源下載
  • 產業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

碩士畢業(yè)于蘇州大學電子信息學院,目前就職于恩智浦(NXP)半導體MCU系統(tǒng)部門,擔任嵌入式系統(tǒng)應用工程師。痞子衡會定期分享嵌入式相關文章