TI Cortex M3串口转以太网例程分析2-----bootloader

        bootloader是TI串口转以太网代码的一小部分,位于Flash开始的4KB空间内。它的一个重要作用是在应用远程升级,可以通过串口、USB、IIC、以太网等通道进行远程固件升级。bootloader是CPU启动后最先执行的程序,它会把自己拷贝到SRAM,并判断是否有固件升级,如果有升级请求,则执行升级程序;反之,执行用户程序。

  一.流程图       

           由于这里只考虑基于以太网的bootloader,其流程图如图2-1所示:


图2-1

 二.配置文件     

        由于bootlaoder可以使用串口、USB、IIC、以太网等通道进行远程固件升级,那么怎么样配置才可以使用以太网呢?这就牵扯到bl_config文件。此文件是专门配置bootloader的。代码就不贴了,看一下这里面几个必须配置的选项:

1. 以下至少且只能定义一个,用于指明使用何种方式升级。

        CAN_ENABLE_UPDATE,       

        ENET_ENABLE_UPDATE,

        I2C_ENABLE_UPDATE,

        SSI_ENABLE_UPDATE,

        UART_ENABLE_UPDATE,

        USB_ENABLE_UPDATE

2. 以下必须定义

        APP_START_ADDRESS                        用户程序启动地址

        VTABLE_START_ADDRESS                 用户程序向量表起始地址

        FLASH_PAGE_SIZE                               Flash页大小,TI的目前为止都为1K

        STACK_SIZE                                           堆栈大小

3. 当选择了以太网升级后,以下必须定义

        CRYSTAL_FREQ                                     目标板晶振频率

三.bootloader启动代码分析

          不少人不喜欢分析汇编文件,甚至总想绕过汇编。网络上也出现一些人教导初学者学习单片机的时候直接用C语言编程,避开汇编。我个人是极其不同意这种“速成”方法的。作为一名合格的嵌入式工程师或者说爱好者,汇编绝不可回避。汇编能帮助理解硬件,特别是CPU结构、存储和寻址等等;现在的嵌入式程序虽然绝大多数是用C编写的,但要想精通C语言,必须具有汇编基础,任何技术都是入门容易,精通难,因此要想深入理解C的指针、数组甚至是变量存储,还非少不了汇编不可;再者,有些地方必须使用汇编,比如一些实时性要求高的模块(不常见),还有就是接下来要说的启动代码。先附源代码。

;******************************************************************************
;
; bl_startup_rvmdk.S - Startup code for RV-MDK.
;
; Copyright (c) 2007-2010 Texas Instruments Incorporated.  All rights reserved.
; Software License Agreement
; 
; Texas Instruments (TI) is supplying this software for use solely and
; exclusively on TI's microcontroller products. The software is owned by
; TI and/or its suppliers, and is protected under applicable copyright
; laws. You may not combine this software with "viral" open-source
; software in order to form a larger program.
; 
; THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
; NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
; NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
; A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
; CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
; DAMAGES, FOR ANY REASON WHATSOEVER.
; 
; This is part of revision 6288 of the Stellaris Firmware Development Package.
;
;******************************************************************************

    include bl_config.inc

;******************************************************************************
;
; A couple of defines that would normally be obtained from the appropriate C
; header file, but must be manually provided here since the Keil compiler does
; not have a mechanism for passing assembly source through the C preprocessor.
; 以下定义通常在C头文件中定义过,但仍要在这里定义,因为keil编译器没有从汇编器直接
; 调用C预编译器的机制.
;
;******************************************************************************
SYSCTL_RESC                     equ     0x400fe05c	  ;复位原因
SYSCTL_RESC_MOSCFAIL            equ     0x00010000
NVIC_VTABLE                     equ     0xe000ed08	  ;向量表偏移量寄存器

;******************************************************************************
;
; Put the assembler into the correct configuration.
;
;******************************************************************************
    thumb			   ;thumb指令
    require8
    preserve8

