sunnyqd 发表于 2014-8-25 16:07:59

【网友经验分享】飞思卡尔Cortex M0+系列 Keil MDK启动文件详解

本帖最后由 FSL_TICS_ZJJ 于 2014-9-11 14:56 编辑

入手飞思卡尔,从启动文件学起吧,这是查阅官方帮助文档整理的版本,是已知目前最详细的启动文件介绍,结合了MAP文件和SCT文件
如有错误还请指正{:tongue:}
在其他平台,启动文件功能基本一致,这里是我整理的MDK下的版本,修正了网上其它版本的一些错误,描述更详细
启动文件
;/*****************************************************************************
; * @file:    startup_MKL02Z4.s
; * @purpose: CMSIS Cortex-M0plus Core Device Startup File for the
; *         MKL02Z4
; * @version: 1.0
; * @date:    2012-10-4
; *
; * Copyright: 1997 - 2013 Freescale Semiconductor, Inc. All Rights Reserved.
;*
; *------- <<< Use Configuration Wizard in Context Menu >>> ------------------
; *
; *****************************************************************************/


; <h> Stack Configuration
;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Stack_Size      EQU   0x00000100
                ;EQU定义符号指示Stack大小
   
                AREA    STACK, NOINIT, READWRITE, ALIGN=3   
                ;AREA 通知汇编器汇编一段新的代码或数据节(section)
                ;STACK 为AREA所定义的新的节的节名称(sectionname)
                ;NOINIT 指示这段数据节不初始化或初始化为0,以SPACE等指示的空间初始化为0,但不能初始化为其它
                ;       备注:即这段不能指定初始化值,如果不加NOINIT的话,MAP文件SPACE的数据类型为Data,为RW Data
                ;             如果加NOINIT的话,MAP文件SPACE的数据类型为Zero,为ZI Data
                ;READWRITE 指示该节类型为RW
                ;ALIGN=3 指示该节对齐到8字节boundary,即为双字地址
                ;      备注:在MAP文件中,可以发现是通过添加PAD类型的数据进行节的对齐
                ;            PAD取何值可以指定
                                                            

Stack_Mem       SPACE   Stack_Size
                ;Stack_Mem 为一个标签(label),标签值为在当前节中的地址
                ;SPACE 指示(directive)一块内存(memory),这块内存所在节的特性由上面的AREA指定
__initial_sp
                ;__initial_sp 为上面SPACE块的顶端地址+1,即说明堆栈的初始值为顶端地址,堆栈是向下生长的满栈
                ;             备注:因为CM的入栈操作是,*(--R13)=R0类似的一种方式,R13指向最后的数据


; <h> Heap Configuration
;   <o>Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU   0x00000100
                ;定义符号指示Heap大小

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
                ;定义一个新的节,节名称为HEAP,其它与STACK相同
                ;EXPORT __heap_base
__heap_base
                ;定义一个标签,指示HEAP的基地址,这个标签只用在使用MICROLIB时被EXPORT,即只有这时有效
                ;使用MICROLIB时,这个标签被EXPORT,类似于C语言中的extern,但只有当HEAP被使用的时候
                ;                才出现在MAP文件中,如在main()中可以这样来访问__heap_base
                ;                void main(void)
                ;                {
                ;                   extern int __heap_base;
                ;                   static int HEAPBASE;    //定义静态变量,在内存中有固定的存储空间
                ;                   ......
                ;                   HEAPBASE = __heap_base; //读取HEAP的即地址,只在MICROLIB时有效
                ;                   ......
                ;               }
                ;如果要在不使用MICROLIB时,读取__heap_base, 那只需要在__heap_base前添加EXPORT __heap_base                  
Heap_Mem      SPACE   Heap_Size
                ;EXPORT Heap_Mem
                ;定义HEAP的内存段,标签为Heap_Mem,将Heap_Mem EXPORT之后,可以这样访问:
                ;       extern int Heap_Mem;
                ;       static int* pHEAPMEM;
                ;       pHEAPMEM = (int*)Heap_Mem;
__heap_limit
                ;定义一个标签,HEAP的地址范围为__heap_base ~ (__heap_limit - 1)
               
;这里结合MAP文件外插上几句
;1. 当:STACK 所在节属性为 AREA    STACK, NOINIT, READWRITE, ALIGN=3
;       HEAP所在节属性为 AREA    HEAP,NOINIT, READWRITE, ALIGN=3
;   时,MAP文件的执行时期RAM镜像为
;   Execution Region RW_IRAM2 (Base: 0x20000000, Size: 0x00000268, Max: 0x00000c00, ABSOLUTE)
;
;   Base Addr    Size         Type   Attr      Idx    E Section Name      Object
;
;   0x20000000   0x00000004   Data   RW            4    .data               system_mkl02z4.o
;   0x20000004   0x00000004   Data   RW         61    .data               blinky.o
;   0x20000008   0x00000060   Zero   RW          139    .bss                c_p.l(libspace.o)
;   0x20000068   0x00000100   Zero   RW         48    HEAP                startup_mkl02z4.o
;   0x20000168   0x00000100   Zero   RW         47    STACK               startup_mkl02z4.o
;
;   可见,在程序运行时,数据在内存中存放的顺序为有初始化的Data类型,和0初始化的Zero类型
;   此时,HEAP向上增长,STACK向下生长,二者的方向是相向的
;   全局变量,如果没有指定初始值,如static int gTestData; 是存放在.dada节,而不是.bss
;
;2. 当:STACK 所在节属性为 AREA    STACK,         READWRITE, ALIGN=3
;       HEAP所在节属性为 AREA    HEAP,NOINIT, READWRITE, ALIGN=3
;   时,MAP文件的执行时期RAM镜像为
;   Execution Region RW_IRAM2 (Base: 0x20000000, Size: 0x00000268, Max: 0x00000c00, ABSOLUTE)
;
;   Base Addr    Size         Type   Attr      Idx    E Section Name      Object
;
;   0x20000000   0x00000004   Data   RW            4    .data               system_mkl02z4.o
;   0x20000004   0x00000004   Data   RW         61    .data               blinky.o
;   0x20000008   0x00000100   Data   RW         47    STACK               startup_mkl02z4.o
;   0x20000108   0x00000060   Zero   RW          139    .bss                c_p.l(libspace.o)
;   0x20000168   0x00000100   Zero   RW         48    HEAP                startup_mkl02z4.o
;
;   此时HEAP向上增长,STACK向下生长,二者的方向是相背的
;   同时可以看出,不用NOINIT修饰时,STACK的数据类型为Data,是一种RW Type,否则是一种ZI Type
;
;部分细节看参见Linker User Guide: Type 2 image, one load region and non-contiguous execution regions
   
                PRESERVE8
                ;通知链接器(linker)当前文件是以堆栈8字节对齐,使用PRESERVE8的话,说明我们现在的文件堆栈是以8字节对齐的
                ;这样的话,这里的函数可以调用由REQUIRE8指示的文件的函数,而不引起错误,如LDRD and STRD指令,因为
                ;LDRD and STRD instructions (double-word transfers) only work correctly if the address they access
                ;is 8-byte aligned.
                THUMB
                ;通知编译器采用THUMB指令集


