请选择 进入手机版 | 继续访问电脑版

 找回密码
 立即注册
搜索
查看: 207|回复: 0

Intel HEX

[复制链接]

264

主题

0

回帖

1049

积分

管理员

积分
1049
发表于 2023-12-19 01:09:48 | 显示全部楼层 |阅读模式

Intel HEX 是一种用 ASCII 文本表达二进制文件信息的文件格式,对于我们玩嵌入式的人来说并不陌生。本文有两个目的:

  • 阐明 Intel HEX 的文件格式;
  • 用 C 语言解析和生成 HEX 文件。

文件格式

Intel HEX 是以记录( record )为单位的,文件的每行存放一条记录。记录的基本格式为:

在一条记录中,除了起始标记( 1 字节的 ASCII 码 “ : ”),其他的部分均为 HEX 数字,两个字节的 HEX 数字表示一个字节的数据,所以后续的说明中若无特别说明,均表示转换后的数据。

数据域长度:1 字节,表示数据域的长度;

地址:2 字节,表示数据域的起始地址;

记录类型:1 字节,表示记录是何种类型的记录,含义如下:

  • 00 数据记录

    :0B0010006164647265737320676170A7

  • 01 文件结束记录

    用于表示 HEX 文件的结束,记录的数据域长度为 00 ,地址域为 0000 。

    :00000001FF

  • 02 扩展段地址记录

    记录的数据域长度为 02 ,地址域为 0000 ,数据域为 2 字节的段基地址地址。由于数据记录( Type: 00 )的地址空间最大为 64 KB,再大就不能表达了。扩展段地址记录正是为了解决该问题,它将地址扩展至 20 位(扩展段地址表示绝对地址的 [19..4] 位),可达到 1 MB 的寻址空间。当扩展段地址为 0000 时可省略。

    :020000021200EA

    表示绝对地址为 0x12000,数据的绝对地址 = 0x12000 + 数据记录地址。

  • 03 起始段地址记录

    记录的数据域长度为 04 ,地址域为 0000 ,数据域为 4 字节的地址,用于加载至 CS:IP 寄存器(前两字节加载至 CS 寄存器,后两字节加载至 IP 寄存器)。由于不具备烧写至 flash 的信息,一般忽略它。

    :0400000300003800C1

  • 04 扩展线性地址记录

    记录的数据域长度为 02 ,地址域为 0000 ,数据域为 2 字节的扩展线性地址(绝对地址高 2 字节)。使用扩展线性地址可以将地址空间扩展到 32 位( 4 GB );当扩展线性地址为 0000 时可省略。显然比扩展段地址更强,更易用。

    :02000004FFFFFC

    表示后续的数据记录( Type:00,地址为 0xabcd )的绝对地址为 0xffffabcd 。

  • 05 起始线性地址记录

    记录的数据域长度为 04 ,地址域为 0000 ,数据域为 4 字节的地址(如 __main 的地址),用于加载至 EIP 寄存器。由于不具备烧写至 flash 的信息,一般忽略它。

    :04000005000000CD2A

数据域:其长度由长度域定义,内容因记录的类型而异。

校验:1 字节,其计算方法是将冒号以后,校验域以前的内容按字节求和,再模 0x100 ,最后用 0x100 减去模运算结果的差即为校验。

CS = 0x100 - sum % 0x100
或 CS = ~(sum & 0xff) + 1
或 CS = ((sum & 0xff) ^ 0xff) + 1

C语言实现

注意:以下源码仅供参考!

#define min(m, n)        ((m) < (n) ? (m) : (n))

static const char digitc[16] = "0123456789ABCDEF";

struct HexRecord
{
    uint8_t type;
    uint16_t address;
    uint8_t data[256];
    uint8_t dataLen;
};

/**
 * 单字符转数值(支持到36进制)
 * @param  c 待转换字符
 * @return   0xff 转换错误, 0 ~ 35 转换结果
 */
uint8_t char2int(char c)
{
    uint8_t ret = 0xff;

    if(c >= '0' && c <= '9')
    {
        ret = c - '0';
    }
    else if(c >= 'A' && c <= 'Z')
    {
        c -= 'A';
        ret = c + 10;
    }
    else if(c >= 'a' && c <= 'z')
    {
        c -= 'a';
        ret = c + 10;
    }

    return ret;
}

