/*IAP跳转函数*/
void iap_load_app(uint32_t appxaddr)
{
    /* 1. 复位外设到默认状态 */
    USART_DeInit(USART1);      // 串口回归默认
    TIM_DeInit(TIM1);
    TIM_DeInit(TIM2);          // 定时器回归默认
    TIM_DeInit(TIM3);
    DMA_DeInit(DMA1_Channel5); // DMA回归默认
    GPIO_DeInit(GPIOA);
    GPIO_DeInit(GPIOB);
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_AFIO, DISABLE);
    /* 2. 关闭所有外设时钟(Bootloader用过的)*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, DISABLE);  // 关串口时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, DISABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, DISABLE);    // 关定时器时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, DISABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, DISABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, DISABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, DISABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, DISABLE);
    /* 3. 关闭自己开的中断 */
    NVIC_DisableIRQ(USART1_IRQn);
    NVIC_DisableIRQ(TIM1_UP_IRQn);
    NVIC_DisableIRQ(TIM3_IRQn);
    NVIC_DisableIRQ(EXTI2_IRQn);
    NVIC_DisableIRQ(EXTI3_IRQn);
    /* 4. 清除外设级挂起的中断标志 */
    USART_GetFlagStatus(USART1, USART_FLAG_IDLE);
    USART_ReceiveData(USART1);
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
    TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
    EXTI_ClearITPendingBit(EXTI_Line3);
    EXTI_ClearITPendingBit(EXTI_Line2);
    /* 5. 清除内核级挂起的中断标志 */
    NVIC_ClearPendingIRQ(USART1_IRQn);
    NVIC_ClearPendingIRQ(TIM1_UP_IRQn);
    NVIC_ClearPendingIRQ(TIM3_IRQn);
    NVIC_ClearPendingIRQ(EXTI2_IRQn);
    NVIC_ClearPendingIRQ(EXTI3_IRQn);
    //NVIC_ClearPendingIRQ(SysTick_IRQn);
    /* 6. 关闭全局中断(最重要)*/
    __disable_irq();
    /* 7. 关闭 SysTick(如果用了)*/
    SysTick->CTRL = 0;

    //(*(volatile  uint32_t *)appxaddr):取出MSP的值
    if (((*(volatile  uint32_t *)appxaddr) & 0x2FFE0000) == 0x20000000)     /* 检查栈顶地址是否合法.可以放在内部SRAM共64KB(0x20000000) */
    {
        /* 用户代码区第二个字为程序开始地址(复位地址) */
        jump2app = (iapfun) * (volatile uint32_t *)(appxaddr + 4);

        /* 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) */
        __set_MSP(*(volatile uint32_t *)appxaddr);

        /* 跳转到APP */
        jump2app();
    }
}
/*APP程序主函数*/
int main()
{  
    SCB->VTOR = FLASH_BASE | (0xC800 & (uint32_t)0xFFFFFE00);
    Serial_Init();
   
    printf("已经进入APP_2.0程序!!\r\n\r\n");
    printf("开始执行APP用户代码!!\r\n\r\n");
    while(1)
    {
    }
}

分析

在FreeRTOS中使用IAP时,需要在程序跳转之前先进行1-7操作,个人理解为先清场,防止对下载进来的APP程序的运行产生干扰;
旧跳转函数没有1-7操作,在裸机环境下APP程序下载完成之后可以正常运行,但在FreeRTOS环境下会卡在打印出“已”这里,添加1-7操作之后可以正常运行

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