单片机
返回首页

MCU Bootload学习笔记分享

2025-09-30 来源:cnblogs

 启动流程

 CPU只会处理机器码,不同 CPU(如 ARM Cortex-M、x86、8051)支持的机器码格式不同。代码通过编译转为机器码的格式,存放在非易失性存储器中。存储器都有地址,可以存储多段数据,CPU通过设置的MSP的位置开始执行。

  • 从固定地址读取初始栈指针和复位向量(CPU会自动加载这两个值)

    Cortex-M内核规定

    复位后CPU从Flash起始地址(如GD32的0x08000000)按顺序读取以下两个关键值:

  • 主栈指针(MSP)初始值

  • 地址:0x08000000(Flash起始地址)。

  • 作用:CPU启动时自动加载到MSP寄存器,建立初始栈空间。

  • 复位向量(Reset_Handler地址)

  • 地址:0x08000004(即MSP地址 + 4)。

  • 作用:CPU跳转到该地址执行Reset_Handler函数。

  • Reset_Handler作为第一个执行的函数,可以从文件startup_gd32f30x_hd.s看到先进行了硬件的初始化操作,才进入main函数执行


    那如何通过bootload进行APP跳转呢? 

  • 跳转本质:通过手动设置MSP和PC寄存器,将控制权移交到用户程序。

  • 关键步骤:关闭中断 → 设置MSP → 重定向VTOR → 跳转Reset_Handler。

  • 关闭中断、设置MSP 、重定向VTOR(中断向量表)可有对应的寄存器或库函数继续设置。重点说说跳转到APP的Reset_Handler执行。


函数的本质:名字 = 地址

在单片机中,函数名就是它在Flash中的起始地址。例如:


void my_func() { /* 代码 */ }

编译后,my_func 可能存储在 0x08001000,那么 my_func 就代表 0x08001000。


那Reset_Handler也是同理,我们已知该函数的地址是固定的,只需在boot中调用APP的Reset_Handler,即可将程序跳转到APP执行。


这时我们就可以编写我们的boot跳转函数



#define MY_APP_ADDR 0x08010000 //定义APP起始地址


void jump_to_app(void)

{

    typedef void (*app_func_t)(void);

       

    u32 app_addr = MY_APP_ADDR;

    u32 stk_addr = *((__IO uint32_t *)app_addr); // 从 MY_APP_ADDR + 0x0 读取 MSP

    // 获取 Reset_Handler 地址(MY_APP_ADDR + 0x4)

    app_func_t app_func = (app_func_t)(*((__IO uint32_t *)(app_addr + 4)));


    /*

       判断APP的MSP、Reset_Handler 地址是否合法。关闭中断等操作。

    */



    __set_MSP(stk_addr);//设置修改主栈指针MSP


    app_func();//执行APP的Reset_Handler


}


 如何定义我们的MY_APP_ADDR ?可通过KIEL设置。


这是boot分配的rom,起始地址为0x08000000 大小为0x10000 也就是64k。

以下是APP的分配,APP起始地址的boot之后,从0x8010000开始,大小为0x30000。也就是192k。该芯片提供的ROM为256K,

通过以上的设置跳转,我们发现APP仍然无法正常使用。



在上述的关键步骤中,发现我们缺少了重定向VTOR。


在计算机体系结构中,VTOR(Vector Table Offset Register,向量表偏移寄存器) 是某些ARM架构处理器(如Cortex-M系列)中的一个特殊功能寄存器,用于重定位中断向量表在内存中的存储位置。


中断向量表是 Cortex-M 的“中断处理目录”,规定了 启动初始化、中断跳转的硬性规则,


中断向量表的位置

默认存储位置:


Cortex-M 内核复位时,默认从 0x00000000(或 Flash 起始地址,如 0x08000000)加载中断向量表。


Cortex-M3/M4/M7 支持通过 VTOR(Vector Table Offset Register) 动态修改向量表位置(如 0x08010000)。

Cortex-M0/M0+ 无 VTOR,向量表必须放在 0x00000000(通常由芯片厂商映射到 Flash)。

对齐要求:


向量表必须 256 字节对齐(地址低 8 位为 0),否则会触发硬件错误。

中断向量表与MSP的区别

对比项中断向量表的位置MSP(主栈指针)
存储内容所有中断服务函数(ISR)的地址仅占向量表的第一个条目(0x00)
作用告诉 CPU 中断时跳转到哪个函数定义程序运行时的栈顶地址
修改方式通过 VTOR 寄存器(Cortex-M3/M4/M7)直接修改 向量表[0] 或调用 __set_MSP()
硬件依赖所有 Cortex-M 均需向量表所有 Cortex-M 均需 MSP 初始化
既然VTOR是通过寄存器,core_cm4.h中就定义了,我们可以直接操作。在系统初始化的使用重新定义该表的地址即可。


core_cm4.h


/** brief  Structure type to access the System Control Block (SCB).

 */

typedef struct

