打印
[应用相关]

STM32/CH32 OTA 使用 srec_cat 添加 CRC32 校验

[复制链接]
21|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2025-1-13 11:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
1 生成带 CRC32 校验的 Bin 文件(通过 Bootloader 下载)
1.1 指定地址保存 CRC 校验
srec_cat [srcHexFile] -intel -crc32-l-e  [crc32Addr] -o [dstHexFile] -intel -Output_Block_Size=16

上述命令将源 Hex 文件 [srcHexFile] 的 CRC32 校验保存到地址 [crc32Addr],最后保存到 [dstHexFile] 文件。

[srcHexFile] 后面的 -intel 表示源文件为 Hex 文件。
[dstHexFile] 后面的 -intel 表示输出文件为 Hex 文件。
-Output_Block_Size=16 表示一行 Hex 为 16 字节,与 STM32/CH32 编译生成的 Hex 文件格式相同,如果没有指定这个参数,则默认为 32 字节。
需要输出 Bin 文件,则应该修改为

srec_cat [srcHexFile] -intel -crc32-l-e  [crc32Addr] -o [dstBinFile] -binary

上述命令将源 Hex 文件 [srcHexFile] 的 CRC32 校验保存到地址 [crc32Addr],最后保存到 [dstBinFile] 文件。

[srcHexFile] 后面的 -intel 表示源文件为 Hex 文件。
[dstBinFile] 后面的 -binary 表示输出文件为 Bin 文件。
上面的输出 Bin 文件,会从 0 地址开始填充 0x00,如果 App 程序的起始地址不是 0,就会将起始地址之前的地址全部填充为0,输出很大的 Bin 文件。可以通过设置偏移的方式,从起始地址生成 Bin 文件。

srec_cat [srcHexFile] -intel -crc32-l-e  [crc32Addr] -o [tempHexFile] -intel -Output_Block_Size=16
srec_cat [tempHexFile] -intel -offset -[offsetAddr] -o [dstBinFile] -binary


这里使用了 2 条命令,第 1 条命令计算了源 Hex 文件 [srcHexFile] 的 CRC32 值,并保存到中间缓存文件 [tempHexFile] 文件中。第 2 条命令将缓存文件 [tempHexFile] 中的起始地址减去 [offsetAddr] 后转换为 Bin 文件。

注意:第 2 条命令中的 [offsetAddr] 前面有 - 表示减去 [offsetAddr]。如果省略该符号,则会在 Hex 文件起始地址 + [offsetAddr] 的长度上填充 0。

上述 2 条命令可以合并为 1 条命令,不生成 [tempHexFile] 中间文件

srec_cat ( [srcHexFile] -intel -crc32-l-e [crc32Addr] ) -offset -[offsetAddr] -o [dstBinFile] -binary

上述命令括号里面的作用与前面第 1 条的作用是一样的,括号外面的命令和第 2 条作用相同。

上述命令还是可以省略括号,可以修改为

srec_cat [srcHexFile] -intel -crc32-l-e [crc32Addr] -offset -[offsetAddr] -o [dstBinFile] -binary

注意:这里的括号省略不会出错,但是在有些情况下,省略括号会出错,下面会有例子。

前面的 [offsetAddr] 需要的手动指定,如果需要命令自动减去最小的地址,就可以使用下面的命令

srec_cat [srcHexFile] -intel -crc32-l-e  [crc32Addr] -o [tempHexFile] -intel -Output_Block_Size=16
srec_cat [tempHexFile] -intel -offset - -minimum-addr [tempHexFile] -intel -o [dstBinFile] -binary


上面使用 -minimum-addr [tempHexFile] -intel 自动计算了 [tempHexFile] 的最低地址,并将值通过 -offset 设置了偏移。-minimum-addr [tempHexFile] -intel 中的 -intel 表示计算最低地址的文件使用的是 Hex 文件。

注意:在 -minimum-addr 前面同样有 - 表示是减去这个偏移。

这 2 条命令同样可以合并成 1 条命令,不生成 [tempHexFile] 中间文件

