历史上的今天
返回首页

历史上的今天

今天是:2025年01月15日(星期三)

正在发生

2019年01月15日 | Cortex-M0(NXP LPC11C14)启动代码分析

2019-01-15 来源:eefocus

启动代码的一般作用


1、堆和栈的初始化;


2、向量表定义;


3、地址重映射及中断向量表的转移;


4、初始化有特殊要求的断口;


5、处理器模式;


6、进入C应用程序。


ARM复位后程序从0x00地址开始执行代码,所以一般都会有将Flash地址映射到0x00的过程。但对于这一款Cortex M0的启动代码比较简单,从存储分布图中我们可以看到LPC11C14拥有32K的片内Flash,地址范围是0x0000 0000 ~ 0x0000 8000,当我们将程序(小于32K)烧写进片内Flash时,启动代码中就可以不用再对Flash的地址重新映射。

NXP LPC11C14存储分布图主要看Flash



CortexM0的启动代码进行分析:


一、堆栈初始化部分


在程序开始处,首先定义栈的大小及属性,然后对堆进行初始化操作,ARM-Thumb过程调用标准和ARM、Thumb C/C++ 编译器总是使用满减(Full descending)类型堆栈。

;

;

;
        Stack_Size EQU 0x00000020        //定义堆栈大小

        AREA STACK, NOINIT, READWRITE, ALIGN=3  //定义一个数据段按8个字节对齐 AREA伪指令用于定义一//个代码段或者数据段
                                                                                          //NOINIT定义此数据段仅仅保留了内存单元,而没有将各初
                                                                                          //始值写入内存单元,或者将各个内存单元值初始化为0
        Stack_Mem SPACE Stack_Size        //保留Stack_Size 大小的堆栈空间,来分配连续Stack_Size
                                                                          //节的存储单元并初始化为0

        __initial_sp        //标号--堆栈顶部地址。M0中堆栈式满递减堆栈,堆栈指针位//于堆栈的高地址

        Stack_Size EQU 0x00000020        //定义堆栈大小

        ; 

        Heap_Size EQU 0x00000000        // 定义堆空间大小

        AREA HEAP, NOINIT, READWRITE, ALIGN=3        // 定义了一个数据段,8字节对齐

        __heap_base        // 标号--代表为堆末底部地址

        Heap_Mem SPACE Heap_Size        // 保留Heap_Size的堆空间
        __heap_limit        // 标号--堆的界限地址

                        PRESERVE8        // 指令指定当前文件保持堆栈8字节对齐。它设置PRES8编译属性,以
                                                      //通知链接器。链接器检查要求堆栈8字节对齐的任何代码是否仅由保持//堆栈8字节对齐的代码直接或者间接地调用。

                        THUMB          //指示编译器以后的伪指令为Thum指令


二、中断量表定义


在MDK生成分散加载文件中,RESET被设置在flash的0地址处,这样就规定了向量表的地址。

; Vector Table Mapped to Address 0 at Reset

        AREA RESET, DATA, READONLY         //定义只读数据段,位于0地址,其实放在CODE区
                                                                        //EXPORT在程序中声明一个全局的标号__Vetors号可
                                                                        //可以在其他文件中使用

/*
        DCD伪指令用于分配一片连续的字存储单元并用伪指令中指定的表达式初始化。其中,表达式可以为程序标号或数字表达式。DCD也可用“&”代替。
        */