{

  __I  uint32_t CPUID;                   /*!< Offset: 0x000 (R/ )  CPUID Base Register                                   */

  __IO uint32_t ICSR;                    /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register                  */

  __IO uint32_t VTOR;                    /*!< Offset: 0x008 (R/W)  Vector Table Offset Register                          */

  __IO uint32_t AIRCR;                   /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset Control Register      */

  __IO uint32_t SCR;                     /*!< Offset: 0x010 (R/W)  System Control Register                               */

  __IO uint32_t CCR;                     /*!< Offset: 0x014 (R/W)  Configuration Control Register                        */

  __IO uint8_t  SHP[12];                 /*!< Offset: 0x018 (R/W)  System Handlers Priority Registers (4-7, 8-11, 12-15) */

  __IO uint32_t SHCSR;                   /*!< Offset: 0x024 (R/W)  System Handler Control and State Register             */

  __IO uint32_t CFSR;                    /*!< Offset: 0x028 (R/W)  Configurable Fault Status Register                    */

  __IO uint32_t HFSR;                    /*!< Offset: 0x02C (R/W)  HardFault Status Register                             */

  __IO uint32_t DFSR;                    /*!< Offset: 0x030 (R/W)  Debug Fault Status Register                           */

  __IO uint32_t MMFAR;                   /*!< Offset: 0x034 (R/W)  MemManage Fault Address Register                      */

  __IO uint32_t BFAR;                    /*!< Offset: 0x038 (R/W)  BusFault Address Register                             */

  __IO uint32_t AFSR;                    /*!< Offset: 0x03C (R/W)  Auxiliary Fault Status Register                       */

  __I  uint32_t PFR[2];                  /*!< Offset: 0x040 (R/ )  Processor Feature Register                            */

  __I  uint32_t DFR;                     /*!< Offset: 0x048 (R/ )  Debug Feature Register                                */

  __I  uint32_t ADR;                     /*!< Offset: 0x04C (R/ )  Auxiliary Feature Register                            */

  __I  uint32_t MMFR[4];                 /*!< Offset: 0x050 (R/ )  Memory Model Feature Register                         */

  __I  uint32_t ISAR[5];                 /*!< Offset: 0x060 (R/ )  Instruction Set Attributes Register                   */

       uint32_t RESERVED0[5];

  __IO uint32_t CPACR;                   /*!< Offset: 0x088 (R/W)  Coprocessor Access Control Register                   */

} SCB_Type;



#define SCB                 ((SCB_Type       *)     SCB_BASE      )   

/*!< SCB configuration struct           */


 system_gd32f30x.c


/*!

    brief      set the NVIC vector table base address

    param[in]  nvic_vict_tab: the RAM or FLASH base address

      arg        NVIC_VECTTAB_RAM: RAM base address

      are        NVIC_VECTTAB_FLASH: Flash base address

    param[in]  offset: Vector Table offset

    param[out] none

    retval     none

*/

void nvic_vector_table_set(uint32_t nvic_vict_tab, uint32_t offset)

{

    SCB->VTOR = nvic_vict_tab | (offset & NVIC_VECTTAB_OFFSET_MASK);

    __DSB();

}



/*!

    brief      setup the microcontroller system, initialize the system

    param[in]  none

    param[out] none

    retval     none

*/

void SystemInit (void)

{

  /* FPU settings */

#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)

    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */

#endif

    /* reset the RCU clock configuration to the default reset state */

    /* Set IRC8MEN bit */

    RCU_CTL |= RCU_CTL_IRC8MEN;

    while(0U == (RCU_CTL & RCU_CTL_IRC8MSTB)){

    }

    RCU_MODIFY(0x50);

    

    RCU_CFG0 &= ~RCU_CFG0_SCS;


#if (defined(GD32F30X_HD) || defined(GD32F30X_XD))

    /* reset HXTALEN, CKMEN and PLLEN bits */

    RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);

    /* disable all interrupts */

    RCU_INT = 0x009f0000U;

#elif defined(GD32F30X_CL)

    /* Reset HXTALEN, CKMEN, PLLEN, PLL1EN and PLL2EN bits */

    RCU_CTL &= ~(RCU_CTL_PLLEN |RCU_CTL_PLL1EN | RCU_CTL_PLL2EN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);

    /* disable all interrupts */

    RCU_INT = 0x00ff0000U;

#endif


    /* reset HXTALBPS bit */

    RCU_CTL &= ~(RCU_CTL_HXTALBPS);

    

    /* Reset CFG0 and CFG1 registers */

    RCU_CFG0 = 0x00000000U;

    RCU_CFG1 = 0x00000000U;


    /* configure the system clock source, PLL Multiplier, AHB/APBx prescalers and Flash settings */

    system_clock_config();


/* add app addr */

nvic_vector_table_set(NVIC_VECTTAB_FLASH, RT_APP_OFFSET);

}


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦四级调频发射机

  • 500W MOS场效应管电源逆变器,12V转110V/220V

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • 12V转110V/220V 500W逆变器

  • DS1669数字电位器

    相关电子头条文章