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指向的函数也能成功。