/**
 * 将2个HEX字符转换为数值
 * 若出现非HEX字符或没有输入字符将出错
 * 若只有一个字符将转换为低4位
 * @param  hex  待转换的字符指针
 * @param  byte 转换后的数值存储指针
 * @return      0 出错  1 转换了1个字符  2转换了两个字符
 */
int hex2byte(const char *hex, uint8_t *byte)
{
    uint8_t tmp;

    if(hex[0])
    {
        tmp = char2int(hex[0]);
        if(tmp < 16)
        {
            *byte = tmp;
            if(hex[1])
            {
                tmp = char2int(hex[1]);
                if(tmp < 16)
                {
                    *byte = (*byte << 4) | tmp;
                    return 2;
                }
            }
            else
            {
                return 1;
            }
        }
    }

    return 0;
}

/**
 * 将1字节的数值转换为2字节的HEX字符
 * @param byte 待转换数值
 * @param hex  转换后的字符存储指针
 */
void byte2hex(uint8_t byte, char *hex)
{
    hex[0] = digitc[byte >> 4];
    hex[1] = digitc[byte & 0x0f];
}

/**
 * 将HEX字符串转换为数值
 * 若出现非HEX字符将出错(不包括空格)
 * 若有奇数个字符,则最后一个字符将转换为低4位
 * @param  hex 待转换HEX字符串指针
 * @param  bin 转换后的数值存储指针
 * @return     -1 转换错误  大于或等于0 数值长度
 */
int hex2bin(const char *hex, uint8_t *bin)
{
    int i, j;

    for(i = 0; *hex;)
    {
        if(*hex == ' ')
        {
            hex++;
        }
        else if((j = hex2byte(hex, &bin[i])) != 0)
        {
            hex += j;
            i++;
        }
        else
        {
            return -1;
        }
    }

    return i;
}

/**
 * 将数值转换为字符串
 * @param  bin 待转换的数值指针
 * @param  len 数值长度
 * @param  hex 转换后的字符串的存储区指针
 * @return     转换后的字符串的存储区指针
 */
char *bin2hex(const uint8_t *bin, int len, char *hex)
{
    char *ret = hex;

    while(len--)
    {
        byte2hex(*bin++, hex);
        hex += 2;
    }
    *hex = '\0';

    return ret;
}

/////////////////////////////////////////////////////////////

/**
 * 计算记录的校验值
 * @param  data 记录的指针(指向长度域)
 * @param  len  记录的长度(从长度域开始,不包含校验域)
 * @return      校验值
 */
static uint8_t checksum(const uint8_t *data, int len)
{
    uint8_t sum = 0;

    while(len--)
    {
        sum += *data++;
    }
    return ~sum + 1;
}

/**
 * 解析一条Intel HEX记录字符串
 * @param  record 待解析记录字符串指针
 * @param  rec    解析结构体存储指针
 * @return        0 失败   1 成功
 */
int hexRecordAnalyse(const char *record, struct HexRecord *rec)
{
    int len;
    uint8_t bin[262];

    if(record[0] == ':')
    {
        if(strnlen(record + 1, 523) < 523)
        {
            len = hex2bin(record + 1, bin);
            if(len >= 5)
            {
                if(bin[0] + 5 == len)
                {
                    if(bin[len - 1] == checksum(bin, len - 1))
                    {
                        rec->type = bin[3];
                        rec->address = (uint16_t)bin[1] << 8 | bin[2];
                        memcpy(rec->data, bin + 4, bin[0]);
                        rec->dataLen = bin[0];
                        return 1;
                    }
                }
            }
        }
    }
    else if(record[0] == '\0') //空行
    {
        rec->type = 0;
        rec->address = 0;
        rec->dataLen = 0;
        return 1;
    }

    return 0;
}

/**
 * Intel HEX记录转bin
 * @param  startAddr 起始地址,若HEX记录的地址小于此地址将出错
 * @param  hex       HEX记录的指针
 * @param  bin       解析后的数据存储指针
 * @param  maxLen    存储区大小
 * @param  fill      记录间的数据区填充值
 * @return           -1 超出范围  大于或等于0 解析后的数据大小(从bin处开始计,到最后一个有效的数据记录止)
 */