; Vector Table Mapped to Address 0 at Reset

                AREA    RESET, DATA, READONLY
                ;指示当前节为RESET,节的属性为DATA(Contains data, not instructions),RO
                EXPORT__Vectors
                EXPORT__Vectors_End
                EXPORT__Vectors_Size
                ;全局变量__Vectors,__Vectors_End,和__Vectors_Size
;分散加载文件SCT为
;   LR_IROM1 0x00000000 0x00008000{       ; load region size_region
;       ER_IROM1 0x00000000 0x00008000{   ; load address = execution address
;         *.o (RESET, +First)             ; +First指示执行时RESET节置于最开始
;         *(InRoot$Sections)             ; InRoot$Sections指代所有必须放在根区域的节
;                                           ; 如__main.o, __scatter*.o,
;                                           ;__dc*.o, and * Region$Table
;         .ANY (+RO)                      ; Read only的数据执行时在这里
;       }
;       RW_IRAM1 0x1FFFFC00 0x00000400{   ; RW data
;         .ANY (+RW +ZI)
;       }
;       RW_IRAM2 0x20000000 0x00000C00{
;         .ANY (+RW +ZI)
;       }
;   }
;我们可以这样更改分散加载文件为
;   LR_IROM1 0x00000000 0x00008000{    ; load region size_region
;       ER_IROM1 0x00000000 0x00008000{; load address = execution address
;         *.o (RESET, +First)
;         *(InRoot$Sections)
;         .ANY (+RO)
;       }
;       RW_IRAM1 0x1FFFFC00 0x00000400{; RW data
;         .ANY (+ZI)
;       }
;       RW_IRAM2 0x20000000 0x00000C00{
;         .ANY (+RW)
;         *.o (.bss)
;       }
;   }
;这样,执行时候的内存镜像变成这样子了,我们利用起来了两块RAM区的特点
;    Execution Region RW_IRAM1 (Base: 0x1ffffc00, Size: 0x00000200, Max: 0x00000400, ABSOLUTE)
;
;    Base Addr    Size         Type   Attr      Idx    E Section Name      Object
;
;    0x1ffffc00   0x00000100   Zero   RW         48    HEAP                startup_mkl02z4.o
;    0x1ffffd00   0x00000100   Zero   RW         47    STACK               startup_mkl02z4.o
;
;
;    Execution Region RW_IRAM2 (Base: 0x20000000, Size: 0x00000068, Max: 0x00000c00, ABSOLUTE)
;
;    Base Addr    Size         Type   Attr      Idx    E Section Name      Object
;
;    0x20000000   0x00000004   Data   RW            4    .data               system_mkl02z4.o
;    0x20000004   0x00000004   Data   RW         61    .data               blinky.o
;    0x20000008   0x00000060   Zero   RW          139    .bss                c_p.l(libspace.o)

__Vectors       DCD   __initial_sp      ; Top of Stack
                                          ; DCD 申请4字节的空间,存放数据
                                          ; 由于分散加载文件SCT中的*.o (RESET, +First)属性
                                          ; RESET节被放在了LR_IROM1的开始位置,即__initial_sp存放在0地址
                DCD   Reset_Handler       ; Reset Handler
                DCD   NMI_Handler         ; NMI Handler
                DCD   HardFault_Handler   ; Hard Fault Handler
                DCD   0; Reserved
                DCD   0; Reserved
                DCD   0; Reserved
                DCD   0; Reserved
                DCD   0; Reserved
                DCD   0; Reserved
                DCD   0; Reserved
                DCD   SVC_Handler         ; SVCall Handler
                DCD   0; Reserved
                DCD   0; Reserved
                DCD   PendSV_Handler      ; PendSV Handler
                DCD   SysTick_Handler   ; SysTick Handler

                ; External Interrupts
                DCD   Reserved16_IRQHandler; Reserved interrupt 16
                DCD   Reserved17_IRQHandler; Reserved interrupt 17
                DCD   Reserved18_IRQHandler; Reserved interrupt 18
                DCD   Reserved19_IRQHandler; Reserved interrupt 19
                DCD   Reserved20_IRQHandler; Reserved interrupt 20
                DCD   FTFA_IRQHandler   ; FTFA command complete/read collision interrupt
                DCD   LVD_LVW_IRQHandler; Low Voltage Detect, Low Voltage Warning
                DCD   Reserved23_IRQHandler; Reserved interrupt 23
                DCD   I2C0_IRQHandler; I2C0 interrupt
                DCD   I2C1_IRQHandler; I2C1 interrupt
                DCD   SPI0_IRQHandler; SPI0 interrupt
                DCD   Reserved27_IRQHandler; Reserved interrupt 27
                DCD   UART0_IRQHandler; UART0 status/error interrupt
                DCD   Reserved29_IRQHandler; Reserved interrupt 29
                DCD   Reserved30_IRQHandler; Reserved interrupt 30
                DCD   ADC0_IRQHandler; ADC0 interrupt
                DCD   CMP0_IRQHandler; CMP0 interrupt
                DCD   TPM0_IRQHandler; TPM0 fault, overflow and channels interrupt
                DCD   TPM1_IRQHandler; TPM1 fault, overflow and channels interrupt
                DCD   Reserved35_IRQHandler; Reserved interrupt 35
                DCD   Reserved36_IRQHandler; Reserved interrupt 36
                DCD   Reserved37_IRQHandler; Reserved interrupt 37
                DCD   Reserved38_IRQHandler; Reserved interrupt 38
                DCD   Reserved39_IRQHandler; Reserved interrupt 39
                DCD   Reserved40_IRQHandler; Reserved interrupt 40
                DCD   Reserved41_IRQHandler; Reserved interrupt 41
                DCD   Reserved42_IRQHandler; Reserved interrupt 42
                DCD   MCG_IRQHandler; MCG interrupt
                DCD   LPTimer_IRQHandler; LPTimer interrupt
                DCD   Reserved45_IRQHandler; Reserved interrupt 45
                DCD   PORTA_IRQHandler; Port A interrupt
                DCD   PORTB_IRQHandler; Port B interrupt