;******************************************************************************
;
; The stack gets placed into the zero-init section.
; 将堆放到零初始化区
;
;******************************************************************************
    area    ||.bss||, noinit, align=2	 ;4字节对齐,2的2次幂

;******************************************************************************
;
; Allocate storage for the stack.
; 为堆分配空间,STACK_SIZE在bl_config.h中定义的宏,通过bl_config.inc加载armcc
;
;******************************************************************************
g_pulStack
    space   _STACK_SIZE * 4

;******************************************************************************
;
; This portion of the file goes into the reset section.
;
;******************************************************************************
    area    RESET, code, readonly, align=3	  ;8字节对齐?

;******************************************************************************
;
; The minimal vector table for a Cortex-M3 processor.
;
;******************************************************************************
    export  __Vectors
__Vectors
    dcd     g_pulStack + (_STACK_SIZE * 4)  ; Offset 00: Initial stack pointer 初始化堆栈指针
    if      :def:_FLASH_PATCH_COMPATIBLE
    dcd     Reset_Handler + 0x1000          ; Offset 04: Reset handler	 为某些Flash打了补丁的器件
    dcd     NmiSR + 0x1000                  ; Offset 08: NMI handler
    dcd     FaultISR + 0x1000               ; Offset 0C: Hard fault handler
    else
    dcd     Reset_Handler                   ; Offset 04: Reset handler
    dcd     NmiSR                           ; Offset 08: NMI handler
    dcd     FaultISR                        ; Offset 0C: Hard fault handler
    endif
    dcd     IntDefaultHandler               ; Offset 10: MPU fault handler
    dcd     IntDefaultHandler               ; Offset 14: Bus fault handler
    dcd     IntDefaultHandler               ; Offset 18: Usage fault handler
    dcd     0                               ; Offset 1C: Reserved
    dcd     0                               ; Offset 20: Reserved
    dcd     0                               ; Offset 24: Reserved
    dcd     0                               ; Offset 28: Reserved
    if      :def:_FLASH_PATCH_COMPATIBLE
    dcd     UpdateHandler + 0x1000          ; Offset 2C: SVCall handler	  SVC异常
    else
    dcd     UpdateHandler                   ; Offset 2C: SVCall handler
    endif
    dcd     IntDefaultHandler               ; Offset 30: Debug monitor handler
    dcd     0                               ; Offset 34: Reserved
    dcd     IntDefaultHandler               ; Offset 38: PendSV handler
    if      :def:_ENET_ENABLE_UPDATE
    import  SysTickIntHandler
    dcd     SysTickIntHandler               ; Offset 3C: SysTick handler
    else
    dcd     IntDefaultHandler               ; Offset 3C: SysTick handler
    endif
    if      :def:_UART_ENABLE_UPDATE :land: :def:_UART_AUTOBAUD
    import  GPIOIntHandler
    dcd     GPIOIntHandler                  ; Offset 40: GPIO port A handler
    else
    dcd     IntDefaultHandler               ; Offset 40: GPIO port A handler
    endif
    if      :def:_USB_ENABLE_UPDATE :lor:                                     \
            (_APP_START_ADDRESS != _VTABLE_START_ADDRESS) :lor:               \
            :def:_FLASH_PATCH_COMPATIBLE
    dcd     IntDefaultHandler               ; Offset 44: GPIO Port B
    dcd     IntDefaultHandler               ; Offset 48: GPIO Port C
    dcd     IntDefaultHandler               ; Offset 4C: GPIO Port D
    dcd     IntDefaultHandler               ; Offset 50: GPIO Port E
    dcd     IntDefaultHandler               ; Offset 54: UART0 Rx and Tx
    dcd     IntDefaultHandler               ; Offset 58: UART1 Rx and Tx
    dcd     IntDefaultHandler               ; Offset 5C: SSI0 Rx and Tx
    dcd     IntDefaultHandler               ; Offset 60: I2C0 Master and Slave
    dcd     IntDefaultHandler               ; Offset 64: PWM Fault
    dcd     IntDefaultHandler               ; Offset 68: PWM Generator 0
    dcd     IntDefaultHandler               ; Offset 6C: PWM Generator 1
    dcd     IntDefaultHandler               ; Offset 70: PWM Generator 2
    dcd     IntDefaultHandler               ; Offset 74: Quadrature Encoder 0
    dcd     IntDefaultHandler               ; Offset 78: ADC Sequence 0
    dcd     IntDefaultHandler               ; Offset 7C: ADC Sequence 1
    dcd     IntDefaultHandler               ; Offset 80: ADC Sequence 2
    dcd     IntDefaultHandler               ; Offset 84: ADC Sequence 3
    dcd     IntDefaultHandler               ; Offset 88: Watchdog timer
    dcd     IntDefaultHandler               ; Offset 8C: Timer 0 subtimer A
    dcd     IntDefaultHandler               ; Offset 90: Timer 0 subtimer B
    dcd     IntDefaultHandler               ; Offset 94: Timer 1 subtimer A
    dcd     IntDefaultHandler               ; Offset 98: Timer 1 subtimer B
    dcd     IntDefaultHandler               ; Offset 9C: Timer 2 subtimer A
    dcd     IntDefaultHandler               ; Offset A0: Timer 2 subtimer B
    dcd     IntDefaultHandler               ; Offset A4: Analog Comparator 0
    dcd     IntDefaultHandler               ; Offset A8: Analog Comparator 1
    dcd     IntDefaultHandler               ; Offset AC: Analog Comparator 2
    dcd     IntDefaultHandler               ; Offset B0: System Control
    if      :def:_FLASH_PATCH_COMPATIBLE
    dcd     0x00000881                      ; Offset B4: FLASH Control
    else
    dcd     IntDefaultHandler               ; Offset B4: FLASH Control
    endif
    endif
    if      :def:_USB_ENABLE_UPDATE :lor:                                     \
            (_APP_START_ADDRESS != _VTABLE_START_ADDRESS)
    dcd     IntDefaultHandler               ; Offset B8: GPIO Port F
    dcd     IntDefaultHandler               ; Offset BC: GPIO Port G
    dcd     IntDefaultHandler               ; Offset C0: GPIO Port H
    dcd     IntDefaultHandler               ; Offset C4: UART2 Rx and Tx
    dcd     IntDefaultHandler               ; Offset C8: SSI1 Rx and Tx
    dcd     IntDefaultHandler               ; Offset CC: Timer 3 subtimer A
    dcd     IntDefaultHandler               ; Offset D0: Timer 3 subtimer B
    dcd     IntDefaultHandler               ; Offset D4: I2C1 Master and Slave
    dcd     IntDefaultHandler               ; Offset D8: Quadrature Encoder 1
    dcd     IntDefaultHandler               ; Offset DC: CAN0
    dcd     IntDefaultHandler               ; Offset E0: CAN1
    dcd     IntDefaultHandler               ; Offset E4: CAN2
    dcd     IntDefaultHandler               ; Offset E8: Ethernet
    dcd     IntDefaultHandler               ; Offset EC: Hibernation module
    if      :def: _USB_ENABLE_UPDATE
    import  USB0DeviceIntHandler
    dcd     USB0DeviceIntHandler            ; Offset F0: USB 0 Controller
    else
    dcd     IntDefaultHandler               ; Offset F0: USB 0 Controller
    endif
    endif

