USB|【解决方案】STM32单片机实现USB DFU IAP在线烧写程序的功能,但JumpToApplication跳转到用户程序时出现HardFault错误的解决办法

跳转代码如下:

/* DFU工具下载的子程序虽然起始地址不是0x08000000 (由项目属性Target选项卡中的IROM1配置) 但是也可以在Keil中直接下载程序, 而且还能使用ST-Link进行程序调试 只要在DFU主程序中禁用CRC校验就行 请确保system_stm32xxxx.c中设置的SCB->VTOR刚好等于程序的起始地址 */ #define CRCEN 1 // 若想要直接在Keil中下载并用ST-Link调试子程序, 则需要在主程序中禁用CRC校验 #define START_ADDR 0x08003a00 // 一定要保证这个地址不在DFU程序的地址范围中, 否则擦除Flash会直接Hard Error #define RESERVED_SIZE 0x100 // 请参考system_xxx.c中描述的VECT_TAB_OFFSET的对齐方式typedef void (*Runnable)(void); static int jump_to_application(void) { #if CRCEN const uint8_t *crc = (const uint8_t *)(START_ADDR + 4); const uint32_t *size = (const uint32_t *)START_ADDR; uint32_t crc2; #endif static Runnable run; uint32_t *addr = (uint32_t *)(START_ADDR + RESERVED_SIZE + 4); uint32_t *msp = (uint32_t *)(START_ADDR + RESERVED_SIZE); #if CRCEN if (START_ADDR + RESERVED_SIZE + *size >= FLASH_BASE + FLASH_SIZE) #else if ((*msp & 0xf0000000) != 0x20000000 || (*addr & 0xff000000) != 0x08000000) #endif { printf("Program data error!\n"); return -1; }#if CRCEN crc2 = calc_crc8((const uint8_t *)START_ADDR + RESERVED_SIZE, *size); if (*crc == crc2) { printf("CRC passed! addr=0x%08x, msp=0x%08x\n", *addr, *msp); #endif printf("Jump to application...\n"); run = (Runnable)*addr; // 一旦修改了栈顶指针, 就不能再使用函数中的局部变量, 否则可能会出现Hard Error错误 // 但可以使用static变量, 所以run变量必须声明为static __set_MSP(*msp); // 修改栈顶指针 run(); return 0; #if CRCEN } else { printf("CRC failed! 0x%02x!=0x%02x\n", *crc, crc2); return -1; } #endif }

程序保存在(START_ADDR + RESERVED_SIZE)地址处。跳转的核心代码很简单,先__set_MSP设置栈顶指针,然后执行函数指针run指向的函数。
最开始,这个函数的run变量没有加static,是一个普通的局部变量,意味着这个变量是在栈上分配的。
而跳转时先执行了__set_MSP修改了栈顶指针,导致了run变量失效,所以执行run()函数就出错了。
【USB|【解决方案】STM32单片机实现USB DFU IAP在线烧写程序的功能,但JumpToApplication跳转到用户程序时出现HardFault错误的解决办法】所以,给run变量加上static修饰符,使其成为从全局空间分配的静态变量,这样即使修改了栈顶指针,run变量也不会失效。执行run指向的函数也能成功。

    推荐阅读