__Vectors_End
                ;这里__Vectors_End的值为0x000000c0 (在MAP文件里查看)

__Vectors_Size         EQU   __Vectors_End - __Vectors   ;计算中断向量表的大小

; 以下为Freescale特有的配置方式(我的理解,在ST中不是采用的这种配置方法)
; Flash配置区域在Flash的0x0000_0400开始的空间,一共16字节
; 关于Flash配置区域在帖子 http://www.amobbs.com/thread-5588189-1-1.html 中有描述
; 以下定义的变量值,在下面有采用,这边略过Flash配置字的含义,详见Reference Mannual
;
;
; <h> Flash Configuration
;   <i> 16-byte flash configuration field that stores default protection settings (loaded on reset)
;   <i> and security information that allows the MCU to restrict acces to the FTFL module.
;   <h> Backdoor Comparison Key
;   <o0>Backdoor Key 0<0x0-0xFF:2>
;   <o1>Backdoor Key 1<0x0-0xFF:2>
;   <o2>Backdoor Key 2<0x0-0xFF:2>
;   <o3>Backdoor Key 3<0x0-0xFF:2>
;   <o4>Backdoor Key 4<0x0-0xFF:2>
;   <o5>Backdoor Key 5<0x0-0xFF:2>
;   <o6>Backdoor Key 6<0x0-0xFF:2>
;   <o7>Backdoor Key 7<0x0-0xFF:2>
BackDoorK0      EQU   0xFF
BackDoorK1      EQU   0xFF
BackDoorK2      EQU   0xFF
BackDoorK3      EQU   0xFF
BackDoorK4      EQU   0xFF
BackDoorK5      EQU   0xFF
BackDoorK6      EQU   0xFF
BackDoorK7      EQU   0xFF
;   </h>
;   <h> Program flash protection bytes (FPROT)
;   <i> Each program flash region can be protected from program and erase operation by setting the associated PROT bit.
;   <i> Each bit protects a 1/32 region of the program flash memory.
;   <h> FPROT0
;       <i> Program flash protection bytes
;       <i> 1/32 - 8/32 region
;       <o.0>   FPROT0.0
;       <o.1>   FPROT0.1
;       <o.2>   FPROT0.2
;       <o.3>   FPROT0.3
;       <o.4>   FPROT0.4
;       <o.5>   FPROT0.5
;       <o.6>   FPROT0.6
;       <o.7>   FPROT0.7
nFPROT0         EQU   0x00
FPROT0          EQU   nFPROT0:EOR:0xFF
;   </h>
;   <h> FPROT1
;       <i> Program Flash Region Protect Register 1
;       <i> 9/32 - 16/32 region
;       <o.0>   FPROT1.0
;       <o.1>   FPROT1.1
;       <o.2>   FPROT1.2
;       <o.3>   FPROT1.3
;       <o.4>   FPROT1.4
;       <o.5>   FPROT1.5
;       <o.6>   FPROT1.6
;       <o.7>   FPROT1.7
nFPROT1         EQU   0x00
FPROT1          EQU   nFPROT1:EOR:0xFF
;   </h>
;   <h> FPROT2
;       <i> Program Flash Region Protect Register 2
;       <i> 17/32 - 24/32 region
;       <o.0>   FPROT2.0
;       <o.1>   FPROT2.1
;       <o.2>   FPROT2.2
;       <o.3>   FPROT2.3
;       <o.4>   FPROT2.4
;       <o.5>   FPROT2.5
;       <o.6>   FPROT2.6
;       <o.7>   FPROT2.7
nFPROT2         EQU   0x00
FPROT2          EQU   nFPROT2:EOR:0xFF
;   </h>
;   <h> FPROT3
;       <i> Program Flash Region Protect Register 3
;       <i> 25/32 - 32/32 region
;       <o.0>   FPROT3.0
;       <o.1>   FPROT3.1
;       <o.2>   FPROT3.2
;       <o.3>   FPROT3.3
;       <o.4>   FPROT3.4
;       <o.5>   FPROT3.5
;       <o.6>   FPROT3.6
;       <o.7>   FPROT3.7
nFPROT3         EQU   0x00
FPROT3          EQU   nFPROT3:EOR:0xFF
;   </h>
;   </h>
;   </h>
;   <h> Flash nonvolatile option byte (FOPT)
;   <i> Allows the user to customize the operation of the MCU at boot time.
;   <o.0>LPBOOT0
;       <0=> Core and system clock divider (OUTDIV1) is 0x7 (divide by 8) or 0x3 (divide by 4)
;       <1=> Core and system clock divider (OUTDIV1) is 0x1 (divide by 2) or 0x0 (divide by 1)
;   <o.4>LPBOOT1
;       <0=> Core and system clock divider (OUTDIV1) is 0x7 (divide by 8) or 0x1 (divide by 2)
;       <1=> Core and system clock divider (OUTDIV1) is 0x3 (divide by 4) or 0x0 (divide by 1)
;   <o.2>NMI_DIS
;       <0=> NMI interrupts are always blocked
;       <1=> NMI pin/interrupts reset default to enabled
;   <o.3>RESET_PIN_CFG
;       <0=> RESET pin is disabled following a POR and cannot be enabled as RESET function
;       <1=> RESET pin is dedicated
;   <o.3>FAST_INIT
;       <0=> Slower initialization
;       <1=> Fast Initialization
FOPT            EQU   0xFF
;   </h>
;   <h> Flash security byte (FSEC)
;   <i> WARNING: If SEC field is configured as "MCU security status is secure" and MEEN field is configured as "Mass erase is disabled",
;   <i> MCU's security status cannot be set back to unsecure state since Mass erase via the debugger is blocked !!!
;   <o.0..1> SEC
;       <2=> MCU security status is unsecure
;       <3=> MCU security status is secure
;         <i> Flash Security
;         <i> This bits define the security state of the MCU.
;   <o.2..3> FSLACC
;       <2=> Freescale factory access denied
;       <3=> Freescale factory access granted
;         <i> Freescale Failure Analysis Access Code
;         <i> This bits define the security state of the MCU.
;   <o.4..5> MEEN
;       <2=> Mass erase is disabled
;       <3=> Mass erase is enabled
;         <i> Mass Erase Enable Bits
;         <i> Enables and disables mass erase capability of the FTFL module
;   <o.6..7> KEYEN
;       <2=> Backdoor key access enabled
;       <3=> Backdoor key access disabled
;         <i> Backdoor key Security Enable
;         <i> These bits enable and disable backdoor key access to the FTFL module.
FSEC            EQU   0xFE
;   </h>
;
; 在这里,定义了一个新的节,名称是.ARM,位于地址0x400,在MAP文件里是这样的
;   ......
;   0x000003cc   0x00000030   Data   RO          263    Region$Table       anon$obj.o
;   0x000003fc   0x00000004   PAD
;   0x00000400   0x00000010   Code   RO         50    .ARM.__at_0x400   startup_mkl02z4.o
;   0x00000410   0x00000174   Code   RO         59    .text               blinky.o
;   0x00000584   0x0000015a   Code   RO         93    .text               c_p.l(aeabi_sdiv.o)
;   ......
; 可见PAD补充字节空间,.ARM.__at_0x400放在了0x0400地址,而这些地址存放的数据,是CODE,RO类型
; 下面的DCB填充了这16个字节
                IF      :LNOT::DEF:RAM_TARGET
                  ; :DEF:A 语句,如果A定义,返回真,否则返回假
                  ; :LNOT:A 语句,是对A的逻辑取反
                  ; 结合上面的 IF,所以以下代码尽在RAM_TARGET没定义的时候会产生
                  ; 如果在RAM中调试的话,应该定义RAM_TARGET,那样的话也就不需要FLASH配置字了
                  ;
                AREA    |.ARM.__at_0x400|, CODE, READONLY
                  ; 定义一个节,存放配置数据,单片机复位后,FLASH控制器加载这些配置
                  ;
                DCB   BackDoorK0, BackDoorK1, BackDoorK2, BackDoorK3
                DCB   BackDoorK4, BackDoorK5, BackDoorK6, BackDoorK7
                DCB   FPROT0,   FPROT1,   FPROT2,   FPROT3
                DCB   FSEC,       FOPT,       0xFF,   0xFF
                ENDIF