;******************************************************************************
;
; Initialize the processor by copying the boot loader from flash to SRAM, zero
; filling the .bss section, and moving the vector table to the beginning of
; SRAM.  The return address is modified to point to the SRAM copy of the boot
; loader instead of the flash copy, resulting in a branch to the copy now in
; SRAM.
; 初始化处理器,将boot loader从flash拷贝到SRAM,将.bss区用零填充并将向量表重映射到
; SRAM的开始处.
;
;******************************************************************************
    export  ProcessorInit
ProcessorInit
    ;
    ; Copy the code image from flash to SRAM.
    ;
    if      :def:_FLASH_PATCH_COMPATIBLE
    movs    r0, #0x1000
    else
    movs    r0, #0x0000		 
    endif
    movs    r1, #0x0000
    movt    r1, #0x2000		 ;将16位的立即数放到寄存器的高16位,低位不受影响
    import  ||Image$SRAM$ZI$Base||  ;为汇编器提供一个在当前汇编程序中未定义的符号
    ldr     r2, =||Image$SRAM$ZI$Base||	;SRAM区中的ZI输出节执行地址
copy_loop
        ldr     r3, [r0], #4
        str     r3, [r1], #4
        cmp     r1, r2
        blt     copy_loop

    ;
    ; Zero fill the .bss section.将.bss区用零填充
    ;
    movs    r0, #0x0000
    import  ||Image$SRAM$ZI$Limit||	   ;SRAM区中ZI 输出节末尾地址后面的字节地址
    ldr     r2, =||Image$SRAM$ZI$Limit||