__Vectors   DCD   __initial_sp ; Top of Stack // 给__initial_sp分配4字节32位的地址

        DCD   Reset_Handler   ; Reset Handler//给标号Reset_Handler分配地址
        DCD   NMI_Handler   ; NMI Handler //给标号NMI_Handler分配地址
        DCD   HardFault_Handler ; Hard Fault Handler
        DCD  MemManage_Handler; MPU Fault Handler
        DCD  BusFault_Handler ; Bus Fault Handler
        DCD   UsageFault_Handler; Usage Fault Handler
        DCD  0; Reserved //保留的,不给任何标号分配
        DCD   0 ; Reserved
                        DCD   0  ; Reserved
                        DCD   0  ; Reserved
                        DCD   SVC_Handler   ; SVCall Handler
                        DCD   DebugMon_Handler  ; Debug Monitor Handler
                        DCD   0  ; Reserved
                        DCD   PendSV_Handler   ; PendSV Handler
                        DCD   SysTick_Handler   ; SysTick Handler

                        ; External Interrupts
                        DCD   WAKEUP_IRQHandler   ; 15 wakeup sources for all the
                        DCD   WAKEUP_IRQHandler   ; I/O pins starting from PIO0 (0:11)
                        DCD   WAKEUP_IRQHandler   ; all 40 are routed to the same ISR 
                        DCD   WAKEUP_IRQHandler   
                        DCD   WAKEUP_IRQHandler   
                        DCD   WAKEUP_IRQHandler
                        DCD   WAKEUP_IRQHandler
                        DCD   WAKEUP_IRQHandler 
                        DCD   WAKEUP_IRQHandler 
                        DCD   WAKEUP_IRQHandler 
                        DCD   WAKEUP_IRQHandler
        DCD   WAKEUP_IRQHandler
                        DCD   WAKEUP_IRQHandler   ; PIO1 (0:11)
                        DCD   CAN_IRQHandler   ; CAN 
                        DCD   SSP1_IRQHandler   ; SSP1 
                        DCD   I2C_IRQHandler   ; I2C
                        DCD   TIMER16_0_IRQHandler   ; 16-bit Timer0
                        DCD   TIMER16_1_IRQHandler   ; 16-bit Timer1
                        DCD   TIMER32_0_IRQHandler   ; 32-bit Timer0
                        DCD   TIMER32_1_IRQHandler   ; 32-bit Timer1
                        DCD   SSP0_IRQHandler   ; SSP0
                        DCD   UART_IRQHandler   ; UART
                        DCD   USB_IRQHandler   ; USB IRQ
                        DCD   USB_FIQHandler   ; USB FIQ
                        DCD   ADC_IRQHandler   ; A/D Converter
                        DCD  WDT_IRQHandler   ; Watchdog timer
                        DCD   BOD_IRQHandler   ; Brown Out Detect
                        DCD   FMC_IRQHandler   ; IP2111 Flash Memory Controller
                        DCD   PIOINT3_IRQHandler   ; PIO INT3
                        DCD   PIOINT2_IRQHandler   ; PIO INT2
                        DCD  PIOINT1_IRQHandler   ; PIO INT1
                        DCD   PIOINT0_IRQHandler   ; PIO INT0

        IF   :LNOT::DEF:NO_CRP   //进行宏定义判断
        AREA   |.ARM.__at_0x02FC|, CODE, READONLY

/******************************************************************************************************************************

这有几个关键的地方“NO_CRP”、 0x02FC和0xFFFFFFFF,如果我们在前面定义有“NO_CRP”,那么我们后面的代码也就不起作用了,所以在需要加密的时候前面就一定不能再定义了 代码读保护,也就是加密的关键字,经过加密后芯片再也无法擦除,除非之前烧写的程序带有IAP,IAP可以使芯片进入ISP模式。

******************************************************************************************************************************/

CRP_Key   DCD  0xFFFFFFFF // 加密等级
        ENDIF

        AREA   |.text|, CODE, READONLY   //代码段定义

利用PROC,ENDP这一对伪指令把程序段分为若干个过程,是程序的结构更加清晰