; 下面是程序端的开始
                AREA    |.text|, CODE, READONLY
                  ; AREA定义.text节存放代码,因为.text不是有效的变量名,所以加||限定


; Reset Handler

Reset_Handler   PROC
                  ; 同FUNCTION,标记一个函数的开始
                EXPORTReset_Handler            
                  ; 弱引用,通知链接器,其它文件中的同名函数的优先级高于这个函数,即只有当其它文件中无
                  ; Reset_Handler函数时,Reset_Handler()才指向这里
                IMPORTSystemInit
                  ; 告诉链接器到其它文件中查找SystemInit函数
                IMPORT__main
                  ; 告诉链接器到其它文件中查找__main函数
                LDR   R0, =SystemInit
                  ; 执行SystemInit();
                BLX   R0
                LDR   R0, =__main
                  ; 执行__main(); __main()调用main(),因而正常情况下不返回
                BX      R0
                ENDP
                  ; 标记函数的结束


; Dummy Exception Handlers (infinite loops which can be modified)
; 异常函数,弱引用,需要在其它文件中自定义相同名称的函数,以进行异常的处理
;
NMI_Handler   PROC
                EXPORTNMI_Handler               
                B       .   ; B . 表示跳转到当前地址
                ENDP
HardFault_Handler\
                PROC
                EXPORTHardFault_Handler         
                B       .
                ENDP
SVC_Handler   PROC
                EXPORTSVC_Handler               
                B       .
                ENDP
PendSV_HandlerPROC
                EXPORTPendSV_Handler            
                B       .
                ENDP
SysTick_Handler PROC
                EXPORTSysTick_Handler         
                B       .
                ENDP

Default_Handler PROC
                EXPORTReserved16_IRQHandler   
                EXPORTReserved17_IRQHandler   
                EXPORTReserved18_IRQHandler   
                EXPORTReserved19_IRQHandler   
                EXPORTReserved20_IRQHandler   
                EXPORTFTFA_IRQHandler   
                EXPORTLVD_LVW_IRQHandler   
                EXPORTReserved23_IRQHandler   
                EXPORTI2C0_IRQHandler   
                EXPORTI2C1_IRQHandler   
                EXPORTSPI0_IRQHandler   
                EXPORTReserved27_IRQHandler   
                EXPORTUART0_IRQHandler   
                EXPORTReserved29_IRQHandler   
                EXPORTReserved30_IRQHandler   
                EXPORTADC0_IRQHandler   
                EXPORTCMP0_IRQHandler   
                EXPORTTPM0_IRQHandler   
                EXPORTTPM1_IRQHandler   
                EXPORTReserved35_IRQHandler   
                EXPORTReserved36_IRQHandler   
                EXPORTReserved37_IRQHandler   
                EXPORTReserved38_IRQHandler   
                EXPORTReserved39_IRQHandler   
                EXPORTReserved40_IRQHandler   
                EXPORTReserved41_IRQHandler   
                EXPORTReserved42_IRQHandler   
                EXPORTMCG_IRQHandler   
                EXPORTLPTimer_IRQHandler   
                EXPORTReserved45_IRQHandler   
                EXPORTPORTA_IRQHandler   
                EXPORTPORTB_IRQHandler   
                EXPORTDefaultISR                     