zero_loop
        str     r0, [r1], #4
        cmp     r1, r2
        blt     zero_loop

    ;
    ; Set the vector table pointer to the beginning of SRAM.
	; 将向量表指针指向SRAM开始处
    ;
    movw    r0, #(NVIC_VTABLE & 0xffff)		;放入r0低16位,高位清零
    movt    r0, #(NVIC_VTABLE >> 16)		;NVIC_VTABLE=0xe000ed08(向量表偏移量寄存器)
    movs    r1, #0x0000
    movt    r1, #0x2000
    str     r1, [r0]						;向量表重定位到0x2000 0000处

    ;
    ; Return to the caller.返回
    ;
    bx      lr

;******************************************************************************
;
; The reset handler, which gets called when the processor starts.
;
;******************************************************************************
    export  Reset_Handler
Reset_Handler
    ;
    ; Initialize the processor.
    ;
    bl      ProcessorInit

    ;
    ; Branch to the SRAM copy of the reset handler.
    ;
    ldr     pc, =Reset_Handler_In_SRAM       ;进入SRAM执行程序

;******************************************************************************
;
; The NMI handler.
;
;******************************************************************************
NmiSR
    if      :def:_ENABLE_MOSCFAIL_HANDLER
    ;
    ; Grab the fault frame from the stack (the stack will be cleared by the
    ; processor initialization that follows).
    ;
    ldm     sp, {r4-r11}
    mov     r12, lr

    ;
    ; Initialize the processor.
    ;
    bl      ProcessorInit

    ;
    ; Branch to the SRAM copy of the NMI handler.
    ;
    ldr     pc, =NmiSR_In_SRAM
    else
    ;
    ; Loop forever since there is nothing that we can do about a NMI.
    ;
    b       .
    endif

;******************************************************************************
;
; The hard fault handler.
;
;******************************************************************************
FaultISR
    ;
    ; Loop forever since there is nothing that we can do about a hard fault.
    ;
    b       .

;******************************************************************************
;
; The update handler, which gets called when the application would like to
; start an update.
; 升级服务函数,当应用程序想要开始升级时,调用这个函数.
;
;******************************************************************************
UpdateHandler
    ;
    ; Initialize the processor.	初始化处理器
    ;
    bl      ProcessorInit		  ;调用子程序

    ;
    ; Branch to the SRAM copy of the update handler.
    ;
    ldr     pc, =UpdateHandler_In_SRAM

;******************************************************************************
;
; This portion of the file goes into the text section.
;
;******************************************************************************
    align   4
    area    ||.text||, code, readonly, align=2

