历史上的今天
返回首页

历史上的今天

今天是:2025年01月16日(星期四)

正在发生

2020年01月16日 | ARM Cortex-M底层技术(三)启动代码的使用

2020-01-16 来源:eefocus

Cortex-M启动代码的使用


    上一篇扯了一些关于启动代码的原理,了解了额原理不去使用意义就没有那么大了,了解了启动代码不是终极目的,我们的目的是深入理解Cortex-M系列MCU的底层原理,并应用到实际的产品中,加速开发,提升产品稳定性;下面就小编我的实际使用经历来看一下,启动代码的具体应用。


    启动代码的本质是在程序进入用户代码(main函数)之前初始化向量表、完成分散加载以及C语言运行环境初始化的一段代码,可以说你需要在进入用户代码(main函数)之前需要搞定的工作都可以放在这里来完成,而且有些代码放到启动代码里面去完成会比在用户代码,main函数最开始完成效果要好得多。


1、如何在启动代码中调用函数?以及什么样的函数不能再启动代码中被调用?


    因为默认的标准的启动代码主要工作是在Reset_Handler里面完成的,调用函数一般也会再这里。


Reset_Handler   PROC


                EXPORT  Reset_Handler               [WEAK]


                IMPORT  SystemInit


                IMPORT  __main



                LDR     r0, =SystemInit


                BLX     r0


                LDR     r0, =__main


                BX      r0


                ENDP


    上面代码展示了如何在Reset_Handler导入并调用SystemInit()函数,首先导入SystemInit()函数的标号


                IMPORT  SystemInit

    然后调用函数,当然这里是用汇编语言来完成的


                LDR     r0, =SystemInit

                BLX     r0

    看不懂的童鞋自己去补汇编课哈。所以在启动代码的汇编语言里调用C语言函数都可以使用以上两步:


    (1)导入函数标号;   


    (2)调用这个函数;


    当然这里有几点注意事项,这里不是所有函数都可以在汇编语言中调用的,因为此时__main还没有运行,C语言运行环境还没有被完整搭建起来,堆栈也没有初始化完成,所以要注意:


    (1)调用的C函数参数不能超过4个,不用可以,但用的话不能超过4个参数,原因是在Cortex-M体系MCU中,函数的1-4个形参会压进R0-R3这4个通用寄存器(Cortex-M系列MCU,M0也好、M3也好、M4也好都只有16个通用寄存器,内部寄存器结构去参照ARM官方的白皮书)如果有第五个参数,这个参数会被压栈,但因为此时__main还没有运行,堆栈没有被初始化所以此时如果函数有超过4个以上的参数,会导致程序跑飞;


    (2)不要把需要调用的函数写到__main之后,因为没有意义,程序不会跑到那里;