Reserved16_IRQHandler
Reserved17_IRQHandler
Reserved18_IRQHandler
Reserved19_IRQHandler
Reserved20_IRQHandler
FTFA_IRQHandler
LVD_LVW_IRQHandler
Reserved23_IRQHandler
I2C0_IRQHandler
I2C1_IRQHandler
SPI0_IRQHandler
Reserved27_IRQHandler
UART0_IRQHandler
Reserved29_IRQHandler
Reserved30_IRQHandler
ADC0_IRQHandler
CMP0_IRQHandler
TPM0_IRQHandler
TPM1_IRQHandler
Reserved35_IRQHandler
Reserved36_IRQHandler
Reserved37_IRQHandler
Reserved38_IRQHandler
Reserved39_IRQHandler
Reserved40_IRQHandler
Reserved41_IRQHandler
Reserved42_IRQHandler
MCG_IRQHandler
LPTimer_IRQHandler
Reserved45_IRQHandler
PORTA_IRQHandler
PORTB_IRQHandler
DefaultISR
    ;上面这些函数的地址都是一样的,即标号的值都相同,但都是弱引用
                B       .

                ENDP


                ALIGN
                  ; ALIGN不指定参数时,将当前地址对起到4字节,填0或NOP,或其它给定值


; User Initial Stack & Heap

                IF      :DEF:__MICROLIB
                  ; 如果定义使用MICROLIB,则将下面的标号声明为全局变量,供MICROLIB使用
                  ;
                EXPORT__initial_sp
                EXPORT__heap_base
                EXPORT__heap_limit

                ELSE

                IMPORT__use_two_region_memory
                  ; 指定存储器模式为双区模式
                  ; 单区模型:(r0, r1)是单个堆栈和堆区,r1大于r0,忽略r2和r3
                  ;         堆和栈共用一块内存区域
                  ; 双区模型:(r0, r2)是初始堆,(r3, r1)是初始栈
                  ;         堆和栈分别指定了单独的内存区域
                  ;
                EXPORT__user_initial_stackheap
                  ; 声明一个全局标号 __user_initial_stackheap作为函数使用
__user_initial_stackheap
                  ; C标准库调用__user_initial_stackheap()函数,获得堆栈信息
                  ; 这里没有使用PROC标记
                  ;
                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

里面我有一个没搞懂的地方,就是最后的__user_initial_stackheap为什么不加PROC标记
KL02官方Keil MDK例程文件

附图1:堆栈分配


附图2:加载镜像和执行镜像

abszy 发表于 2014-8-25 16:09:21

启动文件很长啊需要仔细看下
感谢

浪里白条 发表于 2014-8-25 16:11:18

厉害,这个我还没仔细看过呢,有空了跟着楼主学习下

浪里白条 发表于 2014-8-25 16:15:13

楼楼能否将指令集也传上来下呢

cn_x 发表于 2014-8-25 16:17:05

这个···强烈支持一下 ,慢慢消化

laotui 发表于 2014-8-25 16:19:25

楼主的资料太好了,正是我需要的。

sdlibin007 发表于 2014-8-25 16:19:30

柯南兄的资料室一如既往的犀利啊!!

sunnyqd 发表于 2014-8-25 16:22:32

abszy 发表于 2014-8-25 16:09
启动文件很长啊需要仔细看下
感谢

其实不是很长,因为我加了好多注释

sunnyqd 发表于 2014-8-25 16:23:08

浪里白条 发表于 2014-8-25 16:11
厉害,这个我还没仔细看过呢,有空了跟着楼主学习下

我也是边查资料边整理的,整理了一个下午,嘿嘿

浪里白条 发表于 2014-8-25 16:24:25

sunnyqd 发表于 2014-8-25 16:23
我也是边查资料边整理的,整理了一个下午,嘿嘿

佩服,要弄懂这些语句,必须要有一定的功底才行的。

xlxbangel 发表于 2014-8-25 16:25:22

一直没搞懂启动文件是怎么工作的

xlxbangel 发表于 2014-8-25 16:25:52

好像全部是汇编

sunnyqd 发表于 2014-8-25 16:26:26

浪里白条 发表于 2014-8-25 16:15
楼楼能否将指令集也传上来下呢

我找找我电脑上有没有

tim 发表于 2014-8-25 16:26:38

不错,收藏,鼎鼎!

sunnyqd 发表于 2014-8-25 16:55:44

浪里白条 发表于 2014-8-25 16:15
楼楼能否将指令集也传上来下呢

指令集从ARM官网上下载下来了

sunnyqd 发表于 2014-8-25 16:56:06

cn_x 发表于 2014-8-25 16:17
这个···强烈支持一下 ,慢慢消化

多谢支持

浪里白条 发表于 2014-8-25 16:56:27

sunnyqd 发表于 2014-8-25 16:55
指令集从ARM官网上下载下来了

看到了的,给你顶起了,多谢楼楼了。

sunnyqd 发表于 2014-8-25 16:56:40

laotui 发表于 2014-8-25 16:19
楼主的资料太好了,正是我需要的。

我也是刚好想看,以前这方面都不懂,整理了一遍就都懂啦

sunnyqd 发表于 2014-8-25 16:57:11

sdlibin007 发表于 2014-8-25 16:19
柯南兄的资料室一如既往的犀利啊!!

还第一次被叫柯南兄。。

sunnyqd 发表于 2014-8-25 16:57:53

xlxbangel 发表于 2014-8-25 16:25
一直没搞懂启动文件是怎么工作的

详细的看上一遍基本都懂了

sdlibin007 发表于 2014-8-25 16:59:07

sunnyqd 发表于 2014-8-25 16:57
还第一次被叫柯南兄。。

嗯??难道改叫柯南姐??O(∩_∩)O~

laotui 发表于 2014-8-25 16:59:17

sunnyqd 发表于 2014-8-25 16:56
我也是刚好想看,以前这方面都不懂,整理了一遍就都懂啦

不知道KEIL哪个版本能支持到KL、KE系列?我的芯片列表里没有这两种。

sunnyqd 发表于 2014-8-25 17:01:18

laotui 发表于 2014-8-25 16:59
不知道KEIL哪个版本能支持到KL、KE系列?我的芯片列表里没有这两种。

4.72我的是

sunnyqd 发表于 2014-8-25 17:01:56

sdlibin007 发表于 2014-8-25 16:59
嗯??难道改叫柯南姐??O(∩_∩)O~

嗯,南哥挺好的{:shy:}

laotui 发表于 2014-8-25 17:02:04

sunnyqd 发表于 2014-8-25 17:01
4.72我的是

谢谢,我用的4.7。

sdlibin007 发表于 2014-8-25 17:11:54

sunnyqd 发表于 2014-8-25 17:01
嗯,南哥挺好的

你真是女生啊??

sunnyqd 发表于 2014-8-25 17:35:36