Reset_Handler_In_SRAM
    ;
    ; Call the user-supplied low level hardware initialization function
    ; if provided.
	; 如果用户提供了底层硬件初始化函数,则调用这个函数
    ;
    if      :def:_BL_HW_INIT_FN_HOOK
    import  $_BL_HW_INIT_FN_HOOK
    bl      $_BL_HW_INIT_FN_HOOK
    endif

    ;
    ; See if an update should be performed.
	; 检查是否有升级请求
    ;
    import  CheckForceUpdate
    bl      CheckForceUpdate
    cbz     r0, CallApplication	   ;结果为零则转移(只能跳到下一行)

    ;
    ; Configure the microcontroller.
    ;
EnterBootLoader
    if      :def:_ENET_ENABLE_UPDATE
    import  ConfigureEnet
    bl      ConfigureEnet
    elif    :def:_CAN_ENABLE_UPDATE
    import  ConfigureCAN
    bl      ConfigureCAN
    elif    :def:_USB_ENABLE_UPDATE
    import  ConfigureUSB
    bl      ConfigureUSB
    else
    import  ConfigureDevice
    bl      ConfigureDevice
    endif

    ;
    ; Call the user-supplied initialization function if provided.
	; 如果用户提供了初始化函数,则调用.
    ;
    if      :def:_BL_INIT_FN_HOOK
    import  $_BL_INIT_FN_HOOK
    bl      $_BL_INIT_FN_HOOK
    endif

    ;
    ; Branch to the update handler.
    ; 进入升级处理程序
	;
    if      :def:_ENET_ENABLE_UPDATE
    import  UpdateBOOTP
    b       UpdateBOOTP
    elif    :def:_CAN_ENABLE_UPDATE
    import  UpdaterCAN
    b       UpdaterCAN
    elif    :def:_USB_ENABLE_UPDATE
    import  UpdaterUSB
    b       UpdaterUSB
    else
    import  Updater
    b       Updater
    endif

    ;
    ; This is a second symbol to allow starting the application from the boot
    ; loader the linker may not like the perceived jump.
    ;
    export StartApplication
StartApplication
    ;
    ; Call the application via the reset handler in its vector table.  Load the
    ; address of the application vector table.
    ;
CallApplication
    ;
    ; Copy the application's vector table to the target address if necessary.
    ; Note that incorrect boot loader configuration could cause this to
    ; corrupt the code!  Setting VTABLE_START_ADDRESS to 0x20000000 (the start
    ; of SRAM) is safe since this will use the same memory that the boot loader
    ; already uses for its vector table.  Great care will have to be taken if
    ; other addresses are to be used.
	; 如果必要的话,复制应用程序的向量表到目标地址.
	; 请注意,不正确的boot loader配置会破坏整个程序!设置VTABLE_START_ADDRESS为
	; 0x2000 0000(从SRAM启动)也是可以的,因为这将和boot loader使用同样的内存
    ;
    if (_APP_START_ADDRESS != _VTABLE_START_ADDRESS) ;看应用程序的起始地址是否和应用程序的向量表存储地址相同
    movw    r0, #(_VTABLE_START_ADDRESS & 0xffff)
    if (_VTABLE_START_ADDRESS > 0xffff)
    movt    r0, #(_VTABLE_START_ADDRESS >> 16)
    endif
    movw    r1, #(_APP_START_ADDRESS & 0xffff)
    if (_APP_START_ADDRESS > 0xffff)
    movt    r1, #(_APP_START_ADDRESS >> 16)
    endif

    ;
    ; Calculate the end address of the vector table assuming that it has the
    ; maximum possible number of vectors.  We don't know how many the app has
    ; populated so this is the safest approach though it may copy some non
    ; vector data if the app table is smaller than the maximum.
	; 计算向量表的结束地址,假设向量表有最大数目. 我们不知道应用程序使用了多少
	; 向量表,但这样是最安全的
    ;
    movw    r2, #(70 * 4)
    adds    r2, r2, r0
