控制编译器和链接1.docx
- 文档编号:26057794
- 上传时间:2023-06-17
- 格式:DOCX
- 页数:13
- 大小:60.54KB
控制编译器和链接1.docx
《控制编译器和链接1.docx》由会员分享,可在线阅读,更多相关《控制编译器和链接1.docx(13页珍藏版)》请在冰豆网上搜索。
控制编译器和链接1
控制编译器和链接
-控制编译器和链接
将现有的C代码移植到KeilC语言
不论你将现有的代码从另一个处理器移植到8051或你正在使用Keil开发工具修改8051的底层代码,你已经作出了明智的选择使用了8051Keil工具。
作为整个这本书已被讨论的8051系列微控制器和Keil的工具包是一个强大的组合,这将有助于你完成几乎任何任务。
现有的C代码移植到KeilC语言是一件容易的事,因为C51编译器完全实现了ANSI标准的C语言。
所以只要你没有写代码,使用不符合ANSI标准的语言扩展,你不会有任何麻烦,C51编译器可以编译你的代码。
当你开始你的代码转换,有几件事情你要牢记在每个阶段的过程。
8051移植代码时,通过检查代码,决定应作出什么样的变量声明和代码结构的变化,以帮助它更好地运行8051。
在此之后,检查软件的设计,并确保正确,它会转化为8051的架构。
如果您的项目曾主持另一个微控制器,你要密切关注第三章优化你的代码提示。
所有这些技巧适用于这里。
主要是要牢记的是,一个8位机上运行你的代码,你应该尽量保持8位边界内的所有变量和其他数据元素。
任何变量,只是执行功能的一个标志(即:
他们只有两个值)应该被宣布为位变量。
同样,如果你有一个变量中,经常访问在一个时间单位,要考虑声明为bdata。
有一件事情上要保持密切关注,那就是移植的8051代码指针用法的改变。
这又是在第三章讨论,但值得重复。
你可以节省一定的内存空间,如果你可以限制你的指针和指定的C51编译器使用C语言的扩展,它提供了相当数量的代码大小和执行时间。
这将允许编译器来编写更好的源代码,使用这些指针。
一旦你进行了初步过渡,再优化你的代码为8位化,你需要以examinethe结构的软件,并决定这部分代码将中断例程将被称为'主'。
一旦你建立的职能,将响应你应该运行的代码,通过编译器和链接,并让连接器从多个中断调用树调用的函数的处理器中断产生警告。
这些警告会立即导致那些潜在的关键代码段的功能。
这些都是可能不止一次被称为一次通过递归或中断机制的部分。
因为是8051的架构,C51编译器不会自动生成的代码来处理递归或多个从不同呼叫树的功能。
如果你正在处理的处理器,有完整的堆栈功能,像一个80x86的处理器,然后调用框架可以建成并为每个函数调用入栈。
但是,你不处理一个80x86的处理器,你应该不知道现在的8051内部堆栈空间是不是大到足以支持超过几个功能调用帧。
C51编译器必须使用递归调用的功能,让其具有“折返”的能力。
在这种情况下,C51编译器将模拟默认的内存区域。
这是一个时间和内存耗时的过程,从而“折返”键字应谨慎使用。
不是所有链接的警告都要恐慌。
例如,你可能有一个被称为由定时器0和外部中断1中断服务,但这些中断已经设置为相同的优先级的功能。
由于他们是相同的优先级,并且是唯一可以调用这个函数的调用树,它是非常安全的,认为没有问题,因为一个给定优先级的中断不能中断像一个ISR的优先。
摆脱链接警告的方式之一是引用删除调用的树木。
这可以让你有编译器生成,不需要折返栈。
这个问题将在本章后面详细讨论。
一旦你已经适应了你的8位化代码,并建立您的中断功能和折返功能,你将要考虑以何种方式访问外部存储器。
许多C程序员,他们希望在任何物理地址访问和使用指针的位置上,然后执行所有操作设置一个指针。
此方法将仍然工作在C51,但你可以看看你的代码使用的CBYTE,CWORD,XBYTE,XWORD,DBYTE,双字节,PBYTE,PWORD,absacc.h看上去更整洁。
从而使你的代码更具可读性(为char,int和long)的外部存储器。
此外,如果您的硬件架构的变化很麻烦,访问设备不再是简单的事情了,你可以重写来实现硬件的新内存访问计划。
如果你从另一个编译器包,如阿基米德或阿沃塞特移植的代码,你必须改变他们的关键字,以正确的Keil它们。
由于其他包不支持的所有功能,支持Keil编译器(如bdata变量,折返功能,规范的功能寄存器银行),你要检查你的代码,以确保所有的事情可以得到Keil支持。
在我们的代码和XDATA段存在错误时,不得不改变代码的某些部分,把速度放慢,因为额外的速度上涨,表示该主机的硬件比原先认为的要慢一点。
换句话说,如果你使用的C51包好,就能有更高的效率。
汇编代码移植到KEIL系统
从一个汇编组装项目的Keil汇编,确实有太多你要关心的问题。
主要提醒你的是改变段的名字是与Keil的命名约定兼容。
这将使你的代码接口与KeilC语言更容易处理好。
当然,如果你的项目是一个C语言和汇编的组合,那么你将要变化段名。
一些接口的C代码和汇编代码的指导,请参阅第三章。
在重组与Keil汇编代码,我从未遇到过这么多问题。
事实上,曾经在改变代码时,被重组的唯一定义寄存器PCON里的老阿沃塞特汇编改变方案删除了一行。
原因是,这么老的阿沃塞特汇编,它是被添加到8051系列的省电模式里,因此它必须有一个明确定义的PCONSFR地址。
使用“using”关键字
英特尔8051系列控制器有四个组,每个组有八个寄存器。
这三两个字节驻留在数据存储区的底部。
每个寄存器的组被称为其数量。
默认情况下,8051设置使用登记结算RS0和RS1位在PSWSFR的第零寄存器组,但是,在任何时候,任何一个四个寄存器的组,该软件能够更改默认组的控制器。
部分第三章的讨论围绕在中断功能的寄存器组和它们的用法。
这一章表明你的编译没有任何额外的关键字,其中指定的寄存器默认组的零中断功能相比,它的中断功能的编译器产生的汇编代码与在第二个版本的功能寄存器里的不同。
本节将讨论如何处理这种情况。
第三章指出,有32个处理器周期要保存每个中断调用中断例程分配一个特定的寄存器组。
要利用这一优势,Keil公司建议您分配一个单独的寄存器组,每个“中断级”在你的程序的中断优先级别。
例如,主回路和初始化代码有没有具体的任务,从而将编制使用零号寄存器组。
每个响应优先零中断的中断服务程序将编码使用寄存器的组一,和每一个中断程序响应一个中断优先将编码使用寄存器组二。
任何由ISRS调用的函数必须作为主叫方使用相同的寄存器组或必须编译使用编译器指令(NOAREGS),确保当前寄存器组可用。
下面的代码片断说明了这种选择寄存器为ISRS组的基本设计方法。
清单0-1
voidmain(void){
IP=0x11;//serialintrandextintr0
//havehighpriority
IE=0x97;//enableserial,external1,
//timer0andexternal0
//interrupts
Init_system();
...
for(;;){
PCON=0x81;//enteridlemode
}
}
voidserial_intr(void)interrupt4using2{
//serialinterrupthashigh
//priorityandthuswilluse
//registerbank2
if(_testbit_(RI)){
recv_fsa();
}
if(_testbit_(TI)){
xmit_fsa();
}
}
voidrecv_fsa(void)using2{//recv_fsamustusethesame
//registerbankasserial_intr
//sinceserial_intrcallsit
...
}
voidxmit_fsa(void)using2{//xmit_fsamustusethesame
//registerbankasserial_intr
//sinceserial_intrcallsit
...
}
voidintr_0(void)interrupt0using2{
//highpriorityinterrupt-use
//registerbank2
handle_io();
...
}
voidhandle_io(void)using2{//calledbyanISRusingRB2
//mustuseRB2also
...
}
voidtimer_0(void)interrupt1using1{
//lowpriorityinterrupt–use
//registerbank1
...
}
voidintr_1(void)interrupt2using1{
//lowpriorityinterrupt-use
//registerbank1
...
}
注意寄存器组指定每个ISR以及由ISR调用的函数。
由主程序调用任何函数将不再需要一个寄存器组的定义,因为C51的会自动假设他们是使用第零寄存器组。
这个简单的项目调用树如下所示。
单独的路径不交叉。
图0-1简单的调用树
大多数实时系统没有一个如上所示的一个简单的调用树。
通常有至少除了主回路中由一个以上的中断例程调用的函数的实用型的组合。
举个例子来说,如下面的代码清单。
清单0-2
voidmain(void){
IP=0x11;//serialintrandextintr0
//havehighpriority
IE=0x97;//enableserial,external1,
//timer0andexternal0
//interrupts
init_system();
...
display();//writeamessagetoadisplay
//panel
for(;;){
PCON=0x81;//enteridlemode
}
}
voidserial_intr(void)interrupt4using2{
//serialinterrupthashigh
//priorityandthuswilluse
//registerbank2
if(_testbit_(RI)){
recv_fsa();
}
if(_testbit_(TI)){
xmit_fsa();
}
}
voidrecv_fsa(void)using2{//recv_fsamustusethesame
//registerbankasserial_intr
//sinceserial_intrcallsit
...
display();//writeFSAstatustothe
//displaypanel
}
voidxmit_fsa(void)using2{//xmit_fsamustusethesame
//registerbankasserial_intr
//sinceserial_intrcallsit
...
}
voidintr_0(void)interrupt0using2{
//highpriorityinterrupt-use
//registerbank2
handle_io();
...
}
voidhandle_io(void)using2{//calledbyanISRusingRB2
//mustuseRB2also
...
}
voidtimer_0(void)interrupt1using1{
//lowpriorityinterrupt-use
//registerbank1
display();//writeatimeoutmessageto
//thedisplaypanel
}
voidintr_1(void)interrupt2using1{
//lowpriorityinterrupt-use
//registerbank1
...
}
voiddisplay(void){
...
}
8051有“显示”功能。
这意味着,程序有可能被“显示”功能中断,这将反过来调用“显示”功能中断。
请记住,每个ISR已指定其自己的寄存器组,从而不保存任何数据在当前寄存器银行。
默认情况下,编译器将编写的“显示”功能,使用绝对寄存器寻址寄存器组。
这意味着,不是产生R0..R7的类型引用的寄存器,反而会产生绝对地址。
在“显示”指定的函数的情况下,编译器将产生00..07,为寄存器地址,因为它假定第零寄存器组是被使用的寄存器组。
如果只由一个中断级调用显示功能,默认寄存器组可以响应这个函数而且问题将得到解决。
现在,最重要的事情是告诉编译器如何处理“显示”寄存器,解决的办法是让“显示”寄存器编译,以便它使用当前寄存器组。
显示的代码将被使用,而不是被R0..R7类型的绝对地址寄存器引用。
“显示”功能将保持不变,它允许在文件中的任何其余C51默认的功能编译使用。
#pragmaNOAREGS
voiddisplay(void){
...
}
#pragmaAREGS
图0-2多个中断级调用树
“显示”功能的另一个问题仍然存在。
假设“显示”功能使用几个局部变量,以执行其工作。
而每次调用显示函数使用的局部变量没有在调用树完全相同的内存位置调用它,这是因为在一个80x86或680x0的类型处理器的堆栈没有意义。
但是当一个函数必须调用递归或在使用折返的方式,那么在局部变量的数据难免会出现问题。
“显示”的执行可以改变所有的局部变量的值。
在“显示”功能的默认内存段分配已经被损坏的情况下,我们无法预测任何局部变量“显示”功能的结果。
一旦这个关键字被应用到“显示”功能,它会被编译函数调用栈。
因此,每次调用“显示”将有其自己的一套地址为本地变量。
纠正重入问题后,“显示”功能将变成这个样子。
#pragmaNOAREGS
voiddisplay(void)reentrant{
...
}
#pragmaAREGS
你应该知道声明函数是可重入的,这增加了大量的开销,因此应谨慎使用。
此外,重入堆栈的空间量,只能通过计算最坏情况下的数量,重入的函数调用,可以在一段时间内预测。
你在默认的内存空间有足够的内存来处理折返栈。
该协议栈的设计开始在默认的内存段的顶部(例如,在XDATA的段0FFFFH)和分配的内存段的底部。
一旦你已经编译和链接你的应用程序,你应该仔细检查M51连接器产生的文件,以保证有足够的空间为重入堆栈。
控制链接器的叠加过程
在其中有多个调用树的功能称为应用程序,但由于C51缺乏一个真正的栈,所以这是不能实现的。
就这种类型的情况进行了以上讨论。
可以考虑在“显示”功能里调用,如清单0-3所示的情况。
清单0-3
voidmain(void){
IP=0x00;//allintshavethesamepriority
init_system();
...
display(0);
IE=0x8A;//enabletimer0,andexternal
//0interrupts
for(;;){
PCON=0x81;//enteridlemode
}
}
voidtimer_0(void)interrupt1using1{
//lowpriorityinterrupt-use
//registerbank1
...
display
(1);
}
voidintr_1(void)interrupt2using1{
//lowpriorityinterrupt-use
//registerbank1
...
display
(2);
}
voiddisplay(unsignedcharx){
...
}
根据连接器,此代码包含在其调用树的冲突,因为“显示”功能,可以从主要的调用树以及定时器0的ISR和外部中断1的ISR调用。
由于冲突,链接器将发出以下警告。
***WARNING15:
MULTIPLECALLTOSEGMENT
SEGMENT:
?
PR?
_DISPLAY?
INTEXAM
CALLER1:
?
PR?
TIMER_0?
INTEXAM
CALLER2:
?
PR?
INTR_1?
INTEXAM
***WARNING15:
MULTIPLECALLTOSEGMENT
SEGMENT:
?
PR?
_DISPLAY?
INTEXAM
CALLER1:
?
PR?
INTR_1?
INTEXAM
CALLER2:
?
C_C51STARTUP
链接器警告你ISR这也可以称之为“显示”,“显示”的调用可能被打断。
第一个警告,告诉我们,“显示”呼叫中存在定时器0调用树和中断调用树,第二次警告,告诉我们,也有中断1调用树和主要的调用树之间的冲突。
还要注意,同样的调用树之间存在冲突,主要定时器0。
仔细检查代码结构表明,“折返”通话“显示”是没有问题的。
在软件中已分配的定时器0和外部中断1中断的优先级相同,因此不能同时调用“显示”功能。
因此,第一个警告没有软件设计问题。
第二个警告告诉我们主程序调用“显示”功能可能会被中断的调用打断。
再者,由于主程序调用“显示”功能时中断未使能所以这不是一个问题,故对“显示”的调用不会被中断打断。
这两个警告已被证明是没有根据的。
这并不是说,链接不正常。
事实上,连接器已完成其工作,而是在给予这些警告。
它的工作除了正常的链接功能,还有寻找一些可能折返的情况,并发出警告,若函数没有被声明则可重入处理。
现在,它已确定的警告是没有意义的代码,他们应该怎样做呢?
第一个倾向是简单地忽略这些代码,但这会影响连接器解决模块之间的分配地址重定位的对象的引用的方式。
因此,连接器仍然可以输出一个全功能的可执行文件,它会调用充分的内存空间,但链接器将无法正确执行叠加分析。
事实上,会出现连接器连接的程序将无法正常工作的情况。
故按常理说,连接器的警告不应该被忽视。
在这种情况下,有两种方式可以消除警告。
第一种方式是告诉链接器不执行任何类型的叠加分析。
这将导致代码使用大量的不必要数据空间,但却最容易实现。
第二种方式是,迫使它忽略某些问题,从“显示”功能的调用树帮助链接的叠加分析。
一旦你已经告诉它删除,代码将没有任何警告,并会进行妥善叠加分析联系起来。
第二种方法显然是更可取的,而你可以选择采取第一种方法,如果你只有较短时间但有较多的内存。
上面的代码通过调用下面的命令进行链接。
L51example.obj
第一种方法,消除了多个调用警告,你应该简单地告诉链接器不进行叠加分析。
实现这个要求,你在链接选项对话框清除“启用变量叠加”复选框。
第二种方法是比较复杂,但其实施的回报较高。
第二个方法需要你从两个“显示”函数中调用删除三个调用引用。
这项工作将在命令行上使用覆盖选项。
切记“显示”功能只能被被“main”、“timer_0”和“inter_1”函数调用,你必须从这些功能中选择两个删除。
在这种情况下,在调用树的功能中试图避开调用的问题,最大程度的调用该函数。
在这种情况下,让我们假设,很少发生中断1,定时器0是系统时钟,从而调用将更频繁地发生。
鉴于这种情况,你会删除“main”和“intr_1”对“显示”的调用,但在“timer_0”中调用。
在连接器配置对话框中的“附加”选项卡的“叠加”命令行的部分应进入。
L51example.obj覆盖(主要_display,intr_1〜_display)
当你第一次运行通过链接,你的大部分项目最初将有多个调用警告。
适当注意上述引用删除调用和正确使用折返关键字将消除警告。
记住,你可能不会消除所有的连接器警告,这是一个反复的过程,为了确保消除所有链接的警告,可以采取上述几个步骤。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 控制 编译器 链接