sdlibin007 发表于 2014-8-25 17:11
你真是女生啊??

。。。。你见过女生写这个么。。。

jianbo513 发表于 2014-8-25 18:08:54

强烈支持一下 ,很少有人这么弄了。

sunnyqd 发表于 2014-8-25 18:11:16

jianbo513 发表于 2014-8-25 18:08
强烈支持一下 ,很少有人这么弄了。

我是恰好自己学了一下,终于都搞懂了,嘿嘿

qwert1213131 发表于 2014-8-25 18:59:46

对这个不是很了解了。

sdlibin007 发表于 2014-8-25 19:12:20

sunnyqd 发表于 2014-8-25 17:35
。。。。你见过女生写这个么。。。

论坛里面不是还有女生招亲的么??

pengshicao 发表于 2014-8-25 19:22:19

谢谢分享

xlxbangel 发表于 2014-8-25 19:40:44

sdlibin007 发表于 2014-8-25 19:12
论坛里面不是还有女生招亲的么??

哈哈      

sunnyqd 发表于 2014-8-25 19:46:12

sdlibin007 发表于 2014-8-25 19:12
论坛里面不是还有女生招亲的么??

这个明显不一样好不。。

湛泸骏驰 发表于 2014-8-25 19:50:45

楼主要继续更下去哦

浪里白条 发表于 2014-8-25 19:51:30

看楼楼的签名,女娃儿明显做不来这些事情。

sunnyqd 发表于 2014-8-25 19:51:31

湛泸骏驰 发表于 2014-8-25 19:50
楼主要继续更下去哦

码这么一篇累啊。。

sunnyqd 发表于 2014-8-25 19:52:23

浪里白条 发表于 2014-8-25 19:51
看楼楼的签名,女娃儿明显做不来这些事情。

那也可能是一个强悍的女汉子

zhangchaoying 发表于 2014-8-25 19:54:06

谢谢分享,楼主很细心。

sdlibin007 发表于 2014-8-25 20:17:53

xlxbangel 发表于 2014-8-25 19:40
哈哈

前段时间确实见到有个帖子招亲,O(∩_∩)O~

chenguanghua 发表于 2014-8-25 20:27:00

注释的那叫相当详细啊

sunnyqd 发表于 2014-8-25 20:34:46

chenguanghua 发表于 2014-8-25 20:27
注释的那叫相当详细啊

加完这一遍注释之后,我是全部了解啦,嘿嘿,本来也是一知半解

bluestone2012 发表于 2014-8-25 22:14:53

有空学习了,谢谢

lisingch 发表于 2014-8-25 23:11:00

跟着楼主好好学习。

lyzhangxiang 发表于 2014-8-25 23:13:27

__user_initial_stackheap楼主可以看下ARM官方的doc,直接进主页的doc下面搜索关键字看一下

一般在高版本中已经不需要重写他了,内部有各种实现方式,能够自动检测
可以通过分散加载描述文件或者 Image$$ZI$$Limit 的值等

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0349bc/Cihhdahf.html

lyzhangxiang 发表于 2014-8-25 23:13:45

2.10.5. __user_initial_stackheap()
如果您使用的是旧式源代码,则可能会看到 __user_initial_stackheap()。 这是旧函数,支持它只是为了与旧式源代码向后兼容。 其当前等效项是 __user_setup_stackheap()。
从 RVCT v2.x 及更低版本移植到 RVCT v4.0
在 RVCT 2.x 及更低版本中,__user_initial_stackheap() 的缺省实现使用符号 Image$$ZI$$Limit 的值。 如果链接器使用分散加载描述文件(使用 --scatter 命令行选项指定),则不会定义该符号;因此,如果使用的是分散加载描述文件,则必须重新实现 __user_initial_stackheap(),否则,链接步骤将会失败。
另外,您也可以使用 __user_setup_stackheap()(而不是 __user_initial_stackheap())来升级源代码。
从 RVCT v3 移植到 RVCT v4.0
在 RVCT v3.x 和更高版本中,库包含更多的 __user_initial_stackheap() 实现,并且可通过分散加载描述文件中提供的信息自动选择正确的实现。 这意味着,如果使用的是分散加载文件,则不需要重新实现该函数。 有关详细信息,请参阅使用分散加载描述文件。
语法
extern __value_in_regs struct __user_initial_stackheap __user_initial_stackheap(unsigned R0, unsigned SP, unsigned R2, unsigned SL);
用法
__user_initial_stackheap() 返回:
r0 中的堆基址
r1 中的堆栈基址,即堆栈区中的最高地址
r2 中的堆限制
r3 中的堆栈限制,即堆栈区中的最低地址。
如果重新实现了此函数,则必须满足下列条件:
最多使用堆栈中的 88 个字节
不能破坏 r12 (ip) 以外的寄存器
保持堆的 8 字节对齐方式。
对于缺省单区模型,将忽略 r2 和 r3 中的值,并且 r0 和 r1 之间的所有内存始终可供堆使用。 对于双区模型,堆限制是由 r2 设置的,堆栈限制是由 r3 设置的。
调用 __main() 时的 sp (r13) 值将作为 r1 中的自变量进行传递。 __user_initial_stackheap() 的缺省实现(使用半主机 SYS_HEAPINFO)是由 sys_stackheap.o 模块中的库提供的。
要创建从执行环境中继承 sp 并且不使用堆的 __user_initial_stackheap() 版本,请将 r0 和 r2 设置为 r3 的值并返回。 有关详细信息,请参阅__user_setup_stackheap()。
rt_misc.h 中的 __initial_stackheap 的定义是:
struct __initial_stackheap{
    unsigned heap_base, stack_base, heap_limit, stack_limit;
};
Note
stack_base 的值比堆栈使用的最高地址大 0x1,这是因为使用的是满降序堆栈。
有关该函数的重新实现示例,请参阅示例目录。
返回值
r0 到 r3 中返回的值取决于是使用单区内存模型,还是使用双区内存模型:
单区
(r0,r1) 是单个堆栈和堆区。r1 大于 r0,并忽略 r2 和 r3。
双区
(r0, r2) 是初始堆,(r3, r1) 是初始堆栈。r2 大于或等于 r0。r3 小于 r1。
使用分散加载描述文件
__user_initial_stackheap() 的缺省实现使用 Image$$ZI$$Limit 符号的值。 如果链接器使用分散加载描述文件,则不会定义此符号。 不过,C 库提供了替代实现,可通过分散加载描述文件中的信息来使用这些实现。
自动选择单区模型
在分散加载描述文件中定义一个特殊执行区,即 ARM_LIB_STACKHEAP。 该区具有 EMPTY 属性。
这导致库选择一个 __user_initial_stackheap() 实现,它将该区用作复合堆/堆栈区。 它使用 Image$$ARM_LIB_STACKHEAP$$Base 和 Image$$ARM_LIB_STACKHEAP$$ZI$$Limit 符号的值。
自动选择双区模型
在分散加载描述文件中定义两个特殊执行区:        ARM_LIB_HEAP 和 ARM_LIB_STACK。 两个区均具有 EMPTY 属性。
这导致库选择一个使用以下符号值的 __user_initial_stackheap() 实现:Image$$ARM_LIB_HEAP$$Base、Image$$ARM_LIB_HEAP$$ZI$$Limit、Image$$ARM_LIB_STACK$$Base 和 Image$$ARM_LIB_STACK$$ZI$$Limit。
Example 2.10 显示了一个用于定义 ARM_LIB_HEAP 和 ARM_LIB_STACK 的分散加载描述文件示例。 (它是在主示例目录 install_directory\RVDS\Examples 中作为 Cortex-M3.scat 提供的。)
Example 2.10. ARM_LIB_HEAP 和 ARM_LIB_STACK 分散加载描述
FLASH_LOAD 0x0000 0x00200000
{
;; Maximum of 256 exceptions (256*4 bytes == 0x400)
VECTORS 0x0 0x400
{
    ; Exception table provided by the user in exceptions.c
    exceptions.o (exceptions_area, +FIRST)
}

;; Code is placed immediately (+0) after the previous root region
;; (so code region will also be a root region)
CODE +0
{
    * (+RO)
}

DATA 0x20000000 0x00100000
{
    * (+RW, +ZI)
}

;; Heap starts at 1MB and grows upwards
ARM_LIB_HEAP 0x20100000 EMPTY 0x100000-0x8000
{
}
;; Stack space starts at the end of the 2MB of RAM
;; And grows downwards for 32KB
ARM_LIB_STACK 0x20200000 EMPTY -0x8000
{
}
}

