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

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

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 校验。

4 bootloader 中 CRC 校验程序

4.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;
	}
}

4.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;
	}
}

### 关于 `srec_cat` 命令在裁剪 (Crop) 场景的应用 #### 检查安装与版本验证 为了确认环境已经准备好使用 `srec_cat` 工具,在命令提示符下可以执行如下指令来查询当前安装的 `srec_cat` 版本: ```bash srec_cat -VERSion ``` 这一步骤有助于确保后续操作能够在支持的功能范围内顺利进行[^1]。 #### 处理文件并实现裁剪功能 当涉及到具体的裁剪场景时,可以通过指定起始偏移量以及结束位置的方式来进行数据片段的选择。例如,假设有一个 `.hex` 文件想要从中提取特定部分的数据,并将其保存为新的二进制文件,则可以采用以下方式构建命令: ```bash srec_cat input.hex -intel -offset start_address -o output.bin -binary -address_length 32 ``` 这里的 `-offset start_address` 参数用于定义截取开始的位置;而 `-address_length 32` 则指定了地址长度为32位,这对于某些特殊应用场景可能是必要的设置[^2]。 对于更复杂的裁剪需求,比如需要去除某个范围内的数据或者只保留某一段连续内存区域的内容,还可以利用额外选项进一步定制化处理逻辑。例如,若希望从源文件中排除掉一部分不感兴趣的数据区间 `[start, end)` ,则可以在原有基础上增加相应过滤条件: ```bash srec_cat input.hex -intel --exclude start end -o cropped_output.bin -binary ``` 此命令会读取原始 HEX 文件作为输入,跳过指定区间的记录项后再写入目标 BINARY 文件中[^4]。 #### 实际案例分析——STM32/CH32 OTA 更新过程中的CRC校验添加 在一个实际的例子中,针对 STM32CH32 设备实施空中固件更新(OTA),可能还需要考虑如何向最终生成的BIN文件附加循环冗余检验(CRC)值以增强传输可靠性。此时便可以用到带有 CRC 计算能力的高级用法: ```bash srec_cat firmware.hex -intel -crc32-l-e crc_location -o temp.hex -intel -Output_Block_Size=16 \ && srec_cat temp.hex -intel -offset - -minimum-addr temp.hex -intel -o final_firmware.bin -binary ``` 上述复合语句首先创建了一个临时HEX文件包含了计算好的CRC字段,接着再次调用`srec_cat`完成对整个映像的整体调整(如重新定位),最后导出可用于部署的目标BINARY格式文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值