srec_cat [srcHexFile] -intel -crc32-l-e [crc32Addr] \
                 -offset - -minimum-addr [srcHexFile] -intel -crc32-l-e [crc32Addr] \
         -o [dstBinFile] -binary


由于命令太长,这里使用 \ 将单行命令分行,在 Windows Cmd 中使用 ^ 将单行命令分行,需要将 \ 换成 ^。

与前面 2 条命令相比,这里用 [srcHexFile] -intel -crc32-l-e [crc32Addr] 替换了的前面第 2 条命令的 [tempHexFile] -intel。

在 bootloader 中,可以使用下列方法读取到 CRC32 校验值

uint32_t crc32 = *(uint32_t *)crc32Addr;


注意:将 crc32Addr 修改实际 FLASH 地址,与前面命令中的 [crc32Addr] 相同

1.2 App 程序末尾保存 CRC 地址
如果将 CRC32 保存在 App 最末尾, 可以通过 [srcHexFile] 文件来计算,命令如下

srec_cat [srcHexFile] -intel -crc32-l-e -maximum-addr [srcHexFile] -intel -o [tempHexFile] -intel -Output_Block_Size=16
srec_cat [tempHexFile] -intel -offset - -minimum-addr [tempHexFile] -intel -o [dstBinFile] -binary


上述命令中,使用 -maximum-addr [srcHexFile] -intel 来计算 App 程序的最大地址,将 CRC32 放到了 App 程序的最末尾。

这 2 条命令同样可以合并成 1 条命令

srec_cat ( [srcHexFile] -intel -crc32-l-e -maximum-addr [srcHexFile] -intel ) \
                 -offset - -minimum-addr [srcHexFile] -intel \
         -o [dstBinFile] -binary


这里使用 [srcHexFile] -intel -crc32-l-e -maximum-addr [srcHexFile] -intel 替换了前面第 2 条命令的 [tempHexFile] -intel。由于 CRC 添加到了 App 程序的最末尾,所以 -minimum-addr [tempHexFile] 与 -minimum-addr [srcHexFile] -intel 相同,所以也进行了简单的替换。

注意:这里的括号不能省略掉,如果省略掉括号,-offset 会优先和前面的 [srcHexFile] -intel 合并,也就是说

srec_cat [srcHexFile] -intel -crc32-l-e -maximum-addr [srcHexFile] -intel \
         -offset - -minimum-addr [srcHexFile] -intel \
         -o [dstBinFile] -binary


相当与:

srec_cat [srcHexFile] -intel -crc32-l-e  -maximum-addr \
         ( [srcHexFile] -intel -offset - -minimum-addr [srcHexFile] -intel ) \
        -o [dstBinFile] -binary


CRC32 的实际保存地址为:[srcHexFile] 的最大地址减去 [srcHexFile] 的最小地址,通常这会与 App 代码地址重复。-offset 偏移地址生效地方不是最前面的 [srcHexFile] -intel,而是最近的 [srcHexFile] -intel。
但是可以修改为下面的方式

srec_cat [srcHexFile] -intel -crc32-l-e ( -maximum-addr [srcHexFile] -intel ) \
         -offset - -minimum-addr [srcHexFile] -intel \
         -o [dstBinFile] -binary


App 末尾保存保存 CRC32 需要在程序下载的时候,知道程序的终止地址 endAddr,并将该地址保存到固定 Flash 地址中,方便后续校验取出CRC32校验值。bootloader 可以使用下面的方法取出CRC32的校验值

uint32_t crc32Addr = endAddr;
uint32_t crc32 = *(uint32_t *)crc32Addr;


2 生成带 CRC32 校验和App代码起始\终止地址的 Hex 文件(通过 SWD 下载)
第 1 小节生成了 App 末尾带有 CRC32 校验的 Bin 文件,在用 bootloader 下载时,可以在 bootloader 代码中将 Bin 文件的起始地址和终止地址保存到 Flash 的,上电时,校验 App 代码。但是这样,如果通过 SWD 的方式下载 App 代码时,由于 Hex 文件中并不带有起始地址和终止地址,所以上电校验就无法通过了。这样使用 SWD 的方式调试 App 代码就不方便,所以本节会介绍如何生成带有 CRC32 校验和 App 代码起始和终止地址的 Hex 文件。

