当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]多数嵌入式应用程序最初都是在原型环境下开发的。无论什么样的原型仿真环境与最终产品环境都是有差异的。因此,考虑如何将嵌入式应用程序从其所依赖的开发工具或调试环境中移植到在目标硬件上独立运行是非常重要的。

13.2 编译器的缺省行为

多数嵌入式应用程序最初都是在原型环境下开发的。无论什么样的原型仿真环境与最终产品环境都是有差异的。因此,考虑如何将嵌入式应用程序从其所依赖的开发工具或调试环境中移植到在目标硬件上独立运行是非常重要的。

开始编写嵌入式应用程序时,开发者可能并不清楚目标硬件的具体规格。如,目标系统使用了什么样的外围设备、存储器映射情况甚至不能确定处理器的型号。

为在了解这些详细信息前能够继续软件的开发,RVCT工具提供了很多默认的操作,使用户能编译和调试与目标系统无关的应用程序代码。下面详细介绍这些编译选项,只有深入了解这些编译选项设置,才能使开发更顺利的进行。

13.2.1 Semihosting1.Semihosting简介

在RVCT C库中,对某些ISO C功能的支持由主机调试环境提供。提供该功能的机制被称为Semihostin[1]。大多数的ARM调试系统都支持Semihosting机制,如ReslView Debugger AXD等。

调试系统提供这种机制是非常有用的,因为用于开发使用的硬件系统经常没有最终系统的所有输入和输出设备。在这种情况下,Semihosting可让主机代替目标系统提供这些设备的功能。举例来说,此机制可以用于启用C库中的函数(例如,printf()和scanf())使用主机的屏幕和键盘,而不使用目标系统的屏幕和键盘。

半主机由一组已定义的SWI操作来实现。应用程序调用相应的SWI,然后由调试代理程序(Debug Agent)处理SWI异常。调试代理程序完成系统与主机之间的通信。

图13.1显示了Semihosting机制的处理过程。

图13.1 Semihosting机制的处理过程

在很多情况下,Semihosting SWI由库函数内的代码调用。应用程序也可以直接调用。支持ARM C库中Semihosting的详细信息,请参阅ARM相关文档。

2.Semihosting软件接口

ARM和Thumb SWI指令包含一个软中断号,该中断号可以被应用程序使用。此编号可以由系统中的SWI处理程序进行解码。有关SWI处理程序的详细信息,请参阅本书中ARM异常处理一节。

Semihosting使用固定的中断号调用相应的处理程序。用于Semihosting的SWI是:

· 0x123456(在ARM状态下);

· 0xAB(在Thumb状态下)。

注意

用户在编写自己的中断处理程序时,避免使用Semihosting已经使用的中断向量号。

调试代理通过SWI的中断向量号识别该软中断是目标系统提出的Semihosting请求。具体是何种Semihosting请求(键盘输入请求或屏幕显示请求),通过向寄存器r0传递不同的参数进行区分。所有其他参数通过一个数据块进行传递。该数据块的地址通过寄存器r1传递给中断处理程序。软中断的处理结果放在r0中返回,也可以通过显式的返回值或传递数据块的指针带回程序的处理结果。即使未返回结果,也假定r0是被使用的。

用r0传递的可用Semihosting操作编号分配如下:

· 0x00-0x31 这些编号由ARM公司使用;

· 0x32-0xFF 这些编号由ARM公司保留,以备将来使用;

· 0x100-0x1FF 这些编号保留给用户应用程序。

注意

虽然这些编号ARM公司不使用,用户可以使用这些编号编写自己的SWI操作,但建议使用其他 SWI 编号,而不要使用Semihosting SWI 编号和这些Semihosting的预留操作类型编号。

· 0x200-0xFFFFFFFF这些编号未定义。当前未使用并且不推荐使用这些编号。

在以下部分中,操作名称之后的括号中的编号是调用Semihosting操作时放入r0的值。例如,SYS_OPEN(0x01)。

如果从汇编语言代码中调用SWI,最好使用semihost.h中定义的操作名称。可以用 EQU 伪操作定义操作名称。例如:

SYS_OPEN EQU 0x01

SYS_CLOSE EQU 0x02

3.Semihosting需求函数

Semihosting需要的函数列表如表13.1所示。如果使用默认的Semihosting功能,用户不需要编写任何其他代码。也可以重新实现部分的输入/输出函数,使这些函数和标准Semihosting混合使用。

表13.1 Semihosting函数列表

函 数 名 称

描 述

SYS_OPEN (0x01)

打开文件

SYS_CLOSE(0x02)

关闭使用SYS_OPEN打开的文件

SYS_WRITEC (0x03)

向控制台输出字符

SYS_WRITE0 (0x04)

将空终止的字符串写入控制台

SYS_WRITE (0x05)