VectorCopyLoop
        ldr     r3, [r1], #4
        str     r3, [r0], #4
        cmp     r0, r2
        blt     VectorCopyLoop
    endif

    ;
    ; Set the vector table address to the beginning of the application.
	; 将向量表重定位到应用程序开始处
    ;
    movw    r0, #(_VTABLE_START_ADDRESS & 0xffff)
    if (_VTABLE_START_ADDRESS > 0xffff)
    movt    r0, #(_VTABLE_START_ADDRESS >> 16)
    endif
    movw    r1, #(NVIC_VTABLE & 0xffff)	 			;向量表偏移寄存器
    movt    r1, #(NVIC_VTABLE >> 16)
    str     r0, [r1]

    ;
    ; Load the stack pointer from the application's vector table.
	; 从应用程序向量表装载用户堆栈.
    ;
    if (_APP_START_ADDRESS != _VTABLE_START_ADDRESS)
    movw    r0, #(_APP_START_ADDRESS & 0xffff)
    if (_APP_START_ADDRESS > 0xffff)
    movt    r0, #(_APP_START_ADDRESS >> 16)
    endif
    endif
    ldr     sp, [r0]

    ;
    ; Load the initial PC from the application's vector table and branch to
    ; the application's entry point.
    ;
    ldr     r0, [r0, #4]
    bx      r0

;******************************************************************************
;
; The update handler, which gets called when the application would like to
; start an update.
; 升级处理函数,当用户程序想要开始升级时,调用此函数
;
;******************************************************************************
UpdateHandler_In_SRAM
    ;
    ; Load the stack pointer from the vector table.
	; 从boot loader向量表中装载堆栈指针
    ;
    if      :def:_FLASH_PATCH_COMPATIBLE
    movs    r0, #0x1000
    else
    movs    r0, #0x0000
    endif
    ldr     sp, [r0]

    ;
    ; Call the user-supplied low level hardware initialization function
    ; if provided.
	; 调用用户提供的底层硬件初始化函数
    ;
    if      :def:_BL_HW_INIT_FN_HOOK
    bl      $_BL_HW_INIT_FN_HOOK
    endif

    ;
    ; Call the user-supplied re-initialization function if provided.
	; 调用用户提供的初始化函数
    ;
    if      :def:_BL_REINIT_FN_HOOK
    import  $_BL_REINIT_FN_HOOK
    bl      $_BL_REINIT_FN_HOOK
    endif

    ;
    ; Branch to the update handler.
	; 进入升级例程
    ;
    if      :def:_ENET_ENABLE_UPDATE
    b       UpdateBOOTP		   ;在bl_enet.c中
    elif    :def:_CAN_ENABLE_UPDATE
    import  AppUpdaterCAN
    b       AppUpdaterCAN
    elif    :def:_USB_ENABLE_UPDATE
    import  AppUpdaterUSB
    b       AppUpdaterUSB
    else
    b       Updater
    endif

;******************************************************************************
;
; The NMI handler.
; NMI异常服务例程,处理主振荡器失败
;
;******************************************************************************
    if      :def:_ENABLE_MOSCFAIL_HANDLER
NmiSR_In_SRAM
    ;
    ; Restore the stack frame.
    ;
    mov     lr, r12
    stm     sp, {r4-r11}

    ;
    ; Save the link register.
    ;
    mov     r9, lr

    ;
    ; Call the user-supplied low level hardware initialization function
    ; if provided.
    ;
    if      :def:_BL_HW_INIT_FN_HOOK
    bl      _BL_HW_INIT_FN_HOOK
    endif

    ;
    ; See if an update should be performed.
    ;
    bl      CheckForceUpdate
    cbz     r0, EnterApplication

        ;
        ; Clear the MOSCFAIL bit in RESC.
        ;
        movw    r0, #(SYSCTL_RESC & 0xffff)
        movt    r0, #(SYSCTL_RESC >> 16)
        ldr     r1, [r0]
        bic     r1, r1, #SYSCTL_RESC_MOSCFAIL
        str     r1, [r0]

        ;
        ; Fix up the PC on the stack so that the boot pin check is bypassed
        ; (since it has already been performed).
        ;
        ldr     r0, =EnterBootLoader
        bic     r0, #0x00000001
        str     r0, [sp, #0x18]
        
        ;
        ; Return from the NMI handler.  This will then start execution of the
        ; boot loader.
        ;
        bx      r9

    ;
    ; Restore the link register.
    ;
EnterApplication
    mov     lr, r9

    ;
    ; Copy the application's vector table to the target address if necessary.
    ; Note that incorrect boot loader configuration could cause this to
    ; corrupt the code!  Setting VTABLE_START_ADDRESS to 0x20000000 (the start
    ; of SRAM) is safe since this will use the same memory that the boot loader
    ; already uses for its vector table.  Great care will have to be taken if
    ; other addresses are to be used.
    ;
    if (_APP_START_ADDRESS != _VTABLE_START_ADDRESS)
    movw    r0, #(_VTABLE_START_ADDRESS & 0xffff)
    if (_VTABLE_START_ADDRESS > 0xffff)
    movt    r0, #(_VTABLE_START_ADDRESS >> 16)
    endif
    movw    r1, #(_APP_START_ADDRESS & 0xffff)
    if (_APP_START_ADDRESS > 0xffff)
    movt    r1, #(_APP_START_ADDRESS >> 16)
    endif

    ;
    ; Calculate the end address of the vector table assuming that it has the
    ; maximum possible number of vectors.  We don't know how many the app has
    ; populated so this is the safest approach though it may copy some non
    ; vector data if the app table is smaller than the maximum.
    ;
    movw    r2, #(70 * 4)
    adds    r2, r2, r0
VectorCopyLoop2
        ldr     r3, [r1], #4
        str     r3, [r0], #4
        cmp     r0, r2
        blt     VectorCopyLoop2
    endif

    ;
    ; Set the application's vector table start address.  Typically this is the
    ; application start address but in some cases an application may relocate
    ; this so we can't assume that these two addresses are equal.
    ;
    movw    r0, #(_VTABLE_START_ADDRESS & 0xffff)
    if (_VTABLE_START_ADDRESS > 0xffff)
    movt    r0, #(_VTABLE_START_ADDRESS >> 16)
    endif
    movw    r1, #(NVIC_VTABLE & 0xffff)
    movt    r1, #(NVIC_VTABLE >> 16)
    str     r0, [r1]

    ;
    ; Remove the NMI stack frame from the boot loader's stack.
    ;
    ldmia   sp, {r4-r11}

    ;
    ; Get the application's stack pointer.
    ;
    if (_APP_START_ADDRESS != _VTABLE_START_ADDRESS)
    movw    r0, #(_APP_START_ADDRESS & 0xffff)
    if (_APP_START_ADDRESS > 0xffff)
    movt    r0, #(_APP_START_ADDRESS >> 16)
    endif
    endif
    ldr     sp, [r0, #0x00]

    ;
    ; Fix up the NMI stack frame's return address to be the reset handler of
    ; the application.
    ;
    ldr     r10, [r0, #0x04]
    bic     r10, #0x00000001

    ;
    ; Store the NMI stack frame onto the application's stack.
    ;
    stmdb   sp!, {r4-r11}

    ;
    ; Branch to the application's NMI handler.
    ;
    ldr     r0, [r0, #0x08]
    bx      r0
    endif

;******************************************************************************
;
; The default interrupt handler.
;
;******************************************************************************
IntDefaultHandler
    ;
    ; Loop forever since there is nothing that we can do about an unexpected
    ; interrupt.
    ;
    b       .

;******************************************************************************
;
; Provides a small delay.  The loop below takes 3 cycles/loop.
; 提供一个小的延时函数. 循环一次需要3个时钟周期.
;
;******************************************************************************
    export  Delay
Delay
    subs    r0, #1
    bne     Delay
    bx      lr

;******************************************************************************
;
; This is the end of the file.
;
;******************************************************************************
    align   4
    end

1. 汇编文件正文的第一句

      include bl_config.inc

包含bl_config.inc,这个文件是什么,从哪里来,有什么作用?再看bootloader工程Options---User---Run User Programs Before Build/Rebuild内的用户命令(见图2-2)又是什么?


图2-2

         所有的一切,要从keil MDK的汇编器说起,在启动代码中要用到配置文件bl_config.h中定义的一些配置选项,但因为MDK汇编器不能通过C预处理器运行汇编代码,所以bl_config.h中的相关内容需要 转化为汇编格式并包含到MDK的启动代码中。这需要手动运行C预编译器进行格式转化。图2-2中红色部分圈出的内容正是为了完成这个转换。在点击Build/Rebuild编译按钮之后,会先运行图2-2指定的命令,再进行编译。先来分析一下这条命令:

                                armcc --device DLM -o bl_config.inc -E bl_config.c

          这条命令的作用是将bl_config.c(包含bl_config.h文件)进行而且仅进行预编译处理,并生成bl_config.inc文件。

          armcc是Keil MDK提供的C编译工具,语法为:

                                 armcc [Options]  file1  file2  ...  file n

           介绍一下这里用到的Options选项:

                                   --device<dev>:设置目标的设备类型,DLM为Luminary的设备标识。

                                   -I<directory>   :目录列表

                                   -E                      :仅执行预处理

                                   -o<file>            :指定输出文件的名字

2. 看一下目标板上电后启动代码的运行流程

          上电后程序先到Flash地址0x00处装载堆栈地址,这跟以前接触过的处理器不同,以前0x00处都是放置的复位处理代码,但Cortex M3内核却不是,0x00处是放置的堆栈地址,而不是跳转指令。

           堆栈设置完成后,跳转到Reset处理程序处,调用处理器初始化函数ProcessorInit,该函数将bootloader从Flash拷贝到SRAM,将.bss区用零填充并将向量表重映射到SRAM开始处。

           之后跳转到Reset_Handler_In_SRAM函数,在该函数中,如果用户提供了底层硬件初始化函数(在bl_config.h中使能),则调用这个函数。然后调用CheckForceUpdate函数,检查是否有升级请求。如果没有升级请求,跳转到CallApplication函数,在该函数中,将向量表重映射到应用程序开始处(这里为地址0x1000),装载用户程序堆栈地址,跳转到用户程序的Reset服务函数。

           如果调用CheckForceUpdate函数检测到有升级请求,则配置以太网,跳转到升级程序UpdateBOOTP处执行。

3. 如何在用户程序中调用升级程序

          用户程序存在于Flash地址0x1000处,bootloader存放于Flash地址0x00处,并且用户程序在执行的时候已经将向量表重映射到了Flash地址0x1000处了,那么应用程序是如何调用位于bootloader中的升级程序呢?

        再看bootloader启动代码的中断向量表,在Flash地址的0x2C中存放的是CPU SVC异常服务跳转地址:

                    dcd     UpdateHandler                   ; Offset 2C: SVCall handler

        而bootloader正是用这个异常来处理升级请求的。那么,应用程序只要执行该地址处的跳转指令,就能进行一次程序升级,在应用程序中的swupdate.c中,使用了如下C代码来执行位于Flash地址0x2C内的跳转程序:

                    (*((void (*)(void))(*(unsigned long *)0x2c)))();  

         对C语言还没有入门的同学可能会比较的头痛,这像谜一样的语句是如何执行位于bootloader的SVC异常服务例程呢?还是分解一下吧:

                      (*(unsigned long *)0x2c):将0x2C强制转化为unsigned long类型指针,并指向该地址所在的数据;

                      void (*)(void)                      :函数指针,指针名为空,该函数参数为空,返回值为空

                     (void (*)(void))(*(unsigned long *)0x2c):将Flash地址0x2C中的内容强制转化为函数指针,该函数参数为空,返回值为空

                     (*((void (*)(void))(*(unsigned long *)0x2c)))();:调用函数,即开始从启动代码中的UpdateHandler标号处开始执行。











  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值