将从 ARM_LIB_STACKHEAP(对于单区模型)或 ARM_LIB_STACK(对于双区模型)中对 sp 进行相应的初始化。
错误消息
如果使用分散文件,而没有指定任何特殊区名称,并且没有重新实现 __user_initial_stackheap(),库将生成一条错误消息。

lyzhangxiang 发表于 2014-8-25 23:15:15



看图比较清晰,相关启动文件中所有不清楚的地方都可以参考官方的doc文档说明,非常详细

sunnyqd 发表于 2014-8-25 23:51:29

lyzhangxiang 发表于 2014-8-25 23:13
2.10.5. __user_initial_stackheap()
如果您使用的是旧式源代码,则可能会看到 __user_initial_stackheap() ...

很给力,居然还是中文的,解释的很详细呢

sunnyqd 发表于 2014-8-25 23:51:58

lyzhangxiang 发表于 2014-8-25 23:15
看图比较清晰,相关启动文件中所有不清楚的地方都可以参考官方的doc文档说明,非常详细 ...

找到了,多谢!很好的文档

lyzhangxiang 发表于 2014-8-26 00:11:04

sunnyqd 发表于 2014-8-25 23:51
很给力,居然还是中文的,解释的很详细呢

是啊中文版本的也有
当初我也很惊讶

哎,最近在arm9上移植rtos,网上看了各种资料,参差不齐。还是官方的doc比较靠谱点。

sunnyqd 发表于 2014-8-26 09:52:58

lyzhangxiang 发表于 2014-8-26 00:11
是啊中文版本的也有
当初我也很惊讶



是的,许多书籍都是抄官方文档的

fengyunyu 发表于 2014-8-26 10:01:58

“里面我有一个没搞懂的地方,就是最后的__user_initial_stackheap为什么不加PROC标记”

LZ搞懂了么?

sunnyqd 发表于 2014-8-26 10:03:55

fengyunyu 发表于 2014-8-26 10:01
“里面我有一个没搞懂的地方,就是最后的__user_initial_stackheap为什么不加PROC标记”

LZ搞懂了么? ...

这个没搞懂,但我反汇编查看,这就是作为一个函数调用
我记得有一个地方说,PROC加于不加,只是为了阅读方便和书写条理,但我忘了在哪里看到过了。。。所以我也不确定
PS:兄台看的好仔细

sunnyqd 发表于 2014-8-26 11:12:55

shejian001 发表于 2014-8-26 10:44
好长代码。

我加了很多注释在里面

Lengxue 发表于 2014-8-26 14:18:48

感谢楼主位和47楼的资料 慢慢学习

sunnyqd 发表于 2014-8-29 15:49:16

Lengxue 发表于 2014-8-26 14:18
感谢楼主位和47楼的资料 慢慢学习

多谢支持

myxiaonia 发表于 2014-8-30 20:53:51

我给lz提个意见哈,栈和堆地址不是刚好放在你想的地方的,而是分散加载文件决定的,mdk默认把这个搞定了,去看看这个文件就知道了,在你的当前项目下

myxiaonia 发表于 2014-8-30 20:56:34

至于后面提到不用proc指定,原因是proc指导的就是函数了,会对使用的寄存器有个入栈操作,而这个很明显不需要的,类似于rtos里的任务

myxiaonia 发表于 2014-8-30 20:57:45

第一个问题在坛里搜下xinv1987的帖子,那里bluesky有详细解释

sunnyqd 发表于 2014-8-30 21:05:31

myxiaonia 发表于 2014-8-30 20:53
我给lz提个意见哈,栈和堆地址不是刚好放在你想的地方的,而是分散加载文件决定的,mdk默认把这个搞定了, ...

对的,是通过分散加载文件决定的,我在里面有描述,实际map文件里的位置就是实际的位置吧?

sunnyqd 发表于 2014-8-30 21:06:07

myxiaonia 发表于 2014-8-30 20:56
至于后面提到不用proc指定,原因是proc指导的就是函数了,会对使用的寄存器有个入栈操作,而这个很明显不需 ...

多谢指点,原来这样呢

