Sam*_*ana 0 c stm32 bootloader
我正在着手在 STM32(类似于 IAP AN4657)上开发自定义引导加载程序。假设flash分为3个区域scratch,user area,IAP code(bootloader)和待升级的固件都在scratch area。我想要一个带有固件版本和校验和的标头,我需要了解如何实现固件标头并从 IAP(引导加载程序)代码检查固件的有效性。对资源的任何参考表示赞赏。
您可以创建一个包含头信息的结构,并将该头放在闪存中的已知位置。您看到正在使用两种通用方法:
该方法取决于您的要求、您希望引导加载程序的复杂程度以及您愿意付出多少努力使其更加优化或灵活。通常希望引导加载程序尽可能简单且通常不可自编程(有一个引导加载程序部分在出厂后从未修改过)。无论哪种方式,标头都应位于闪存内的恒定偏移量处。进一步阐述上述两种方法:
1.(填充的)二进制文件末尾的固件标头。
这是一种更简单的方法,特别是因为某些工具/IDE(例如 IAR)具有现成的机制。假设您的固件标头具有以下格式:
typedef struct
{
uint32_t firmware_verson;
uint32_t crc32;
} sFirmwareHeader;
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您将固件二进制文件(0xFF例如)填充到FLASH_SIZE - sizeof(sFirmwareHeader)并将结构放在那里。这些相同的工具通常还具有计算固件二进制文件的 CRC 并将其放在末尾的能力,这正是适合这种方法的地方。最大的缺点是每次要以这种方式进行固件升级时,都需要从头到尾传输整个应用程序二进制文件,包括填充字节。如果您的应用程序很小,这将传输大量不必要的字节。当然,我没有提到任何可以缩小它的压缩方法,因为这些方法会使您的引导加载程序更复杂,因此更容易出错。
2. 应用程序二进制文件开头的固件头
另一种方法是将固件标头放在开头的某个位置。这样做的好地方可能是在 ISR 向量之后但在 .text、.data 和 .bss 之前,它们的大小会随着您更改应用程序代码而变化。一个好主意是使用firmware_size字段扩展上述结构:
typedef struct
{
uint32_t firmware_verson;
uint32_t firmware_size;
uint32_t crc32;
} sFirmwareHeader;
Run Code Online (Sandbox Code Playgroud)
这样引导加载程序不仅可以验证要加载的固件文件的 CRC,还可以验证已经加载到闪存中的应用程序的完整性,因为固件头仍然驻留在闪存中的恒定偏移量下,它是只是不像第一种方法那样在最后。这种方法的优点是您只需要根据需要传输尽可能多的字节。缺点是可能没有任何现成的工具可供您使用。在构建应用程序固件二进制文件后,您需要编写某种简单的程序/脚本来计算 CRC 值(firmware_size 值可以由链接器提供)。
为了使答案更完整,这里有一段代码,无论您打算使用哪种方法,它都应该为您提供一个良好的起点。这些是针对 gcc 的。
链接描述文件的一部分定义了一个部分,用于将固件标头保存在0x200从FLASH存储器部分开始计数的恒定偏移量处:
__fw_header_offset = 0x200;
SECTIONS
{
/* ISR vectors */
.fw_header : ALIGN(4)
{
FILL(0xFF)
. = ORIGIN(FLASH) + __fw_header_offset;
KEEP(*(.fw_header))
} >FLASH
/* Other sections in Flash */
}
Run Code Online (Sandbox Code Playgroud)
然后在您的应用程序代码中,您可以执行以下操作:
const sFirmwareHeader __attribute__ ((section(".fw_header"))) FirmwareHeader = {
1, // firmware_verson
0 // crc32, this can be filled with an external application after building firmmwre binary
};
Run Code Online (Sandbox Code Playgroud)
它创建了一个全局FirmwareHeader结构并放置在早期定义的“.fw_header”部分下。