2.1 在固定地址保存指定的起始/终止地址
srec_cat [srcHexFile] -intel \
                 -crc32-l-e  -maximum-addr [srcHexFile] -intel \
                 -generate [startSaveAddr] [startSaveAddr+4] [startAddr] 4 \
                 -generate [endSaveAddr] [endSaveAddr+4] [endAddr]  4 \
                 -o [dstHexFile] -intel -Output_Block_Size=16


上面命令中,输入源 Hex 文件 [srcHexFile] 计算 CRC32 校验,并放到 App 程序末尾。同时在 [startSaveAddr] 地址保存了 App 代码起始地址 [startAddr],在 [endSaveAddr] 地址保存了 App代码的终止地址 [endAddr]。其中的 4 表示一个地址占用 4 个字节。最后输出到 [dstHexFile] 文件中。使用 SWD 方式下载 [dstHexFile] 文件就能通过校验了。

注意:[startSaveAddr] 和 [endSaveAddr] 地址要与 bootloader 中指定的地址相同。

2.2 在固定地址保存自动计算的起始/终止地址
srec_cat [srcHexFile] -intel \
                 -crc32-l-e  -maximum-addr [srcHexFile] -intel \
                 -generate [startSaveAddr] [startSaveAddr+4] -constant-l-e -minimum-addr [srcHexFile] -intel 4 \
                 -generate [endSaveAddr] [endSaveAddr+4] -constant-l-e -maximum-addr [srcHexFile] -intel  4 \
                 -o [dstHexFile] -intel -Output_Block_Size=16


上面使用 -constant-l-e -minimum-addr [srcHexFile] -intel 代替了前面的 [startAddr],使用 -constant-l-e -maximum-addr [srcHexFile] -intel 代替了前面的 [endAddr]。App程序的起始地址 [startAddr] 通过 [srcHexFile] 文件的最小地址确定,App程序的终止地址 [endAddr] 通过 [srcHexFile] 文件的最大地址确定。

bootloader 可以使用下面的方法取出 App 程序的起始地址、终止地址和CRC32的校验值

uint32_t startAddr = *(uint32_t *)startSaveAddr;
uint32_t endAddr = *(uint32_t *)endSaveAddr;
uint32_t crc32Addr = endAddr;
uint32_t crc32 = *(uint32_t *)crc32Addr;


3 在第 2 小节的基础上生成带 Bootloader 的 Hex 文件(通过 SWD 下载)
第 2 小节生成了带有 App 代码起始地址、终止地址和 CRC32 校验的 Hex 文件。这样就可以先通过SWD下载bootloader程序,然后再通过SWD/bootloader下载 App 程序。srec_cat 也可以直接生成带有 bootloader 的 Hex 文件,这样就只需要通过SWD下载 1 次程序,就能同时把 bootloader 和 App 程序下载进去。命令如下

srec_cat [bootHexFile] -intel \
         ( \
         [appHexFile] -intel \
                 -crc32-l-e  -maximum-addr [appHexFile] -intel \
                 ) \
                 -generate [startSaveAddr] [startSaveAddr+4] -constant-l-e -minimum-addr [appHexFile] -intel 4 \
                 -generate [endSaveAddr] [endSaveAddr+4] -constant-l-e -maximum-addr [appHexFile] -intel  4 \
                 -o [dstHexFile] -intel -Output_Block_Size=16


[bootHexFile] 表示源 bootloader 程序 Hex 文件,[appHexFile] 表示源 App 程序 Hex 文件。上述命令执行步骤如下:

生成末尾带有 CRC32 校验的 App 程序 Hex 文件
将前 1 步生成的 App 程序 Hex 文件与 bootloader 程序的 Hex 文件合并
计算原始 App 程序的起始地址保存到 [startSaveAddr]
计算原始 App 程序的起始地址保存到 [endSaveAddr]
最后生成的 Hex 文件保存到 [dstHexFile]
注意:这里的括号不能省略,否则会计算 bootloader 和 App 程序合并后的 CRC32 校验。