; Reset Handler
        Reset_Handler   PROC//过程的开始
        EXPORT   Reset_Handler [WEAK]   //[WEAK]弱定义,意思是如果在别处也定义该标
                          //(函数),在连接时,用别处的地址。如果没有
                          //他地方定义,编译器也不报错,以此处地址进行链接

        IMPORT __main  // 通知编译要使用标号在其他文件
        LDR R0, =__main   //使用“=”表示LDR目前是伪指令,不是标准指令。把__main
                         //地址赋给R0。
        BX R0   //BX是ARM指令集和THUMB指令集之间程序的跳转
        ENDP //过程结束

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

        NMI_Handler   PROC
        EXPORT NMI_Handler   [WEAK]
        B   .//原地跳转(即无限循环)
        ENDP
        HardFault_Handler\
          PROC
          EXPORT HardFault_Handler   [WEAK]
          B   .
          ENDP
        MemManage_Handler\
          PROC
          EXPORT MemManage_Handler   [WEAK]
          B   .
          ENDP
        BusFault_Handler\
          PROC
          EXPORT BusFault_Handler   [WEAK]
          B   .
          ENDP
        UsageFault_Handler\
          PROC
          EXPORT UsageFault_Handler   [WEAK]
          B   .
          ENDP
        SVC_Handler   PROC
          EXPORT SVC_Handler   [WEAK]
          B   .
          ENDP
        DebugMon_Handler\
          PROC
          EXPORT DebugMon_Handler   [WEAK]
          B   .
          ENDP
        PendSV_Handler PROC
         EXPORT PendSV_Handler   [WEAK]
         B   .
         ENDP
        SysTick_Handler PROC
          EXPORT SysTick_Handler   [WEAK]
          B   .
          ENDP

        Default_Handler PROC

          EXPORT    WAKEUP_IRQHandler    [WEAK]
          EXPORT   CAN_IRQHandler    [WEAK]
          EXPORT    SSP1_IRQHandler    [WEAK]
          EXPORT    I2C_IRQHandler    [WEAK]
          EXPORT    TIMER16_0_IRQHandler     [WEAK]
          EXPORT    TIMER16_1_IRQHandler     [WEAK]
          EXPORT   TIMER32_0_IRQHandler     [WEAK]
          EXPORT   TIMER32_1_IRQHandler    [WEAK]
          EXPORT   SSP0_IRQHandler     [WEAK]
          EXPORT   UART_IRQHandler     [WEAK]

          EXPORT    USB_IRQHandler    [WEAK]
          EXPORT    USB_FIQHandler     [WEAK]
          EXPORT    ADC_IRQHandler    [WEAK]
          EXPORT    WDT_IRQHandler     [WEAK]
          EXPORT    BOD_IRQHandler     [WEAK]
          EXPORT   FMC_IRQHandler     [WEAK]
          EXPORT   PIOINT3_IRQHandler     [WEAK]
          EXPORT   PIOINT2_IRQHandler     [WEAK]
          EXPORT   PIOINT1_IRQHandler     [WEAK]
          EXPORT   PIOINT0_IRQHandler     [WEAK]

        WAKEUP_IRQHandler
        CAN_IRQHandler
        SSP1_IRQHandler
        I2C_IRQHandler
        TIMER16_0_IRQHandler
        TIMER16_1_IRQHandler
        TIMER32_0_IRQHandler
        TIMER32_1_IRQHandler
        SSP0_IRQHandler
        UART_IRQHandler
        USB_IRQHandler
        USB_FIQHandler
        ADC_IRQHandler
        WDT_IRQHandler
        BOD_IRQHandler
        FMC_IRQHandler
        PIOINT3_IRQHandler 
        PIOINT2_IRQHandler 
        PIOINT1_IRQHandler
        PIOINT0_IRQHandler
          B .
          ENDP

          ALIGN填充字节使地址对齐

        ; User Initial Stack & Heap


三、堆和栈的初始化


