You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

STM32F469BIT Bootloader跳转后应用任务无法运行问题求助

STM32F469 Bootloader跳转后App1无响应排查与修正

现有配置信息

Flash分区划分

Bootloader -> FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 32K
App1 -> FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 1008K
App2 -> FLASH (rx) : ORIGIN = 0x08104000, LENGTH = 1008K

向量表偏移配置

App1 -> #define VECT_TAB_OFFSET 0x00008000U
App2 -> #define VECT_TAB_OFFSET 0x00104000U

跳转核心代码

typedef StaticTask_t osStaticThreadDef_t;
typedef void (*pFunction)(void);

#define APP1_ADDRESS  ((uint32_t)0x08008000)
#define NVIC_IRQ_COUNT  240
#define BASEPRI_MASK   ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

extern TIM_HandleTypeDef htim6;
extern TIM_HandleTypeDef htim2;

static void prvSafeGPIODeInit(void) {
    GPIO_InitTypeDef init = { 0 };
    const uint32_t keepA = GPIO_PIN_13 | GPIO_PIN_14;        /* SWD */

    HAL_GPIO_DeInit(GPIOA, ~keepA);
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOD, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOE, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOF, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOG, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOH, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOI, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOJ, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOK, GPIO_PIN_All);

    init.Pin = keepA;
    init.Mode = GPIO_MODE_AF_PP;
    init.Pull = GPIO_NOPULL;
    init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    init.Alternate = GPIO_AF0_SWJ;
    HAL_GPIO_Init(GPIOA, &init);
}

static void prvNVIC_Reset(void) {
    for (uint32_t i = 0; i < NVIC_IRQ_COUNT; ++i) {
        NVIC_DisableIRQ((IRQn_Type) i);
        NVIC_ClearPendingIRQ((IRQn_Type) i);
    }
}

static void prvPeriph_DeInit(void) {
    HAL_TIM_Base_DeInit(&htim6);
    HAL_TIM_Base_DeInit(&htim2);
}

static void prvBoot_DeInit(void) {
    __disable_irq();
    __set_BASEPRI(0xf0);

    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;

    prvSafeGPIODeInit();
    prvNVIC_Reset();
    prvPeriph_DeInit();

    HAL_RCC_DeInit();
    HAL_DeInit();
}

static void prvJumpToApp(uint32_t appAddr) {
    uint32_t newSP  = *(uint32_t*)appAddr;
    pFunction entry = (pFunction)*(uint32_t*)(appAddr + 4);

    prvBoot_DeInit();

    SCB->VTOR = appAddr;
    __DSB();
    __ISB();
    __set_MSP(newSP);

    __enable_irq();

    entry();

    while (1) {
        __NOP();
    }
}

void StartupEntry(void *argument) {
    /* USER CODE BEGIN StartupEntry */
    /* Infinite loop */
    (void) argument;
    for (;;) {
        HAL_GPIO_TogglePin(BM83_EN_GPIO_Port, BM83_EN_Pin);
        if (HAL_GPIO_ReadPin(ENC2_SW_GPIO_Port, ENC2_SW_Pin) == GPIO_PIN_SET) {
            prvJumpToApp(APP1_ADDRESS);
        }
        osDelay(20);
    }
    /* USER CODE END StartupEntry */
}

向量表存储信息

  • Bootloader .isr_vector
Section: .isr_vector
Region: FLASH
Address: 0x08000000
Size: 436
00 00 05 20 25 16 00 08 7D 14 00 08 85 14 00 08 8D 14 00 08 95 14 00 08 9D 14 00 08 00 00 00 00 ...
  • App1 .isr_vector
Section: .isr_vector
Region: FLASH
Address: 0x08008000
Size: 436
00 00 05 20 B5 C7 00 08 CD B7 00 08 D5 B7 00 08 DD B7 00 08 E5 B7 00 08 ED B7 00 08 00 00 00 00 00 ...

排查关键点

  1. App1栈地址合法性
    从小端模式解析App1向量表首字节00 00 05 20,得到栈顶地址为0x20050000,但STM32F469的SRAM范围仅为0x20000000~0x2002FFFF(192KB),该地址超出SRAM边界,会直接导致App启动后栈溢出崩溃。

  2. RTOS任务清理缺失
    跳转操作在FreeRTOS任务中直接执行,未终止RTOS调度器,内核残留的任务状态、定时器等会干扰App的正常运行。

  3. 外设DeInit不完整
    prvPeriph_DeInit()仅对TIM6、TIM2执行了DeInit,Bootloader中使用过的其他外设(如EXTI、对应GPIO时钟等)未做清理,残留状态会影响App初始化。

  4. BASEPRI设置冗余
    prvBoot_DeInit()中先执行__disable_irq(),再设置__set_BASEPRI(0xf0)属于冗余操作,可能干扰后续中断状态恢复。

  5. App1链接脚本与启动文件匹配
    需确认App1链接脚本中_estack地址是否合法,以及启动文件中是否正确处理向量表偏移,避免覆盖Bootloader设置的SCB->VTOR

修正建议

1. 修复App1栈地址

修改App1链接脚本,将_estack设置为SRAM合法末端地址:

_estack = 0x2002FFFF; /* 192KB SRAM的有效末端地址 */

2. 完善RTOS调度终止

在跳转前先终止FreeRTOS调度:

void StartupEntry(void *argument) {
    (void) argument;
    for (;;) {
        HAL_GPIO_TogglePin(BM83_EN_GPIO_Port, BM83_EN_Pin);
        if (HAL_GPIO_ReadPin(ENC2_SW_GPIO_Port, ENC2_SW_Pin) == GPIO_PIN_SET) {
            vTaskEndScheduler(); // 终止FreeRTOS调度器
            prvJumpToApp(APP1_ADDRESS);
        }
        osDelay(20);
    }
}

3. 移除冗余BASEPRI设置

修改prvBoot_DeInit()

static void prvBoot_DeInit(void) {
    __disable_irq();

    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;

    prvSafeGPIODeInit();
    prvNVIC_Reset();
    prvPeriph_DeInit();

    HAL_RCC_DeInit();
    HAL_DeInit();
}

4. 补全外设DeInit

将Bootloader中使用过的所有外设加入DeInit列表,例如添加EXTI的DeInit:

static void prvPeriph_DeInit(void) {
    HAL_TIM_Base_DeInit(&htim6);
    HAL_TIM_Base_DeInit(&htim2);
    HAL_EXTI_DeInit(); // 示例:添加EXTI外设清理
    // 补充其他使用过的外设DeInit操作
}

内容的提问来源于stack exchange,提问作者mmercan

火山引擎 最新活动