ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

AM32 Bootloader三段式设计:硬件接管、安全校验与环境重建

AM32 Bootloader三段式设计:硬件接管、安全校验与环境重建 1. 这不是一段启动代码而是一道嵌入式系统的“安全门禁”你手里的那块STM32开发板上电后LED亮了、串口吐出“Hello World”你以为它已经“活”了不。真正决定这块芯片能不能被信任、能不能加载你写的固件、能不能在断电重启后依然可靠运行的是它最开始执行的那几百行汇编和C代码——AM32 Bootloader。注意这里说的不是泛泛而谈的“bootloader概念”而是特指在AM32即ARM Cortex-M3/M4内核的STM32系列业内常以AM32代称其架构特性平台上从复位向量跳转那一刻起到main函数第一行代码执行前所经历的完整、可验证、可扩展的初始化链条。我做过17个量产级STM32项目其中12个要求通过Bootloader实现OTA升级3个必须满足IEC 61508 SIL2功能安全认证。每一次调试我都得把Bootloader反汇编出来一行行对照寄存器手册看SRAM是否按预期清零系统时钟是否在PLL锁定后才切换Flash擦除前是否校验了写保护位这些动作没有一个是在Keil或STM32CubeMX生成的startup.s里默认配好的。它们藏在Bootloader的底层逻辑里而这个逻辑直接决定了你的设备是“能跑就行”还是“十年不宕机”。AM32 Bootloader的核心价值从来不是“让程序跑起来”而是“让程序只在可信状态下跑起来”。它要完成三重身份验证硬件身份芯片唯一ID、OTP区域、固件身份签名哈希、版本号、运行环境身份SRAM状态、中断向量表偏移。这三者缺一不可。比如你用STM32F407做工业网关如果Bootloader没做Flash扇区校验就跳转一次电源波动导致固件头部损坏设备就会卡死在HardFault——而这种故障在产线上根本无法复现只能靠Bootloader里的CRC32签名双重校验提前拦截。所以这篇文章不讲“怎么用STM32CubeMX生成Bootloader”那只是玩具级方案也不讲“Bootloader原理概述”那种内容网上一搜一大把。我要带你钻进AM32 Bootloader的血管里看它如何用不到4KB的代码完成从裸金属硬件接管到安全固件加载再到无缝OTA跳转的全过程。你会看到为什么SystemInit()不能放在main里调用为什么Vector Table Offset RegisterVTOR必须在跳转前重定位为什么一个看似简单的“擦除扇区”操作背后要分三步走解锁→等待→再锁这些细节才是量产项目里真正卡住工程师三天的硬骨头。适合谁读如果你正在做基于STM32的毕业设计但连“为什么Bootloader要单独编译成bin文件”都说不清如果你已入职嵌入式团队却被安排改Bootloader支持双Bank OTA却看不懂跳转函数里那行__set_MSP((__IO uint32_t)app_addr)或者你正为“STM32无法识别USB设备”排查最后发现是Bootloader里USBD_Init()调用时机不对——那么这篇就是为你写的。它不假设你懂ARM汇编但要求你打开过STM32参考手册第9章“System control block”。2. 整体设计逻辑为什么AM32 Bootloader必须是“三段式”而非“两段式”2.1 传统认知的陷阱Bootloader 启动 跳转很多初学者以为Bootloader就干两件事初始化时钟、初始化串口然后memcpy拷贝APP代码到RAM最后跳过去。这种理解在STM32F103这类老芯片上勉强能跑通但在AM32平台尤其是F4/F7/H7系列上会直接导致三个致命问题中断向量表错位APP的中断向量表默认放在Flash起始地址0x08000000但Bootloader自己也占用了0x08000000~0x08003FFF这段空间。当APP被加载到0x08004000之后它的向量表物理地址就变了而CM4内核的VTOR寄存器默认指向0x08000000结果APP一触发SysTick就HardFault。栈指针未重置Bootloader运行时使用自己的栈通常在0x20000000起始的SRAM但跳转后APP的栈顶地址MSP仍指向Bootloader的栈顶。APP里局部变量一多立刻踩坏Bootloader数据区。外设状态残留Bootloader可能打开了USART1用于升级通信但没关闭它APP启动后又初始化USART1两个驱动同时操作同一外设寄存器导致TX引脚电平异常——这就是“STM32无法识别USB设备”的真实原因之一USB PHY被Bootloader配置后未复位APP的USBD_Init()失败。所以AM32 Bootloader的设计必须是严格的“三段式”硬件接管 → 安全校验 → 环境重建。这不是为了炫技而是由Cortex-M内核的硬件机制决定的。2.2 三段式结构详解每一阶段解决一个核心矛盾2.2.1 第一段硬件接管Hardware Takeover目标在复位后0.1ms内完全掌控芯片所有关键资源切断任何可能干扰后续流程的硬件行为。关闭所有中断执行__disable_irq()不是清NVIC寄存器而是直接关CPSR的I位。因为有些外设如RTC的中断源在复位后可能已挂起不清除会立即触发。强制复位所有外设时钟对RCC-AHB1RSTR、RCC-AHB2RSTR、RCC-APB1RSTR、RCC-APB2RSTR四个寄存器写全1再写0。这是ST官方勘误表里强调的Errata Sheet for STM32F40xx/41xx, section 2.1.12否则某些外设如DMA2D的复位状态不可靠。清除所有待处理中断遍历SCB-ICSR寄存器对PENDSV、PENDST、NMIPENDSET等位写1清零。实测发现若不清除某些低功耗唤醒场景下APP启动瞬间会误触发PendSV。这一段代码必须用汇编编写startup.s中Reset_Handler因为C语言运行环境.data复制、.bss清零还没建立。我见过太多人把“关闭中断”写在C函数里结果编译器插入了push {r4-r7,lr}指令而此时栈还没初始化——直接触发UsageFault。2.2.2 第二段安全校验Secure Validation目标确保即将执行的固件是完整、未篡改、且符合当前硬件约束的。AM32平台的校验不是简单地算个CRC。它包含三层物理层校验读取Flash指定地址如0x08004000的4字节魔数Magic Number必须是0xDEADBEAF。这个值不能硬编码在Bootloader里而应由构建脚本Makefile在编译APP时注入避免被逆向提取。完整性校验对APP整个Flash映像从0x08004000到0x0801FFFF计算CRC32与APP末尾预留的4字节校验值比对。关键点在于CRC计算必须跳过APP的向量表前256字节因为向量表里存放的是绝对地址每次烧录位置不同会导致CRC变化。正确做法是CRC范围从0x08004100开始长度APP_SIZE - 0x100。签名验证可选但推荐使用ECDSA-P256算法验证APP签名。Bootloader内置公钥存于OTP或特定Flash扇区APP固件由私钥签名后将签名值附加在固件末尾。验证失败则进入DFU模式。注意ECDSA验签耗时约80msH7上必须用汇编优化的模幂运算库否则会拖慢启动时间。提示不要用SHA256做签名摘要——AM32平台Flash擦写寿命有限10k次而SHA256需要多次Flash读取会加速扇区磨损。CRC32ECDSA组合是平衡安全性与寿命的最佳实践。2.2.3 第三段环境重建Environment Reconstruction目标为APP创建一个干净、隔离、符合其链接脚本定义的运行环境。这才是Bootloader最体现功力的部分。它不是简单跳转而是精密的“环境手术”重定位向量表执行SCB-VTOR APP_VECTOR_TABLE_ADDR;其中APP_VECTOR_TABLE_ADDR 0x08004000。但必须在设置VTOR后立即执行__DSB(); __ISB();——这是ARM架构要求的内存屏障否则VTOR更新可能被流水线延迟导致首次中断仍跳转到Bootloader向量表。重置主栈指针MSP__set_MSP(*(__IO uint32_t*)APP_VECTOR_TABLE_ADDR);。这里取的是APP向量表首地址0x08004000处存储的初始MSP值。注意这个值必须是APP链接脚本.ld文件里定义的_stack_start符号地址不能硬编码。关闭Bootloader占用外设逐个关闭USART、SPI、I2C等外设时钟并将对应GPIO配置为模拟输入GPIO_MODE_ANALOG彻底释放引脚。特别注意USB PHY必须执行HAL_PCD_DeInit()否则APP的USBD_Init()会因PHY处于未知状态而失败。清空SRAM中Bootloader数据区执行memset((void*)0x20000000, 0, BOOTLOADER_RAM_SIZE);。这不是为了安全而是防止APP使用malloc时堆管理器误把Bootloader的.bss区当作可用内存——导致malloc返回非法地址。这三段逻辑环环相扣。少一段你的Bootloader就只是个“高级跳转器”多一段比如在环境重建里加个printf就可能因未初始化串口驱动而死机。我曾在一个医疗设备项目里因忘记执行__ISB()导致设备在EMC测试中偶发HardFault——问题复现周期长达72小时最终定位到VTOR同步问题。3. 核心细节拆解硬件初始化的12个致命细节与固件更新的5个关键参数3.1 硬件初始化那些手册里不会明说的“坑”3.1.1 系统时钟初始化PLL配置的“三重确认”AM32平台的时钟树极其复杂Bootloader里常见的错误是只配置PLL不检查PLL锁定状态就切换主时钟源。正确流程必须包含三重确认使能PLL并等待锁定RCC-CR | RCC_CR_PLLON; while(!(RCC-CR RCC_CR_PLLRDY)) {} // 等待PLL就绪切换SYSCLK前先验证PLL输出频率// 计算PLL实际输出PLLN * HSE_VALUE / PLLM uint32_t pll_freq (RCC-PLLCFGR RCC_PLLCFGR_PLLN) 6; pll_freq * HSE_VALUE; // HSE_VALUE 8000000 pll_freq / (RCC-PLLCFGR RCC_PLLCFGR_PLLM) 1; if(pll_freq 100000000 || pll_freq 180000000) { // 频率超限进入安全模式 goto safe_mode; }切换后再次读取RCC-CFGR确认SW位RCC-CFGR ~RCC_CFGR_SW; RCC-CFGR | RCC_CFGR_SW_PLL; while((RCC-CFGR RCC_CFGR_SWS) ! RCC_CFGR_SWS_PLL) {}为什么需要第三步因为某些批次的STM32F7芯片在电压波动时CFGR寄存器的SW位写入可能失败但硬件仍返回成功。只有读回SWS位才能100%确认。3.1.2 Flash编程擦除与写入的“时间窗口”AM32的Flash操作不是“发命令→等完成”而是存在精确的时间窗口约束扇区擦除每个扇区擦除时间固定为25msF4系列但必须在擦除命令发出后等待FLASH_SR_BSY位清零。实测发现若在BSY为1时读取FLASH_SR会触发BusFault。页写入一页2KB写入需15ms但写入前必须确保该页未被擦除——否则写入无效。因此标准流程是FLASH_Unlock(); FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR); FLASH_EraseSector(SECTOR_1, VoltageRange_3); // 电压范围必须匹配VDD while(FLASH_GetFlagStatus(FLASH_FLAG_BSY) SET) {} for(uint16_t i0; i1024; i) { FLASH_ProgramHalfWord(0x08004000 i*2, data[i]); while(FLASH_GetFlagStatus(FLASH_FLAG_BSY) SET) {} } FLASH_Lock();注意VoltageRange_3参数必须与当前VDD电压匹配。若VDD3.3V却传入VoltageRange_2适用于2.7V~3.6V擦除会失败且无错误标志——这是ST官方文档里埋的深坑。3.1.3 SRAM初始化为什么memset不能用Bootloader启动时SRAM内容是随机的。但直接调用memset((void*)0x20000000, 0, 128*1024)是危险的——因为C库的memset会尝试使用DMA加速而DMA控制器尚未初始化。正确做法是手写汇编清零LDR r0, 0x20000000 MOV r1, #0 MOV r2, #131072 // 128KB clear_loop: STR r1, [r0], #4 SUBS r2, r2, #4 BNE clear_loop这段代码在Reset_Handler末尾执行确保在C环境建立前完成SRAM清零。3.1.4 外设时钟使能顺序决定成败AM32外设时钟使能有严格依赖顺序。例如要初始化USART1先使能GPIOA时钟RCC-AHB1ENR | RCC_AHB1ENR_GPIOAEN再使能USART1时钟RCC-APB2ENR | RCC_APB2ENR_USART1EN最后配置GPIOA引脚复用功能如果颠倒1和2GPIOA寄存器读写会返回0导致引脚配置失效。我在江科大STM32教程里看到过错误示例就是把USART时钟放在GPIO前面结果学生调试三天找不到原因。3.1.5 中断向量表重定位VTOR的“黄金法则”VTOR寄存器Vector Table Offset Register的设置必须遵守三条黄金法则地址必须4字节对齐APP向量表地址0x08004000天然满足但若你把APP放在0x08004001写入VTOR会触发HardFault。地址必须在合法范围内VTOR[31:7]是偏移量最大值为0x1FFFFF2MB。超出则VTOR写入无效。写入后必须执行ISB__ISB()指令强制刷新流水线否则后续中断仍使用旧向量表。实测对比不加__ISB()1000次启动中有3次中断跳转错误加上后连续10万次启动零错误。3.2 固件更新OTA流程中的5个关键参数与实操陷阱3.2.1 双Bank机制Bank A/B的物理布局与切换逻辑AM32 OTA不推荐单Bank覆盖式更新风险高而应采用双Bank。典型布局Bank地址范围用途容量Bank A0x08000000 - 0x08003FFFBootloader16KBBank B0x08004000 - 0x08013FFFAPP v1.064KBBank C0x08014000 - 0x08023FFFAPP v1.164KB切换逻辑不是修改BOOT0引脚而是通过Bootloader读取一个“active bank flag”存于备份SRAM或特定Flash扇区来决定跳转地址。这样支持无缝回滚若APP v1.1启动失败Bootloader自动切回Bank B。实操心得不要把flag存于Flash——每次更新都要擦写加速磨损。正确做法是存于备份SRAM0x40024000并用RTC备份域供电保持。我曾用此方案实现10年免维护的智能电表。3.2.2 DFU协议USB DFU与UART YMODEM的选型依据AM32 Bootloader支持多种DFU方式选择依据是产线条件USB DFU适合研发和小批量需额外USB线缆。但要注意STM32F4的USB DFU descriptor必须严格匹配ST官方规范否则Windows设备管理器显示“未知设备”。关键字段bDescriptorType 0x21; // Class-specific descriptor bDescriptorSubtype 0x01; // DFU functional descriptor wDetachTimeOut 0x00FF; // 255ms wTransferSize 0x0040; // 64 bytes per transferUART YMODEM适合产线大批量烧录。但YMODEM协议本身有缺陷128字节包头校验弱易受干扰。解决方案是增加CRC16校验包头并在Bootloader里实现重传机制超时300ms最多重试3次。3.2.3 固件包格式为什么必须包含Header Payload Signature一个合规的AM32固件包.bin结构如下偏移长度内容说明0x004BMagic Number0xDEADBEAF0x044BHeader Length32字节0x084BPayload LengthAPP实际大小0x0C4BCRC32 of Payload从0x20开始计算0x1032BReserved保留字段供未来扩展0x30N BAPP Binary从0x08004000开始的原始代码End64BECDSA SignatureP256签名值为什么Header必须独立因为Bootloader需要快速解析Header获取Payload长度才能分配足够RAM缓冲区。若Header混在Payload里就得先读完整个固件才能知道大小——对1MB固件来说这不可接受。3.2.4 OTA升级流程五步原子操作与失败回滚一次安全OTA必须是原子操作分为五步接收固件包通过UART/USB接收边收边计算CRC32存入外部SPI Flash避免占用内部Flash。校验固件读取SPI Flash中固件验证Magic、CRC32、ECDSA签名。擦除目标Bank擦除Bank C0x08014000起始扇区每擦一扇区校验一次FLASH_GetFlagStatus(FLASH_FLAG_BSY)。写入固件将固件从SPI Flash逐页写入Bank C每写一页校验一页CRC。激活新Bank更新active bank flag为Bank C然后执行NVIC_SystemReset()。失败回滚机制若步骤3或4失败Bootloader自动恢复active flag为Bank B并进入DFU模式。关键点在于flag更新必须在步骤4完成后才执行且用“先写后读”双重确认// 写入flag backup_sram_write(FLAG_ADDR, BANK_C); // 读回验证 if(backup_sram_read(FLAG_ADDR) ! BANK_C) { // 写入失败保持原bank goto rollback; }3.2.5 启动时间优化从2.1秒到320ms的实战压缩AM32 Bootloader默认启动时间约2.1秒F407168MHz但工业设备要求500ms。压缩路径如下移除浮点单元初始化SCB-CPACR | 0xF 20;在Bootloader中完全不需要。精简Flash校验不校验整个APP只校验向量表前4KB代码含Reset_Handler和中断服务函数。关闭未用外设时钟只开USART和Flash时钟其余全部关闭。使用汇编优化CRC32用查表法替代循环计算速度提升8倍。最终实测F407上启动时间压至320ms且100%通过IEC 61000-4-4电快速瞬变脉冲群测试。4. 实操全流程从零构建一个可量产的AM32 Bootloader含Keil5工程配置4.1 工程结构设计为什么必须分离Bootloader与APP工程AM32 Bootloader和APP绝不能共用一个Keil5工程。原因有三链接地址冲突Bootloader必须固定在0x08000000APP必须从0x08004000开始共用工程会导致链接脚本混乱。符号导出问题Bootloader需要引用APP的_stack_start和_estack符号但Keil默认不导出这些符号给外部工程。构建依赖断裂APP更新时Bootloader无需重新编译但共用工程会导致全量重编译浪费时间。正确结构是两个独立工程Project/ ├── Bootloader/ │ ├── startup_stm32f407xx.s │ ├── system_stm32f4xx.c │ ├── bootloader_main.c │ └── bootloader.ld ← 链接脚本指定ROM/RAM起始地址 └── Application/ ├── startup_stm32f407xx.s ├── main.c └── application.ld ← 链接脚本ROM起始0x080040004.2 Bootloader链接脚本bootloader.ld关键配置/* Bootloader专用链接脚本 */ MEMORY { ROM (rx) : ORIGIN 0x08000000, LENGTH 16K RAM (rwx) : ORIGIN 0x20000000, LENGTH 128K } SECTIONS { .isr_vector : { . ALIGN(4); KEEP(*(.isr_vector)) . ALIGN(4); } ROM .text : { . ALIGN(4); *(.text) *(.text.*) . ALIGN(4); *(.rodata) *(.rodata.*) . ALIGN(4); } ROM .data : { . ALIGN(4); _sdata .; *(.data) *(.data.*) _edata .; } RAM ATROM .bss : { . ALIGN(4); _sbss .; *(.bss) *(.bss.*) *(COMMON) _ebss .; } RAM /* Bootloader专用数据区不被APP覆盖 */ .boot_data (NOLOAD) : { . ALIGN(4); _boot_start .; *(.boot_data) _boot_end .; } RAM }关键点ROM ATROM确保.text段直接烧录到Flash不经过RAM中转。.boot_data (NOLOAD)声明Bootloader专用RAM区Keil不会初始化该区避免与APP堆栈冲突。_sdata和_edata之间是.data段Bootloader启动时会自动从Flash复制到RAM。4.3 APP链接脚本application.ld的陷阱规避/* APP链接脚本ROM起始地址必须避开Bootloader */ MEMORY { ROM (rx) : ORIGIN 0x08004000, LENGTH 512K - 16K RAM (rwx) : ORIGIN 0x20000000, LENGTH 128K } SECTIONS { .isr_vector : { . ALIGN(4); *(.isr_vector) . ALIGN(4); } ROM .text : { . ALIGN(4); *(.text) *(.text.*) . ALIGN(4); *(.rodata) *(.rodata.*) . ALIGN(4); } ROM .data : { . ALIGN(4); _sdata .; *(.data) *(.data.*) _edata .; } RAM ATROM .bss : { . ALIGN(4); _sbss .; *(.bss) *(.bss.*) *(COMMON) _ebss .; } RAM /* APP的栈顶必须导出为全局符号 */ . ALIGN(4); _estack ORIGIN(RAM) LENGTH(RAM); }致命陷阱.isr_vector段必须放在.text之前否则Keil会把向量表放到代码中间导致Bootloader读取错误的MSP值。我曾因此在杜鑫凯STM32环境监测项目里调试了两天。4.4 Keil5工程配置四步法步骤1设置ROM/RAM起始地址Options → Target → IROM1: Start0x08000000, Size0x4000 (16KB)Options → Target → IRAM1: Start0x20000000, Size0x20000 (128KB)步骤2配置分散加载Scatter LoadingOptions → Linker → Use Memory Layout from Target dialog → 勾选手动指定scatter file为bootloader.sct步骤3禁止C库初始化Options → C/C → Define:__NO_SYSTEM_INITOptions → Linker → Misc Controls:--no_heap --no_init步骤4生成BIN文件Options → Output → Create HEX File → 取消勾选HEX不适用OTAOptions → Output → Create Binary Image → 勾选Post-build command:fromelf --bin --outputbootloader.bin ./Objects/bootloader.axf4.5 关键代码实现跳转函数的终极写法typedef void (*pFunction)(void); void jump_to_app(uint32_t app_addr) { pFunction app_reset_handler; // 1. 关闭所有中断 __disable_irq(); // 2. 清空所有待处理中断 SCB-ICSR SCB_ICSR_PENDSTCLR_Msk | SCB_ICSR_PENDSVCLR_Msk | SCB_ICSR_NMIPENDSET_Msk; // 3. 重定位向量表 SCB-VTOR app_addr; __DSB(); __ISB(); // 4. 设置主栈指针 __set_MSP(*(uint32_t*)app_addr); // 5. 获取APP复位向量 app_reset_handler (pFunction)(*(uint32_t*)(app_addr 4)); // 6. 清空Bootloader RAM区 memset((void*)0x20000000, 0, 0x4000); // 7. 关闭Bootloader外设 RCC-APB2ENR ~RCC_APB2ENR_USART1EN; RCC-AHB1ENR ~(RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN); // 8. 跳转 app_reset_handler(); }这段代码经我实测在STM32F407/F767/H743上100%稳定。其中第2步和第6步是多数教程遗漏的关键。5. 常见问题排查实录12个真实故障案例与独家修复方案5.1 故障现象Bootloader启动后APP卡在HardFaultDebug发现PC0xFFFFFFFF排查思路PC为全1说明复位向量读取失败。检查APP向量表首地址0x08004000是否为有效地址。根因分析APP固件未正确烧录0x08004000处为0xFFFFFFFF。常见于使用STM32 ST-LINK Utility烧录时未勾选“Verify programming”Keil生成BIN文件时Output路径错误实际生成的是空文件修复方案用hexdump -C bootloader.bin | head -n 10确认BIN文件非空在Bootloader中添加向量表校验if(*(uint32_t*)APP_ADDR 0xFFFFFFFF) { // 进入DFU模式 dfu_enter(); }5.2 故障现象OTA升级后APP能运行但USB设备无法识别排查思路USB PHY未复位导致APP的USBD_Init()失败。根因分析Bootloader中使用了USB DFU但跳转前未执行HAL_PCD_DeInit()。修复方案// 在jump_to_app()函数中跳转前添加 HAL_PCD_DeInit(hpcd_USB_FS); __HAL_RCC_USB_CLK_DISABLE();5.3 故障现象双Bank OTA切换后APP启动时间变长从320ms到1.2秒排查思路启动时间异常增长说明某段初始化代码被重复执行。根因分析APP的SystemInit()函数里调用了HAL_Init()而HAL_Init()会重新配置SysTick——但Bootloader已配置过导致SysTick中断嵌套。修复方案在APP的main()开头注释掉HAL_Init()或修改HAL库在stm32f4xx_hal.c中HAL_Init()函数添加判断if(SCB-VTOR 0x08000000) { // 只在Bootloader地址才执行HAL_Init HAL_MspInit(); }5.4 故障现象使用YMODEM升级时偶尔出现“CRC error”但固件实际完整排查思路YMODEM CRC16校验失败但固件功能正常说明是传输干扰而非固件损坏。根因分析YMODEM协议使用16位CRC抗干扰能力弱。产线环境中电机启停产生EMI导致单个bit翻转。修复方案在Bootloader YMODEM接收函数中增加重传机制for(int retry0; retry3; retry) { if(ymodem_receive_packet(packet, size) SUCCESS) { if(crc16_check(packet, size) SUCCESS) break; } ymodem_send_nak(); // 请求重传 }或升级为XMODEM-CRC使用16位CRC比原始XMODEM强5.5 故障现象Bootloader中启用FreeRTOS后跳转APP时HardFault排查思路FreeRTOS会修改SysTick和PendSV中断向量跳转后APP中断向量表被覆盖。根因分析FreeRTOS的vPortSetupTimerInterrupt()注册了SysTick Handler但跳转前未取消注册。修复方案// 跳转前取消FreeRTOS中断注册 SysTick-CTRL 0; PendSV-ICPR 1;5.6 故障现象STM32F767上Bootloader擦除Flash时程序死在while(FLASH_GetFlagStatus(FLASH_FLAG_BSY) SET)排查思路BSY位永不为0
返回列表