IF   :DEF:__MICROLIB   // “DEF”的用法:DEF:xx就是说xx定义了则为真,否则为假

        EXPORT __initial_sp   //则将栈顶定制
        EXPORT __heap_base   // 堆起始地址赋予全局属性
        EXPORT __heap_limit   //堆末端界限地址赋予全局属性,使外部程序可调用

        ELSE   //如果没有定义__MICROLIB,则使用默认的C运行时库

        IMPORT __use_two_region_memory   // 通知编译器要使用的标号在其他文件//__use_two_region_memory
        EXPORT __user_initial_stackheap   // 声明全局标号__user_initial_stackheap,这样外程序也可调用此标号
                          //则进行堆栈和堆的赋值,在__main函数执行过程中调用
                          //如果了使用默认的C库,程序启动过程中不会执行标号下//的代码

/******************************************************************************************************************************

_user_initial_stackheap() 返回:

· r0 中的堆基址
        · r1 中的堆栈基址,即堆栈区中的最高地址
        · r2 中的堆限制
        · r3 中的堆栈限制,即堆栈区中的最低地址。

有单区模型和双区模型。

单区模型:(r0,r1)是单个堆栈和堆区。r1 大于 r0,并忽略 r2和r3。
        r0--r1这一块内存区域被堆和栈共用,堆从r0向上生长,栈从r1向下生长。

双区模型:(r0, r2)是初始堆,(r3, r1) 是初始堆栈。r2 大于或等于r0,r3小于r1。
        堆和栈分别指定了单独的内存区域。

******************************************************************************************************************************/

__user_initial_stackheap   // 标号__user_initial_stackheap,表示拥护堆栈初始化程序入口
                       //则进行堆栈和堆得赋值,在__main函数执行过程中调用
        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


推荐阅读

史海拾趣

屹晶微(EG)公司的发展小趣事

屹晶微的创始人黄米龙,原本在发电厂从事电气运营工作长达八年。这段经历让他对电子领域有了深入的了解和浓厚的兴趣。然而,他并没有满足于现状,而是看到了中国芯片产业的巨大潜力和发展空间。于是,在2007年,他毅然决定从发电厂辞职,利用自己的积蓄和借来的资金,在台州创立了屹晶微电子有限公司。

在创立初期,屹晶微面临着资金短缺、技术落后和市场竞争激烈的困境。但黄米龙凭借对电子行业的深刻理解和坚定的信念,带领团队克服了种种困难。他们不断引进先进技术和设备,加强研发力量,提升产品质量。经过几年的努力,屹晶微逐渐在芯片设计领域崭露头角,并成功推出了多款具有自主知识产权的芯片产品。

ETC2公司的发展小趣事

为了拓展全球市场,ETC2公司制定了国际化战略。他们积极参加国际展览和交流活动,与国际同行建立了广泛的合作关系。同时,ETC2公司还针对不同国家和地区的市场特点,推出了定制化的ETC产品和服务。这些举措不仅帮助ETC2公司成功打入国际市场,也为其未来的发展奠定了坚实的基础。

华大北斗(Allystar)公司的发展小趣事

ETC2公司深知服务对于企业的重要性,因此不断优化服务体系以提升竞争力。他们建立了完善的客户服务体系,为用户提供24小时在线客服支持,确保用户在使用过程中遇到问题能够及时得到解决。此外,ETC2公司还定期举办用户培训活动,帮助用户更好地了解和使用ETC设备。这些举措不仅提高了用户的满意度和忠诚度,也为ETC2公司赢得了更多的市场份额。

DREMEL公司的发展小趣事

在电子行业的快速发展中,Dremio公司以其独特的数据治理技术崭露头角。他们开发了一种新型的数据处理引擎,能够直接在云数据湖中查询和分析数据,无需将数据复制到专有数据仓库中。这一创新技术大大提高了数据处理效率,降低了成本,并为企业提供了更灵活的数据管理方式。Dremio凭借这一技术,迅速在电子行业中树立了领先地位。

Broadcom(博通)公司的发展小趣事

进入21世纪,随着网络泡沫的破灭,博通陷入了严重的财务困境。亏损累计高达65亿美元,股价大幅下跌,公司甚至不得不裁员以维持运营。然而,正是在这样的困境中,博通展现出了坚韧不拔的精神。通过优化产品结构、调整市场策略,博通逐渐走出了低谷,并在2003年推出了全球第一个802.11b单片机,重新赢得了市场的关注。