int hexRecord2bin(uint32_t startAddr, char *hex, uint8_t *bin, uint32_t maxLen, uint8_t fill)
{
    char *pos = NULL;
    struct HexRecord rec;
    uint32_t len, addr, extAddr = 0, binLen = 0;

    memset(bin, fill, maxLen);
    while(*hex)
    {
        if((pos = strstr(hex, "\n")) != NULL)
        {
            if(*(pos - 1) == '\r') *(pos - 1) = '\0';
            else *pos = '\0';
        }
        if(hexRecordAnalyse(hex, &rec))
        {
            if(rec.type == 0) //data
            {
                addr = extAddr + rec.address;
                if(startAddr > addr || startAddr + maxLen < addr + rec.dataLen) return -1; //超出范围
                len = addr - startAddr + rec.dataLen;
                memcpy(bin + addr - startAddr, rec.data, rec.dataLen);
                binLen = binLen < len ? len : binLen;
            }
            else if(rec.type == 1) //end
            {
                break;
            }
            else if(rec.type == 2) //扩展段地址
            {
                extAddr = ((uint32_t)rec.data[0] << 12) | ((uint32_t)rec.data[1] << 4);
            }
            else if(rec.type == 4) //扩展线性地址
            {
                extAddr = ((uint32_t)rec.data[0] << 24) | ((uint32_t)rec.data[1] << 16);
            }
        }

        if(pos) hex = pos + 1;
        else break;
    }

    return binLen;
}

/**
 * add extended linear address record
 * @param extAddr 线性扩展地址
 * @param record  记录存储指针
 * @return        记录长度
 */
int addExtLinAddrRec(uint16_t extAddr, char *record)
{
    uint8_t tmp[8];

    tmp[0] = 2;
    tmp[1] = 0;
    tmp[2] = 0;
    tmp[3] = 4;
    tmp[4] = extAddr >> 8;
    tmp[5] = extAddr;
    tmp[6] = checksum(tmp, 4 + 2);
    bin2hex(tmp, 7, record + 1);

    record[0] = ':';
    record[15] = '\r';
    record[16] = '\n';

    return 17;
}

/**
 * add data record
 * @param addr    数据目标起始地址
 * @param data    数据指针
 * @param dataLen 数据长度
 * @param record  记录存储指针
 * @return        记录长度
 */
int addDataRec(uint16_t addr, const uint8_t *data, uint8_t dataLen, char *record)
{
    uint8_t tmp[21];

    tmp[0] = dataLen;
    tmp[1] = addr >> 8;
    tmp[2] = addr;
    tmp[3] = 0;
    memcpy(&tmp[4], data, dataLen);
    tmp[4 + dataLen] = checksum(tmp, 4 + dataLen);
    bin2hex(tmp, 5 + dataLen, record + 1);

    record[0] = ':';
    dataLen = (dataLen + 5) << 1;
    record[dataLen + 1] = '\r';
    record[dataLen + 2] = '\n';

    return dataLen + 3;
}

/**
 * add end-of-file record
 * @param  record 记录存储指针
 * @return        记录长度
 */
int addEndRec(char *record)
{
    memcpy(record, ":00000001FF\r\n", 13);

    return 13;
}

/**
 * 将数据转换为Intel HEX记录
 * @param  addr   数据目标起始地址
 * @param  bin    数据的指针
 * @param  len    数据的长度
 * @param  hex    转换记录的存储指针
 * @return        0 转换失败  1 转换成功
 */
int bin2HexRecord(uint32_t addr, const uint8_t *bin, uint32_t len, char *hex)
{
    uint32_t m, n;

    if(0 - addr < len) //32位地址空间剩余空间不足够存储
    {
        return 0;
    }

    m = 0;
    while(len)
    {
        if((addr & 0xffff0000) > (m & 0xffff0000))
        {
            m = addr;
            hex += addExtLinAddrRec(addr >> 16, hex);
        }

        n = min(len, 16 - (addr % 16));
        hex += addDataRec(addr, bin, n, hex);
        bin += n;
        addr += n;
        len -= n;
    }
    hex += addEndRec(hex);
    *hex = '\0';

    return 1;
}

参考资料

GENERAL: INTEL HEX FILE FORMAT http://www.keil.com/support/docs/1584.htm

Intel HEX https://en.wikipedia.org/wiki/Intel_HEX


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|深圳市棱讯科技有限公司 ( 粤ICP备2024228160号-2|粤公网安备44030002003510号 )

GMT+8, 2024-5-29 05:05 , Processed in 0.021522 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表