sunnyqd 发表于 2014-8-30 21:06:54

myxiaonia 发表于 2014-8-30 20:57
第一个问题在坛里搜下xinv1987的帖子,那里bluesky有详细解释

好的,明天电脑搜一下试试,现在在高铁上。。多谢!

javabean 发表于 2014-8-30 22:12:32

好长,收藏先~

myxiaonia 发表于 2014-8-30 22:19:07

sunnyqd 发表于 2014-8-30 21:05
对的,是通过分散加载文件决定的,我在里面有描述,实际map文件里的位置就是实际的位置吧? ...

map文件就是链接后的绝对地址了,你分析的很详细啊,而我却对你的帖子看的还不够仔细
关于flash配置部分,stm32有单独的选项字节配置文件叫stm32option.s,具体在mdk哪个目录我也记不清了

taojie 发表于 2014-8-31 05:53:29

启动文件有中文解析的啊

qinshiysb 发表于 2014-8-31 07:43:09

理解启动文件,更好地连接单片机的工作特点

sunnyqd 发表于 2014-8-31 07:51:56

taojie 发表于 2014-8-31 05:53
启动文件有中文解析的啊

这个是分析的最详细的版本

sunnyqd 发表于 2014-8-31 07:52:11

qinshiysb 发表于 2014-8-31 07:43
理解启动文件,更好地连接单片机的工作特点

是这样子的

DiaoMao_Huang 发表于 2014-8-31 07:54:26

楼主要拿3万的那个

qinshiysb 发表于 2014-8-31 07:55:50

sunnyqd 发表于 2014-8-31 07:52
是这样子的

看这个东西,有点枯燥哈'也不好理解

qinshiysb 发表于 2014-8-31 07:59:08

lyzhangxiang 发表于 2014-8-25 23:15
看图比较清晰,相关启动文件中所有不清楚的地方都可以参考官方的doc文档说明,非常详细 ...

这是一个软件还是一份帮助文档

sunnyqd 发表于 2014-8-31 08:17:52

DiaoMao_Huang 发表于 2014-8-31 07:54
楼主要拿3万的那个

那个鸭梨太大

sunnyqd 发表于 2014-8-31 08:18:56

qinshiysb 发表于 2014-8-31 07:55
看这个东西,有点枯燥哈'也不好理解

很好理解的,整理了一遍我也理解比较透彻了

chjf 发表于 2014-8-31 08:41:48

不错,支持楼主。

sunnyqd 发表于 2014-8-31 09:03:06

chjf 发表于 2014-8-31 08:41
不错,支持楼主。

多谢支持,嘿嘿

DiaoMao_Huang 发表于 2014-9-1 07:52:37

sunnyqd 发表于 2014-8-31 08:18
很好理解的,整理了一遍我也理解比较透彻了

那倒也是,你整理的,你会细心的看

NEXEN1106 发表于 2014-9-1 08:08:48

厉害,强帖要MARK,留着学习

dongyanbo 发表于 2014-9-1 08:41:51

好资料,学习了

FSL_TICS_ZJJ 发表于 2014-11-25 13:17:26

非常好的文章,必须支持!

szy494468597 发表于 2014-11-25 19:55:41

多谢分享留着备用

openm4 发表于 2014-11-25 19:56:03

谢谢分享,收藏。

freefei 发表于 2014-11-25 19:56:28

真够用功的,先赞一下啊

sunnyqd 发表于 2014-11-26 15:18:27

FSL_TICS_ZJJ 发表于 2014-11-25 13:17
非常好的文章,必须支持!

多谢,刚来看又有一个精华啦{:lol:}

orson 发表于 2014-11-26 16:42:08

写贴不易啊,这么多内容,感谢了,顶起!

zhaopk666 发表于 2014-11-26 17:36:37

要认真学习一下,先收藏看看~

秦天 发表于 2014-11-27 08:55:58

这个东西 以前不在意 后来发现还是很有用的

za250010447 发表于 2014-11-27 09:20:49

这个很给力啊

HZKJ 发表于 2014-11-27 09:27:48

有时候,一款芯片都用好久了才想起来研究启动文件。

zhangchaoying 发表于 2014-11-28 19:44:21

这启动文件,看得眼花啊~没坚持看完

kinsno 发表于 2014-11-28 19:53:44

sunnyqd 发表于 2014-8-25 16:57
还第一次被叫柯南兄。。

最近见你的身影比较少了;

彼岸花开@ 发表于 2014-12-7 12:14:29

如果不使用堆空间。,我看到map文件中。都是先分配 .data 在分配rodata 最后.bss 段。确实在SRAM 中都是连续的。随后就是stack 的地址。 栈顶地址就是stack 的地址加上他的大小了、
1、在SRAM 中。这些是连续分布的。。
2、ke02 有两快 RAM .如果keil 中都设置了。那么首先就会从高端开始分配那么变量。
3、这样的话。SRAM_L 是不是就没有使用呢。

eliterxzgxu 发表于 2014-12-7 16:41:51

厉害,这个我还没仔细看过呢,有空了跟着楼主学习下

彼岸花开@ 发表于 2014-12-7 21:39:40

你代码的map部分有这段 代码 ;   0x20000068   0x00000100   Zero   RW         48    HEAP                startup_mkl02z4.o
1、 请问是不是在程序中使用malloc()函数
2、我在启动文件中设置了堆的大小,但是看不到map 文件的有关堆的信息、

yangzi8000 发表于 2014-12-9 16:30:22

mark。。。。。。。。。。。

qs6361036 发表于 2014-12-10 21:24:14

注释写的很仔细 , 很好的帖子 ,好好学习一下 。

7inspire 发表于 2014-12-11 10:45:19

这么详细,竟然还有注释,

湛泸骏驰 发表于 2014-12-11 12:59:07

7inspire 发表于 2014-12-11 10:45
这么详细,竟然还有注释,

学习一下启动文件。确实对理解单片机很有帮助。。

blue1025 发表于 2015-2-8 13:49:19

好东西,正需要呢,谢谢!{:tongue:}

fiaanull 发表于 2015-2-9 10:08:39

最近要用到,收藏看看再说!谢谢楼主了.

子鱼 发表于 2015-2-9 10:59:05

认真学习一下,收藏看看~
页: [1]
查看完整版本: 【网友经验分享】飞思卡尔Cortex M0+系列 Keil MDK启动文件详解