EPCOS/TDK公司的发展小趣事

2009年,EPCOS与TDK元件事业部合并,标志着两家公司在电子元器件领域的强强联合。合并后的公司更名为TDK-EPC,由位于日本的TDK-EPC公司管理。这一合并进一步增强了EPCOS在电子元器件领域的实力,使其在全球市场中的地位更加稳固。合并后,公司继续投入大量研发资源,致力于开发更小、更轻、更高效的电子元器件,以满足不断增长的市场需求。

问答坊 | AI 解惑

枕头里的私人音乐空间

这是一个内置音乐播放器、扬声器的枕头,手提箱式的设计方便携带,打开之后还可以展开一张很大的毯子,就算是躺在操场的草坪上,也不用担心草尖会扎到脖颈。内置的播放器可以识别U盘或存储卡,随时随地可以营造一个有轻音乐伴随的休憩空间。 …

查看全部问答>

100分求一个用于C#上能带图片的按钮控件.

我想用C#开发WinCE的程序,找了半天没有找到怎么在按钮上放图片,听说是没有带这样的控件,  都是第三方的才行. 哪位有发给小弟一个. …

查看全部问答>

求助:关于windows mobile5的gprs上网,ppp协商过程失败

每次协商过程都是一样的,分析ppp协议,好像每次都是被基站拒绝,以后窜口就没有反应了。 打印信息如下: default : Other RIL_ APIs IOCTL_RIL_SETGPRSCONTEXT RilDrv: Sending cmd: AT+CGDCONT=1,\"IP\",\"cmnet\",\"\",0,0 710MUX: Chnl CO ...…

查看全部问答>

新手求救:EVC开法软件怎么把附属的文件下载到模拟器中?

EVC程序点编译则执行文件自动下载到模拟器中,可我的程序运行时要读入一个数据文件,怎么把这个数据文件同时下载到模拟器中?谢谢各位大侠!…

查看全部问答>

加breakpoint不会停

在自己公司的开发板上开发wince5.0,基于2443开发包修改 一切都已运行,但pause后加breakpoint后,运行到断点处不会停, 有时停止后,继续运行,就会crash掉,即使在运行不到的地方加断点, 然后取掉,再运行也会crash掉,不知有人遇到过这种情 ...…

查看全部问答>

usb实现两个com口

const u8 Virtual_Com_Port_ConfigDescriptor[] =  {    /*Configuation Descriptor*/    0x09,   /* bLength: Configuation Descript ...…

查看全部问答>

从51转到stm32需要学习哪些知识?

                                 最近订购了一块stm32的板子.想学习这个芯片,得学习哪些知识.老手给指点下…

查看全部问答>

关于批量生产时,向芯片中烧写程序的问题!

现在产品已基本上开发出来,即将批量生产,但是却存在一个问题,在以前开发阶段,我总是通过在线 编程的方式将程序写入芯片,现在要批量生产了,总不能还通过这种方式吧,能不能向51单片机一样将程序编译成HEX或二进制文件,通过编程器,先加密,然后写到片子 ...…

查看全部问答>

【MP430共享】MSP430的一氧化碳报警器

我对报警器有特殊的感情,所以特别观注这个,MSP430以省电著称,再加上一氧化碳传感器,非常省电了应该。我想不是不能用电池供电,最好,电源供电,为什么?因为安全,那个传感器得无时不刻地烧着。 …

查看全部问答>

【视频分享】电源设计小贴士50:铝电解电容器常见缺陷的规避方法

大家看过电源设计小贴士49后觉得怎么样? 现在继续和大家分享电源设计小贴士50:铝电解电容器常见缺陷的规避方法。 因其低成本的特点,铝电解电容器一直都是电源的常用选择。但是,它们寿命有限,且易受高温和低温极端条件的影响。铝电解电容器在 ...…

查看全部问答>