2、什么样的功能或者函数适合放在启动代码中启动或者执行?在main()函数里面执行不行吗?有什么区别?


    这个问题的关键就是__main(不是main函数是__main,在上一篇文章中有对他们两个区别的详细论述),说白了启动代码中以及main()函数或用户代码中的最大区别就是是在__main之前还是之后。


    下面小编我罗列几种适合在启动代码中执行即__main之前执行,效果好过在main()函数中执行即__main之后执行的几种典型情况:


    (1)FPU(浮点协处理单元)初始化:


    准确的说这个是必须在启动代码里面也就是__main之前执行的代码,FPU只在Cortex-M4&Cortex-M7以及刚刚发布的Cortex-M33中才有,Cortex-M3&Cortex-M0/M0+中没有,原因是__main中会初始化浮点C的浮点库,如果在这之前浮点硬件没有被初始化,程序会直接挂掉跑飞。当然你也可以选择不用FPU,在开发环境的编译器选项中都会有这个选项,即不启用FPU,这样你就可以不去初始化FPU了,程序也不会有问题,但只要你用FPU,你就要在__main之前初始化FPU,以LPC54608为例FPU初始化代码集成在了SystemInit函数中,我也用过有单独函数去初始化FPU的MCU,代码基本上都是厂商SDK里面会提供的,你只需要调用就好了;


    (2)片外SRAM/SDRAM初始化:


    有很多有过开发经验的老同志马上跳起来了“老子之前用过片外SDRAM,是在main()函数里面才初始化的,也没出过问题,你别在这瞎BB。”原因是这样的,看情况,看你是否要把一部分RW/ZI数据段放到片外片外SRAM/SDRAM上去初始化,就是说看你是否需要要分散加载过程作用到你的片外SRAM/SDRAM。如果要,那就需要在启动代码__main之前调用片外SRAM/SDRAM的初始化函数;如果不要,则无所谓,在main函数里面初始化片外SRAM/SDRAM也行。


    你也许会问,什么情况下我们需要分散加载作用到片外SRAM/SDRAM?(分散加载以后会专门开几篇文章来讨论,这里先过)其实有很多种情况,以后讨论分散加载再细说,这里先举个例子,还是拿LPC54608来举例,其他Cortex-M类的MCU情况相同,LPC54608片内有200KB的SRAM,最大连续的SRAM有128KB,另外72KB与这128KB的SRAM在地址上不连续,这是一个细节(大家在选择MCU时也要注意地址的连续性)比如我要开一个150K的BUFFER,来缓存我程序中间的数据,看似LPC54608片内有200KB的SRAM可以实现150K的BUFFER,但实际不然,编译器无法跨地址来分配这种BUFFER,也就是说实际的课分配单一最大BUFFER为128KB,但是你要150KB,所以没办法,你要外扩SDRAM,因为这个BUFFER为编译器去分配空间以及初始化的,所以必然会被计算为RW/ZI数据段,这样这个数据段就需要被分散加载,这就需要把外扩SDRAM的初始化代码放到__main之前,这样__main才会知道片外有一个SDRAM的空间需要被初始化(这也需要分散加载脚本文件的配合,以后文章再细说)。也有其他情况需要在启动代码中初始化SDRAM的,所以个人建议片外SRAM/SDRAM初始化最好放到启动代码里面就是__main之前,因为这样基本不会错,放到用户代码里面会有很多情况Cover不到。


    (3)看门狗的开关、低电压检测等初始化:


    有些厂商的MCU会在芯片的BOOTROM里面打开看门狗,可以理解为一上电看门狗就开了,有些厂商则不然,需要根据应用自己去开关开门狗;这里的核心问题是__main的运行时间,考虑到这个因素,我强烈建议把看门狗,低电压检测等功能放到启动代码中也就是__main之前来初始化。


    __main的运行时间???纳尼??什么鬼??没错,这是一个很重要的隐形要素,__main的运行时间是根据你的用户代码来决定的,还记得上篇文章中提到__main的功能吗?


    初始化堆栈


    初始化C Library


    分散加载


    所以使用MacroLib速度就会快一些,使用标准C Lib会慢一点,分散加载内容多会慢一点,反之会快一些,当然也跟MCU的主频相关,主频高速度也会快,大概__main的运行时间从100-200微秒到几十个毫秒之间。所以如果程序在这中间发生一些BUG,比如你在__main之前调用了几个初始化函数里面有BUG,或者__main运行时电压发生跌落等情况时,MCU会对这些紧急情况有所反应,比如复位,或者保护重要数据等,增加系统稳定性。因为如果你的程序比较复杂__main可能要运行几十个毫秒,对MCU来讲,几十个毫秒以及是很长的时间了,尤其是你的产品出货量很大的情况下,各种小概率事件都会被用量放大为大的品质问题,所以这段时间MCU的运行安全也是需要考虑的。所以我建议启动代码的结构如下,把看门狗&低电压检测放到最前面:    


Reset_Handler   PROC

                EXPORT  Reset_Handler               [WEAK]

                IMPORT  SystemInit

                IMPORT  LVDInit

                IMPORT  WDTInit

                IMPORT  OtherInit

                IMPORT  __main

 

                LDR     r0, =WDTInit                ; 看门狗初始化

                BLX     r0,

                LDR     r0, =LVDInit                ; 低电压检测初始化

                BLX     r0

                LDR     r0, =SystemInit             ; 系统初始化

                BLX     r0

                LDR     r0, =OtherInit              ; 其他你需要的初始化

                BLX     r0

                LDR     r0, =__main

                BX      r0

                ENDP

    (4)时间敏感性任务/操作等:


    核心问题还是__main的运行时间,有些产品会对实时性要求很高,要求在很短的时间内响应某些重要事件,考虑到一旦进了__main,就会有一段时间无法操作MCU,所以在__main之前把需要搞定的时间敏感性任务/操作等搞定。


推荐阅读

史海拾趣

FORMOSA公司的发展小趣事

在半导体技术日新月异的时代,一家名为“FORMOSA半导体科技”的公司凭借其在先进制程技术上的突破,迅速在行业内崭露头角。该公司专注于研发和生产高性能的处理器和存储器芯片,为智能手机、数据中心等高端应用提供核心动力。通过持续的研发投入和与全球顶尖科技公司的合作,FORMOSA半导体科技成功打破了多项技术壁垒,其产品在市场上赢得了广泛赞誉。公司还积极响应绿色环保的号召,推出了一系列低功耗、高效率的半导体解决方案,为可持续发展贡献力量。

E-T-A Circuit Breakers公司的发展小趣事

自1970年代起,E-T-A公司开始积极拓展全球市场,逐渐在国际上建立了知名度。公司设立了多个分支机构和办事处,覆盖了全球60多个国家,为当地客户提供优质的销售和技术支持。这一举措不仅增强了公司的国际竞争力,也为其在全球范围内推广先进的电路保护技术提供了有力支持。

