【解決方案】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()函數就出錯了。

所以,給run變量加上static修飾符,使其成爲從全局空間分配的靜態變量,這樣即使修改了棧頂指針,run變量也不會失效。執行run指向的函數也能成功。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章