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標號處開始執行。
轉載:http://blog.csdn.net/zhzht19861011/article/details/6675170