为ARM Cortex-M系列芯片编写Bootloader

本文介绍如何为ARMCortex-M系列芯片编写Bootloader,包括CMSIS启动文件分析、Bootloader机理及向量表重定向应用等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://zhuanlan.zhihu.com/p/25356501


为ARM Cortex-M系列芯片编写Bootloader

为ARM Cortex-M系列芯片编写Bootloader

王小军 王小军
6 个月前

为ARM Cortex-M系列芯片编写Bootloader

本文仅在ARM Cortex M3/M4芯片上进行过测试

1.引言

Bootloader用于用户程序的引导,其用途在于软件启动、固件升级等,Bootloader编写的核心内容是向量表的重定位。为了读者能够比较清晰了解Bootloader的机制,小军会说明CMSIS启动文件的机理,为此本文分为以下三个方面:

  • CMSIS启动文件简单分析
  • Bootlader的机理及实现
  • 向量表重定向的应用

2.CMSIS启动文件简单分析

下述为Adu360(ARM Cortex-M3内核)芯片的启动代码,由于符合CMSIS标准的启动文件大同小异,本文将以此启动文件作为示例,详细代码如下:

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

                PRESERVE8
                THUMB

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; The NMI handler
                
;中间省略若干个中断向量

	            DCD     PWM2_Int_Handler          ; PWM2                       [38]
	            DCD     0                         ;                            [39]
__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors

                AREA    |.text|, CODE, READONLY

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

; Dummy Exception Handlers (infinite loops which can be modified)

NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP
                
;中间省略若干弱引用的中断函数

PWM2_Int_Handler        
                B       .

                ENDP

                ALIGN

                 IF      :DEF:__MICROLIB
                
                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit
                
                 ELSE
                
                 IMPORT  __use_two_region_memory
                 EXPORT  __user_initial_stackheap
                 
__user_initial_stackheap

                 LDR     R0, =  Heap_Mem
                 LDR     R1, =(Stack_Mem + Stack_Size)
                 LDR     R2, = (Heap_Mem +  Heap_Size)
                 LDR     R3, = Stack_Mem
                 BX      LR

                 ALIGN

                 ENDIF

                 END

启动程序还是比较简单的,其主要分为三个部分:

  • 堆栈的预分配
  • 中断向量表定义
  • 堆栈的初始化

ARM Cortex-M系列芯片堆栈的相关操作为C准备运行时环境,这里不做细说;中断向量表为芯片运行的根基,我们来重点分析一下中断向量表:

  • 中断向量表的起始为主栈指针(MSP)的初始值,用于在C运行时入口函数初始化堆栈之前建立基本的C运行时环境;
  • 紧接着是向量表的第一个中断向量复位向量,其不仅仅作为系统的一个中断存在,还是芯片执行程序的入口函数,其具体代码如下:
; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

在复位向量中调用了SystemInit函数及__main函数,SystemInit函数为CMSIS定义的芯片初始化函数,用于对芯片的时钟等进行初始化配置,__main函数为C运行时库的入口函数,所以跳转到Reset_handler函数中可以启动应用;

  • 后面则是各种弱引用的中断向量,可以被C代码中同名的强引用定义取代;

一般,ARM Cortex-M芯片从存储器地址0x00000000处开始执行程序,通过__initial_sp初始化主栈指针,通过执行Reset_handler执行main函数中的程序。

2.Bootloader机理及其实现

如果我们希望升级系统中的固件或者从SD卡(或网络)中加载程序执行,需要执行如下步骤:

  • 启动Bootloader
  • 执行Bootloader中的任务加载用户程序
  • 切换到用户程序中执行 这时,我们就需要用到ARM Cortex-M内核的向量表重定向机制,其提供了一个中断向量表偏移寄存器(SCB->VTOR),改寄存器中的值为向量表的起始地址。

其示例程序为:

/* 在调用此函数之前完成用户应用程序的加载和拷贝 */

 void retarget_vector(uint32_t new_addr)
 {
    __DMB();        /* 数据存储器屏障 */
    SCB->VTOR = new_addr;
    __DSB();        /* 数据同步屏障,保证在其后中断均为新的地址 */
 }

重定向之后,我们还需要跳转到用户程序中运行,其示例程序为:

/* 在调用此函数之前完成重定向操作 */

__asm void jump_to_application(uint32_t addr)
{
    LDR SP, [R0]            ; 设置用户程序MSP的值
    LDR PC, [R0, #4]        ; 运行用户程序的Reset_handler
}

在Bootloader的编写中还需要注意以下几点:

  • 固件采用hex格式还是bin格式

使用bin格式,因为hex文件附带地址信息,并不是我们需要的纯二进制文件;bin格式为按存储顺序的二进制文件

  • 注意ARM芯片的大小端字序

有可能存储设备和ARM芯片的大小端不一致,导致加载的程序不符合ARM芯片的大小端字序,从而导致程序无法运行;

  • VTOR的地址有条件限制

使用VTOR时需要将中断向量表的大小拓展到最近的2的幂次方,且新向量的基址必须要对齐到这个数值。

  • 什么样的固件才能升级

这里的固件升级对固件编译情况是有要求的,比如说想要把固件放到0x4000-0x20000的位置,固件在编译时需要设置其ROM的启动地址为0x4000,大小为0x1C000,bootloader复制代码到0x4000-0x20000,并且中断向量表重定向后固件才可以正常运行。

  • 跳转用户程序之前需要清理BootLoader使用过的资源

为了防止对用户程序产生副作用,Bootloader程序需要关闭所有使用过的芯片资源,最好关掉中断(感谢@望海楼提醒。 )

Bootloader的实现过程可以总结如下(详细见《ARM Cortex-M3 与 Cortex-M4 权威指南》):利用启动ROM中的向量表启动Bootloader

  1. 启动Bootloader中的任务,完成用户程序的加载
  2. 设置VTOR指向用户程序存储器中的向量表
  3. 跳转到用户程序存储器的向量表中的中断向量

3.向量表重定向的应用

  • Bootloader的编写
  • 应用程序加载到RAM中执行,其类似于Bootloader
  • 动态修改向量表,有些情况下ROM中可能会有一个中断的多个处理示例,可能在应用的不同阶段在它们之间切换,这种情况下,可以将向量表从程序存储中复制到RAM中,设置VTOR指向RAM中的向量表,RAM中的内容可以任意时间修改,因此可以在应用的不同阶段使用不同的中断向量。

此部分详细见《ARM Cortex-M3 与 Cortex-M4 权威指南》





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值