STM32 Bootloader跳转到APP程序中无法启动实时系统

STM32 Bootloader跳转到APP程序中无法启动实时系统

一、问题描述:

STM32G431 bootloader跳转到APP中执行,能够进入APP的main函数,但main函数里创建实时系统(CMSIS-RTOS2)失败。

二、问题解决过程:

首先明确:APP程序稍作修改(去掉中断向量偏移,flash起始地址改为0x08000000),不用bootloader,直接烧录到单片机里是肯定可以执行的,实时系统也是可以运行的。
以下是bootloader跳转处的程序(bootloader也用的是CMSIS-RTOS2系统)。

  if((Flash_Read_Word(AppCode_Address) & 0xFF000000 ) == 0x20000000)//检查栈顶地址高字节是否为0x20
  {
    osKernelLock();
    jump2app=(iapFun)*(volatile uint32_t*)(AppCode_Address+4);//APP代码区第二个字存放复位中断的地址
    MSR_MSP(*(volatile uint32_t*)AppCode_Address);//APP代码区的第一个字用于存放栈顶地址
    jump2app();//跳转到APP
    osKernelUnlock();
  }

此程序能跳到APP中,但无法创建实时系统。

1、首先想到的是中断,所以在bootloader 添加__set_PRIMASK(1)或__set_FAULTMASK(1)关闭所有中断,在APP中添加__set_PRIMASK(0)或__set_FAULTMASK(0)打开中断。

    if((Flash_Read_Word(AppCode_Address) & 0xFF000000 ) == 0x20000000)//检查栈顶地址高字节是否为0x20
    {
        osKernelLock();
        __set_FAULTMASK(1);//__set_PRIMASK(1);     
        jump2app=(iapFun)*(volatile uint32_t*)(AppCode_Address+4);//APP代码区第二个字存放复位中断的地址
        MSR_MSP(*(volatile uint32_t*)AppCode_Address);//APP代码区的第一个字用于存放栈顶地址
        jump2app();//跳转到APP
        osKernelUnlock();
    }

没有成功,接下来我于是调整__set_PRIMASK(0)或__set_FAULTMASK(0)的位置,比如说放到main函数的开头、稍后或者bootloader中,作尝试,都没有成功。

    if((Flash_Read_Word(AppCode_Address) & 0xFF000000 ) == 0x20000000)//检查栈顶地址高字节是否为0x20
    {
        osKernelLock();
        __set_PRIMASK(1);     
        jump2app=(iapFun)*(volatile uint32_t*)(AppCode_Address+4);//APP代码区第二个字存放复位中断的地址
        MSR_MSP(*(volatile uint32_t*)AppCode_Address);//APP代码区的第一个字用于存放栈顶地址
        __set_PRIMASK(0);
        jump2app();//跳转到APP
        osKernelUnlock();
    }

2、接下来我对程序究竟死在哪儿,做了研究。

如果不加__set_PRIMASK(1),CMSIS-RTOS2的osKernelInitialize()函数返回值是-6,然后会再往下执行。

/// Status code values returned by CMSIS-RTOS functions.
typedef enum {
  osOK                      =  0,         ///< Operation completed successfully.
  osError                   = -1,         ///< Unspecified RTOS error: run-time error but no other error message fits.
  osErrorTimeout            = -2,         ///< Operation not completed within the timeout period.
  osErrorResource           = -3,         ///< Resource not available.
  osErrorParameter          = -4,         ///< Parameter error.
  osErrorNoMemory           = -5,         ///< System is out of memory: it was impossible to allocate or reserve memory for the operation.
  osErrorISR                = -6,         ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.
  osStatusReserved          = 0x7FFFFFFF  ///< Prevents enum down-size compiler optimization.
} osStatus_t;

如果加上__set_PRIMASK(1),程序直接运行到HardFault_Handler中断里了。

/**
  * @brief  This function handles Hard Fault exception.
  * @param  None
  * @retval None
  */
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

3、在bootloader里对分中断再做处理,禁止Systick、禁止串口中断、禁止外部中断。

    if((Flash_Read_Word(AppCode_Address) & 0xFF000000 ) == 0x20000000)//检查栈顶地址高字节是否为0x20
    {
        osKernelLock();
        __set_PRIMASK(1);
        
        SysTick->CTRL = 0X00;//禁止SysTick
        
        NVIC_DisableIRQ(USART1_IRQn);
        NVIC_DisableIRQ(EXTI2_IRQn);
        
        NVIC_ClearPendingIRQ(USART1_IRQn);
        NVIC_ClearPendingIRQ(EXTI2_IRQn);
        
        jump2app=(iapFun)*(volatile uint32_t*)(AppCode_Address+4);//APP代码区第二个字存放复位中断的地址
        MSR_MSP(*(volatile uint32_t*)AppCode_Address);//APP代码区的第一个字用于存放栈顶地址

        jump2app();//跳转到APP
        osKernelUnlock();
    }