Barkston Plastics Engineering Ltd.公司的发展小趣事

随着电子行业的快速发展,Barkston Plastics Engineering Ltd.意识到单一产品线的局限性,于是开始扩展其产品线,涵盖更多种类的塑料电子元件。同时,公司积极寻求与行业内其他企业的战略合作,通过技术共享和市场资源互补,共同开拓更广阔的市场。这一战略转型不仅提升了Barkston的市场竞争力,也为其后续发展奠定了坚实的基础。

Engineered Components Co公司的发展小趣事

为了确保产品质量和客户满意度,ECC建立了完善的质量管理体系。他们从原材料采购到生产、检测、包装等各个环节都制定了严格的质量标准。ECC还引入了先进的质量检测设备和方法,确保每一件产品都符合高标准的质量要求。此外,ECC还定期对员工进行质量培训,提高员工的质量意识和操作技能。这些措施使得ECC的产品在市场上赢得了良好的口碑和信誉。

Diconex公司的发展小趣事

人才是企业发展的核心动力。Diconex公司高度重视人才培养和引进工作。公司建立了完善的人才培养机制,为员工提供广阔的发展空间和良好的职业晋升通道。同时,公司还积极引进行业优秀人才,为公司的快速发展提供了有力的人才保障。这种人才战略的成功实施使得Diconex在电子行业中拥有了强大的技术团队和人才队伍。

Cambridge Electronic Industries Ltd公司的发展小趣事

面对日益严峻的环境问题和社会责任,CEI积极响应可持续发展的号召。公司加大了对环保技术的研发投入,推出了多款节能、环保的电子产品。同时,CEI还关注员工福利和社会公益事业,积极参与社会捐赠和公益活动。展望未来,CEI将继续坚持技术创新和可持续发展战略,为电子行业的繁荣和发展做出更大的贡献。

通过以上五个故事,我们可以看到Cambridge Electronic Industries Ltd公司在电子行业里发展起来的艰辛与辉煌。他们凭借技术创新、市场拓展、品质管理和可持续发展等方面的努力,逐渐成为了电子行业的佼佼者。

问答坊 | AI 解惑

RAM的扩展,RAM的扩展仿真

RAM的扩展,RAM的扩展RAM的扩展,RAM的扩展RAM的扩展,RAM的扩展RAM的扩展,RAM的扩展…

查看全部问答>

关于毕设DSP的选择

麻烦大家了。毕业设计将要做的是视频图像那块,然后在DSP上实现。发现现在大多的论文都是在TI上完成的。 基于一些原因,我想如果在ADI的DSP上去实现,会不会不太主流,请大家帮忙分析一下还有什么弊端。先谢过。…

查看全部问答>

LPCXpresso下载与安装

热烈庆祝获得LPC1343开发板,写了个LPCXpresso下载与安装的教程。 1.LPCXpresso下载 lpcxpresso_3.3.4_170.exe下载地址,需要简单注册 http://lpcxpresso.code-red-tech.com/LPCXpresso/ 2.  安装过程 我选择的是默认设置,所以一 ...…

查看全部问答>

wince上下载图片文件并保存

现在我知道网上有个图片的地址,我想用代码实现下载并保存下,有什么方法实现阿…

查看全部问答>

AMD回应英特尔抢先发新品:时间早晚并不重要

按照计划,AMD的四核处理器“巴塞罗那”的发布日期为2007年9月10日。然而,其老对手英特尔却抢先一步,将其新一代四核至强处理器——7300系列新品的发布日期由原定的9月中旬提前至9月6日。针对英特尔的“抢先”举措,8月28日,AMD大中华区计算产品 ...…

查看全部问答>

MQX BSP移植指南

这是我们写的一个MQX操作系统的移植指南。转载请注明出处…

查看全部问答>

《AlientekSTM32例程手册》28个实验连载--串口实验--整理后

1.注意我们的教材讲解是基于寄存器操作,方便初学者理解透彻, 2.我们另外还提供了该实例的库函数源码,下载链接:https://bbs.eeworld.com.cn/icview-210815-1-1.html 3.此实验的教程在《Alientek STM32不完全手册》的 3.3节:   ...…

查看全部问答>

51最小系统(含原理图和PCB)

51最小系统(含原理图和PCB)…

查看全部问答>

NVIC_SetPriority(SysTick_IRQn, 0x04)

replyreload += \',\' + 1316591;NVIC_SetPriority(SysTick_IRQn, n);n=0x00~0x03  设置Systick为抢占优先级0n=0x04~0x07  设置Systick为抢占优先级1n=0x08~0x0B  设置Systick为抢占优先级2n=0x0C~0x0F   ...…

查看全部问答>