加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

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

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

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

一、和校驗(yàn)法基本原理

1.1 校驗(yàn)依據(jù)

  

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

1.2 和校驗(yàn)位

  

為了實(shí)現(xiàn)和校驗(yàn),通常會(huì)在傳輸?shù)倪@組 n bytes 數(shù)據(jù)最后插入一個(gè)額外的和校驗(yàn)字節(jié)(byte),用它來(lái)記錄這組數(shù)據(jù)累加和的低 byte 結(jié)果。

1.3 校驗(yàn)方法

  

前面講到和校驗(yàn)位實(shí)際上是 n bytes 數(shù)據(jù)包的累加和,那么一包數(shù)據(jù)的長(zhǎng)度 n 到底怎么確定呢?為了確定 n,我們通常會(huì)在一包數(shù)據(jù)開(kāi)始的時(shí)候額外插入一個(gè)信息位標(biāo)明當(dāng)前數(shù)據(jù)包長(zhǎng)度。
  

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

所以最終完整的數(shù)據(jù)包變成如下格式:

起始標(biāo)志字節(jié)(1 byte)    長(zhǎng)度字節(jié)(1-2 byte)    原始數(shù)據(jù)位(n bytes)    和校驗(yàn)字節(jié)(1 byte)
  

有了上述前導(dǎo)信息位,我們便可以準(zhǔn)確找到一包數(shù)據(jù)中的原始數(shù)據(jù)位進(jìn)行累加計(jì)算得出和,然后與數(shù)據(jù)包中的校驗(yàn)和字節(jié)進(jìn)行比較驗(yàn)證當(dāng)前包數(shù)據(jù)的正確性。
  

需要注意的是,對(duì)于校驗(yàn)和字節(jié),有時(shí)候并不一定是數(shù)據(jù)位所有字節(jié)之和結(jié)果的原碼,也有可能是反碼或補(bǔ)碼(關(guān)于三者區(qū)別,請(qǐng)參考痞子衡另一篇文章《整數(shù)在計(jì)算機(jī)中的表示》),需要結(jié)合不同校驗(yàn)和應(yīng)用標(biāo)準(zhǔn)區(qū)別對(duì)待,否則會(huì)導(dǎo)致驗(yàn)證結(jié)果有誤。

1.4 C 代碼實(shí)現(xiàn)

實(shí)際中校驗(yàn)和字節(jié)為數(shù)據(jù)之和 byte 結(jié)果(認(rèn)定被截?cái)嗟?bit9 為 1)的補(bǔ)碼應(yīng)用較多,因?yàn)樵隍?yàn)證數(shù)據(jù)包時(shí),直接將所有數(shù)據(jù)連同校驗(yàn)和字節(jié)直接相加得到 byte 結(jié)果為 0,即表示數(shù)據(jù)包正確。此處示例代碼以補(bǔ)碼校驗(yàn)和為例:

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

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

編譯器:GNU GCC 5.1.0

調(diào)試器: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 計(jì)算數(shù)據(jù)塊的 checksum(補(bǔ)碼)
 *
 * @param src, 待處理的數(shù)據(jù)塊 .
 * @param lenInBytes, 待處理的數(shù)據(jù)塊長(zhǎng)度 .
 */
uint8_t get_checksum(uint8_t *src,
                     uint32_t lenInBytes)
{
    uint8_t checksum = 0;
    // 計(jì)算數(shù)據(jù)和,丟棄高 bytes
    while (lenInBytes--)
    {
        checksum += *src++;
    }
    // 轉(zhuǎn)換為補(bǔ)碼
    checksum = (~checksum) + 1;
    return checksum;
}

/*!
 * @brief 驗(yàn)證數(shù)據(jù)包的 checksum
 *
 * @param src, 待處理的數(shù)據(jù)包 .
 * @retval 0, 數(shù)據(jù)包 checksum 校驗(yàn)正確 .
 * @retval 1, 數(shù)據(jù)包起始標(biāo)志字節(jié)錯(cuò)誤 .
 * @retval 2, 數(shù)據(jù)包 checksum 校驗(yàn)錯(cuò)誤 .
 */
int32_t verify_packet(uint8_t *src)
{
    uint8_t sum = 0;
    packet_header_t *header = (packet_header_t *)src;
    // 校驗(yàn)數(shù)據(jù)包頭
    if (header->startByte != kPacketStartByte)
    {
        return 1;
    }
    // 求所有數(shù)據(jù)及校驗(yàn)字節(jié)之和
    for (uint32_t i = 0; i < header->length; i++)
    {
        sum += *(src + sizeof(packet_header_t) + i);
    }
    // 結(jié)果為非 0,則 checksum 錯(cuò)誤
    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);
    // 填充數(shù)據(jù)
    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]);
    }

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

    return 0;
}

1.5 行業(yè)應(yīng)用

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

二、和校驗(yàn)法失效分析

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

當(dāng)多個(gè) byte 發(fā)生的 bit 錯(cuò)誤發(fā)生抵消現(xiàn)象(引起的增量和結(jié)果是 0x100 的倍數(shù)),無(wú)法識(shí)別錯(cuò)誤。

當(dāng) packet 中 byte 數(shù)據(jù)順序發(fā)生調(diào)換時(shí),無(wú)法識(shí)別錯(cuò)誤。

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

和校驗(yàn)法雖然能夠校驗(yàn) packet,且有一定的錯(cuò)誤 bit 檢測(cè)能力,但其是把 packet 當(dāng)做無(wú)序數(shù)據(jù)包來(lái)處理的,有沒(méi)有其他比和校驗(yàn)法更好且能夠校驗(yàn)數(shù)據(jù)次序的檢錯(cuò)方法呢?痞子衡在下篇會(huì)繼續(xù)聊。

  

至此,嵌入式里數(shù)據(jù)差錯(cuò)控制技術(shù)之和校驗(yàn)痞子衡便介紹完畢了,掌聲在哪里~~~

相關(guān)推薦

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

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