没有成功,再对NVIC作清理。

    if((Flash_Read_Word(AppCode_Address) & 0xFF000000 ) == 0x20000000)//检查栈顶地址高字节是否为0x20
    {
        osKernelLock();
        __set_PRIMASK(1);
        
        SysTick->CTRL = 0X00;//禁止SysTick
        
        NVIC_DisableIRQ(USART1_IRQn);
        NVIC_DisableIRQ(EXTI2_IRQn);
        
        NVIC_ClearPendingIRQ(USART1_IRQn);
        NVIC_ClearPendingIRQ(EXTI2_IRQn);
        
        for(i=0;i<8;i++)//适用于中断编号是16~255
        {
            NVIC->ICER[i] = 0xFFFFFFFF;//写1清除中断使能
            NVIC->ICPR[i] = 0xFFFFFFFF;//写1清除中断挂起状态
        }
        
        jump2app=(iapFun)*(volatile uint32_t*)(AppCode_Address+4);//APP代码区第二个字存放复位中断的地址
        MSR_MSP(*(volatile uint32_t*)AppCode_Address);//APP代码区的第一个字用于存放栈顶地址

        jump2app();//跳转到APP
        osKernelUnlock();
    }

没有成功。

4、接下来,将APP main函数里的程序作屏蔽,能屏蔽的都全部屏蔽了,没有成功,又在bootloader里添加了禁止指令Cache、Data Cache等程序,没有成功,后来怀疑svc中断是不是没有打开。

5、猜想是不是bootloader占了内存,APP运行也占了内存,内存不够呢?于是屏蔽没用到线程,给bootloader的CMSIS-RTOS2堆分配6K,给APP的CMSIS-RTOS2堆分配15K,单片机的总内存是32K;没有成功。

6、看了两个帖子,事情有了眉目。

https://club.rt-thread.org/ask/question/425321.html
https://club.rt-thread.org/ask/question/425358.html

改成这样:

    if((Flash_Read_Word(AppCode_Address) & 0xFF000000 ) == 0x20000000)//检查栈顶地址高字节是否为0x20
    {
        osKernelLock();
        __set_PRIMASK(1);
        SysTick->CTRL = 0X00;//禁止SysTick
        SysTick->LOAD = 0;
        SysTick->VAL = 0;
        
        NVIC_DisableIRQ(USART1_IRQn);
        NVIC_DisableIRQ(EXTI2_IRQn);
        
        NVIC_ClearPendingIRQ(USART1_IRQn);
        NVIC_ClearPendingIRQ(EXTI2_IRQn);
        
        for(i=0;i<8;i++)//适用于中断编号是16~255
        {
            NVIC->ICER[i] = 0xFFFFFFFF;//写1清除中断使能
            NVIC->ICPR[i] = 0xFFFFFFFF;//写1清除中断挂起状态
        }
        
        __set_PRIMASK(0);
//        SCB->ICSR = 0x9E000000;

        __HAL_FLASH_PREFETCH_BUFFER_DISABLE();
        __HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
        __HAL_FLASH_DATA_CACHE_DISABLE();
        __HAL_FLASH_INSTRUCTION_CACHE_RESET();
        __HAL_FLASH_DATA_CACHE_RESET();
        
        jump2app=(iapFun)*(volatile uint32_t*)(AppCode_Address+4);//APP代码区第二个字存放复位中断的地址
        MSR_MSP(*(volatile uint32_t*)AppCode_Address);//APP代码区的第一个字用于存放栈顶地址
        
        SCB->VTOR = 0x08007800;
        __set_CONTROL(0);
        
        jump2app();//跳转到APP
        
        osKernelUnlock();
    }

成功,于是将屏蔽的线程全部打开、堆分配给够,再试,成功。

三、问题解决:

成功的关键是添加了__set_CONTROL(0),这个的作用是将0写到CONTROL寄存器,即程序运行在特权访问等级,使用MSP主栈指针。
CONTROL寄存器
关于CONTROL寄存器的介绍,参考《Cortex M3与M4权威指南》的86页(电子版)、《ARM Cortex-M3与Cortex-M4权威指南(第三版)》的59页(中文纸质)。

有以下的分析:
bootloader的程序本来就运行在特权访问等级和进程栈指针(PSP)模式下,所以__set_CONTROL(0)主要作用是将PSP改为了MSP,所以跳转后APP里的实时系统就可以运行起来了。(周四花费半天、周五花费一天、周日花费半天)

  • 10
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
你好!对于你的问题,可能有几个原因导致HAL_Delay函数在跳转APP程序无法工作。以下是一些常见的可能原因和解决方法: 1. 时钟配置:确保在跳转APP程序后,时钟配置与bootloader的配置相匹配。如果时钟配置不正确,可能会导致HAL_Delay函数无法正常工作。可以使用调试器来检查时钟配置是否正确。 2. 断配置:在跳转APP程序后,确保断配置与bootloader的配置相同。如果断配置不正确,可能会干扰HAL_Delay函数的正常运行。可以使用调试器来检查断配置是否正确。 3. 调用位置:确定你在APP程序正确地调用了HAL_Delay函数。确保在需要延时的地方正确地调用了该函数,并且没有其他代码干扰了延时的执行。 4. 代码重定位:如果你的APP程序使用了代码重定位(例如使用链接脚本),可能需要适当地配置重定位地址。确保重定位地址与bootloader的配置相匹配,以确保HAL_Delay函数可以正确地执行。 5. 系统时钟频率:检查系统时钟频率是否正确配置,并且与HAL库的设置相匹配。如果系统时钟频率不正确,可能会导致HAL_Delay函数无法正常工作。 如果以上方法都没有解决问题,那么可能需要更详细地检查你的APP程序代码,以确定是否有其他因素导致HAL_Delay函数无法正常工作。希望这些信息对你有所帮助!如果你有任何其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值