写入主机上的文件

续表

函 数 名 称

描 述

SYS_READ (0x06)

将文件内容读取到缓存器

SYS_READC (0x07)

从控制台读取字节

SYS_ISERROR (0x08)

确定返回代码是否错误

SYS_ISTTY (0x09)

检查文件是否连接到交互设备

SYS_SEEK (0x0A)

搜索到文件中的某个位置

SYS_FLEN (0x0C)

返回文件的长度

SYS_TMPNAM (0x0D)

返回文件的临时名称

SYS_REMOVE (0x0E)

删除主机上的文件

SYS_RENAME (0x0F)

重命名主机上的文件

SYS_CLOCK (0x10)

执行开始后的厘秒数

SYS_TIME (0x11)

1970 年 1 月 1 日到现在的秒数

SYS_SYSTEM (0x12)

将命令传递给主机命令行解释程序

SYS_ERRNO (0x13)

获得 C 库 errno 变量的值

SYS_GET_CMDLINE (0x15)

获得用于调用可执行程序的命令行

SYS_HEAPINFO (0x16)

获得系统堆参数

SYS_ELAPSED (0x30)

获得自执行开始的目标滴答声数目

SYS_TICKFREQ (0x31)

确定滴答声的频率

13.2.2 C 库结构

从概念上来讲,C库函数可被化分成两类,一类为ISO C语言的规范部分,该部分的主要功能是向用户提供一个调用接口;另一类为ISO C语言规范提供支持。图13.2显示了这两类函数在C库中的结构。

图13.2 C库的函数结构

对部分ISO C功能的支持是由主机调试环境在支持函数的设备驱动程序级别提供的。

例如,RVCT C库通过写入调试器控制台窗口来实现ISO C printf()系列函数。通过调用__sys_write()来提供该功能。这是一个执行半主机SWI的支持函数,使字符串被写入到控制台。

13.2.3 默认存储器映射

对于没有描述存储器映射的映像(Image),RVCT根据默认存储器映射放置代码和数据。默认的存储器映射如图13.3所示。

图13.3 默认存储器映射

结合图13.3,可以看出默认的存储器映射使用以下规则:

· 链接映像,在地址0x8000加载并运行。首先放置所有的RO(只读)段,其次是RW(读写)段,然后是ZI(零初始化)段。

· 堆(Heap)直接从ZI段的顶端地址算起,因此,其准确位置在链接时决定。

· 栈(Stack)的起始地址在应用程序启动过程时由Semihosting操作提供。具体Semihosting操作设置的值由调试系统的不同而不同。

① RealView ARMulator ISS(RVISS)设置为配置文件peripherals.ami中设定的值。默认值是0x08000000。

② Multi-ICE将该地址设置为调试器内部变量top_of_memory的值。默认值是0x00080000。

13.2.4 链接程序放置规则

链接程序遵守一组规则,以决定代码和数据位于存储器中的什么位置,如图13.4所示。

链接程序放置遵循以下规则:

① 映像首先按属性组织:RO段在最低的存储器地址,其次是RW段,然后是ZI段。每一种属性中,代码在数据之前。

② 链接程序按名称的字母顺序放置输入段(Section)。输入段名称即汇编程序AREA伪操作定义的名称。

图13.4 链接程序放置规则

③ 在输入段中,独立对象的代码和数据,按照对象文件在链接程序命令行中被指定的顺序放置。

要精确放置代码和数据,ARM公司建议不要过分依靠这些规则。相反,必须使用分散加载机制来完全控制代码和数据的放置。请参阅下一章的调整映像存储器映射以适应目标系统硬件存储器的实际要求。

13.2.5 应用程序启动

多数嵌入式系统中,执行主任务前,执行初始化序列来设置系统。默认的RVCT初始化序列如图13.5所示。

图13.5 默认RVCT初始化序列

在进入用户代码(main())前,初始化序列可分成三个功能块:__main直接跳转到__scatterload;__scatterload负责建立运行时的映像存储器映射,而__rt_entry(运行时的入口)则负责初始化C库。

__scatterload执行代码和数据复制以及ZI数据的清零。对于ZI数据的清零和未改变的RW数据来说,这一步总是要做的。

__scatterload跳转到__rt_entry。它设置应用程序的栈和堆,初始化库函数及其静态数据,并调用任何全局声明的对象的构造函数(仅C++)。

然后__rt_entry跳转到应用程序入口main()。主应用程序结束执行时,__rt_entry将库关闭,然后把控制权交还给调试器。

RVCT中,函数main()有一个特殊含意。main()函数的存在强制链接程序链接到__main和__rt_entry中的初始化代码。没有main()函数,就不会链接到初始化进程,那么一些标准C库功能就不会得到支持。



[1] 在一些ARM的中文参考文献中,将Semihosting译为半主机。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