3 bootloader 中 CRC 校验程序
3.1 使用查表法计算 CRC32
const uint32_t crcTab[256] =
{
    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
    0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
    0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
    0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
    0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
    0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
    0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
    0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
    0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
    0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
    0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
    0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
    0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
    0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
    0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
    0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
    0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
    0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
    0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
    0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};

/**
* @brief  CRC32 计算
* @param  startSaveAddr: APP 代码保存起始地址的地址
* @param  endSaveAddr: APP 代码保存终止地址的地址
* @retval 0, CRC32 校验成功; 其他, CRC32 校验失败
*/
uint8_t CRC32_Calculate(uint32_t startSaveAddr, uint32_t endSaveAddr)
{
        uint8_t val;
        uint32_t addr, startAddr, endAddr, crc32Addr, crc32;
    uint32_t temp = 0xFFFFFFFF;

        startAddr = *(uint32_t *)startSaveAddr; /* App 起始地址 */
        endAddr = *(uint32_t *)endSaveAddr; /* App 终止地址 */
       
        if(startAddr >= endAddr)
        {
                return 1;
        }

        crc32Addr = endAddr; /* CRC32 校验地址 */
        crc32 = *(uint32_t *)crc32Addr; /* CRC32 校验值 */

    for(addr = startAddr; addr < endAddr; addr++)
    {
        val = *(uint8_t *)addr;
        temp = (temp >> 8) ^ crcTab[(temp & 0xFF) ^ val];
    }
       
        if(crc32 == ~temp)
        {
                return 0;
        }
        else
        {
                return 2;
        }
}



3.2 使用 CH32V103/CH32V307 硬件 CRC 计算 CRC32
/**
*  @brief  CRC32 将一个 32-bit 数据按位反转,如 0011 -> 1100
*  @param  data: 输入数据
*  @retval 反转后的数据
*/
uint32_t CRC32_ReverseBits(uint32_t data)
{
    data = (((data & 0xAAAAAAAA) >> 1) | ((data & 0x55555555) << 1));
    data = (((data & 0xCCCCCCCC) >> 2) | ((data & 0x33333333) << 2));
    data = (((data & 0xF0F0F0F0) >> 4) | ((data & 0x0F0F0F0F) << 4));
    data = (((data & 0xFF00FF00) >> 8) | ((data & 0x00FF00FF) << 8));

    return((data >> 16) | (data << 16));
}

/**
* @brief  CRC32 计算
* @param  startSaveAddr: APP 代码保存起始地址的地址
* @param  endSaveAddr: APP 代码保存终止地址的地址
* @retval 0, CRC32 校验成功; 其他, CRC32 校验失败
*/
uint32_t CRC32_Calculate(uint32_t startSaveAddr, uint32_t endSaveAddr)
{
    uint32_t addr, val, startAddr, endAddr, crc32Addr, crc32;

        startAddr = *(uint32_t *)startSaveAddr; /* App 起始地址 */
        endAddr = *(uint32_t *)endSaveAddr; /* App 终止地址 */
       
        if(startAddr >= endAddr)
        {
                return 1;
        }

        crc32Addr = endAddr; /* CRC32 校验地址 */
        crc32 = *(uint32_t *)crc32Addr; /* CRC32 校验值 */

        /* 使能 CRC 时钟 */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);

    /* CRC 复位 */
    CRC_ResetDR();

        /* 输入数据,计算 CRC 值 */
    for(addr = startAddr; addr < endAddr; addr += 4)
    {
        val = *(uint32_t *)addr;
        CRC->DATAR = CRC32_ReverseBits(val);
    }

        /* 获取 CRC 值 */
    val = CRC32_ReverseBits(CRC->DATAR);

        /* 关闭 CRC 时钟 */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, DISABLE);

        if(crc32 == ~val)
        {
                return 0;
        }
        else
        {
                return 2;
        }
}


————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_38026359/article/details/145027854

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2063

主题

16016

帖子

15

粉丝