第七章 单片机混合编程.docx
《第七章 单片机混合编程.docx》由会员分享,可在线阅读,更多相关《第七章 单片机混合编程.docx(11页珍藏版)》请在冰豆网上搜索。
第七章单片机混合编程
第七章单片机与C语言的混合编程
一、混合编程的原则
对于一个程序,整体程序框架,大部分的程序用c语言编写,有些要求严格定时等要求的用汇编语言编写,有时可以达到较好的效果。
C51函数声明
转换后的函数名
说明
voidfunc1(void)
FUNC1
无参数传递,函数名不改变
typefunc2(args)
_FUNC2
有参数传递,前加“_”,通过寄存器传递
typefunc3(args)reentrant
-?
FUNC3
重入函数,有参数传递,前加“_?
”,通过堆栈传递(在调用时也用寄存器传递,在函数中再转存入堆栈中)
混合编程最关键的问题是参数传递,也就是在连接时,不同语言编写的程序,参数是如何传递的。
已经知道,c51有一套严格的参数传递规定,一般来说用汇编编写的程序变量的传递参数所使用的寄存器是无规律的,汇编语言较随便。
因此混合编程中,汇编语言服从c51的规定。
按照C51的参数传递标准可让你的程序有很好的可读性,并有利于维护。
编写出来的函数很容易和C编写的函数进行连接,如果你用汇编编写的函数和C编译器编译出来的代码风格一样的话,连接器将能够对你的数据段进行覆盖、分析。
二、编译、连接的规则
1、C51中函数名的转换规则
2、几个概念:
1)编译
将源程序翻译成单片机可以执行的目标代码,产生一个目标文件(.OBJ)。
源文件编译时只能得到相对地址。
列表文件是编译后生成的含源程序、目标代码和错误信息的可打印文件。
2)段的概念
段是程序存储器或数据存储器的单位。
分为绝对段和可重新定位段。
绝对段是不能重新定位的,如主程序的开始,必然在0000H,中断矢量入口也不能变。
绝对段没有段名。
可以重新定位的段,例如函数,库函数等。
要有段名、类型及属性。
可以重新定位的段位置由连接时确定。
3)模块
模块是包含一个或多个段的文件,由编程者命名。
4)连接与定位
将各模块中所以具有相同段名及类型的段连接起来,连接以后各段都分配了绝对地址。
生成一个完整的程序。
例如在几个函数中都调用了sin(x),连接时就会合并到一起,在整个程序中计算sin(x)的段只有1个。
产生一个可以固化到单片机中的文件(.HEX)。
3.文件处理过程:
编辑:
用编辑器分别编辑C源代码和汇编源代码。
编译:
用户编写的c语言源程序经过c51编译器编译后,生成浮动地址的目标代码文件。
这种浮动地址的目标代码是不能直接装入805l单片机的EPROM中运行的,必须经过连接定位器L51的连接和定位,生成具有绝对地址的目标代码,才能在8051单片机中运行。
连接:
L51能将各个模块中具有相同名字的再定位部分段组合到—个单一的再定位段中,同时产生—个段表(MAP)。
段表中包含了每个段的类型、基地址、长度和段名。
在进行段组合时要求所有具有相同名字的部分段都有相同的存储器类型(CODE、DATA、IDATA、XDATA或BIT),如果类型不同则会发生错误。
组合段的长度不能超过存储器的实际长度。
L51能够进行多程序模块的连接,它具有静态覆盖功能,可使8051单片机中有限的存储器资源得到允分利用。
在连接定位的过程中能自动加入运行库中必要的库函数。
L51连接定位器生成绝对目标输出文件。
L5l采用静态覆盖技术来操作数据存储器,使8051系列单片机中有限的内部RAM得到充分的利用。
对于由c51编译器或A51汇编器生成的目标模块中没有相互调用的参数和局部变量,将在存储器中覆盖,从而可产牛非常紧凑的数据区。
转换成*.HEX文件:
通过符号转换器0HS51转换成Inte1HEX文件,该文件可以固化到EPROM
4、C51编译器的段名规则
1)段名
段名包含源程序文件名、存储类型和名字,名字就相当与C程序中的函数名。
注意:
所有的段名都被转换为大写字符保存,因此混合编程时,用汇编语言编写段名要用大写。
存储类型用前缀表示:
段名前缀
存储区类型
说明
?
PR?
code
可执行程序段
?
CO?
code
程序存储器中的常数数据段
?
BI?
bit
内部数据存储区的位类型数据段
?
BA?
bdata
内部数据存储区的可位寻址的数据段
?
DT?
data
内部数据存储区的数据段
?
FD?
far
外部数据存储区的far型数据段
?
FC?
constfar
程序存储器中的far型常数数据段
?
ID?
idata
内部数据存储区的间接寻址的数据段
?
PD?
pdata
外部数据存储区的分页数据段
?
XD?
xdata
外部数据存储区的一般数据段
?
C?
库文件
C源程序编译后,段名的形式如下:
?
PR?
XIAOYAN?
SHUIL
?
C?
LIB_CODE
其中:
XIAOYAN?
为名称,SHUIL为文件名,?
PR?
为段名前缀,含义如下:
5、设置变量地址
有时候我们希望把变量存储在指定的地点,特别是在主控制器初始化SRAM之后,从8051系统才开始工作的情况,在这种情况下两个系统必须在存储器分配上达成一致。
如果你不想在编译时才给变量分配地址,KeilC可以让你指定变量的存储地址。
例如你想定义一个整型变量并把它初始化为0x4050,用C是不能够把变量指定在某个地址的,另外你也不能指定位变量的地址,但是对于不需要初始化的变量你可以使用关键字_at_来指定地址。
你的变量将分配在DATA段中下面是一个指定地址的例子
unsignedchardatabyteval_at_0x32;
关键字_at_的另一个功能是能通过给I/O器件指定变量名,为你的输入输出器件指定变量名。
例如你在XDATA段的地址0x4500处有一个输入寄存器,你可以通过下面的代码为它指定变量名:
unsignedcharxdatainpreg_at_0x4500;
以后在读该输入寄存器的时候只要使用变量名inpreg就可以了。
三、C51项目中汇编语言文件的格式
1、声明部分
1)模块名定义格式(在文件开始)
NAME模块名
用关键字NAME说明模块名
2)子程序代码段声明格式(在模块名定义之后)如:
?
PR?
[_|_?
]函数名1(子程序名1)?
模块名SEGMENTCODE
?
PR?
[_|_?
]函数名2(子程序名2)?
模块名SEGMENTCODE
:
?
PR?
[_|_?
]函数名n(子程序名n)?
模块名SEGMENTCODE
主要说明符号类型,分配空间。
3)子程序被调用属性声明格式(紧接前面)如:
PUBLIC[_|_?
]函数名1(子程序名1)
PUBLIC[_|_?
]函数名2(子程序名2)
:
PUBLIC[_|_?
]函数名n(子程序名n)
用PUBLIC说明该模块可以与其他子程序、模块共享。
调用它的模块必须在模块开头包含外部函数说明EXTERN
3、用汇编语言编程时段声明格式
设汇编文件名字为Delay.asm
NAMEDelay;给该模块一个名字
PUBLIC_DELAY;表示有参数传递
?
PR?
_DELAY?
DelaySEGMENTCODE;代码段声明
RSEG?
PR?
_DELAY?
Delay;RSEG表示可重定位
_DELAY:
(程序段)
RET
上面是用汇编编程,用c调用时汇编程序段的一个框架模式。
汇编文件的格式化是很简单的,给存放功能函数的段一个段名,因为是在代码区内,所以段名的开头为PR,在编程时,“?
PR?
”是可以省略的。
对于传递参数的功能函数,必须符合参数的传递规则。
下面看一个用c调用汇编的例子。
例:
(混合编程2)
dpjc.c(文件名)
#defineucharunsignedchar
ucharadd1(ucharx,uchary);
voidmain(void)
{
ucharx,y,z;
x=0x10;
y=0x20;
z=add1(x,y);
}
汇编语言源程序:
(注意汇编的格式,文件名用字母)
huib.asm(文件名)
PUBLIC_ADD1
_ADD1?
HUIBSEGMENTCODE
RSEG_ADD1?
HUIB
_ADD1:
MOVA,R7
ADDA,R5
MOVR7,A
RET
END
上面分别是两个文件,按下列步骤进行编辑、编译和连接:
1)建立工程文件,然后建立C源文件dpjc.c和汇编源文件huibian.a51,建立方法相同。
2、编译:
将光标放在要编译的文件(如huibian.a51)右击,弹出浮动菜单。
将光标移到“Translate”上单击(或用工具栏按钮)对该程序进行编译。
将两个文件全部编译无误后,方可进行连接。
3、连接:
单击“Buildtarget”工具栏按钮即可连接。
4、运行:
运行时要打开汇编窗口“DisassemblyWindow”和”Watch&CallStackWindow”窗口,观察汇编语言中的变量是如何传递的,注意变量是如何变化的。
编译、连接以后产生的汇编代码如下:
3:
voidmain(void)
4:
{
5:
ucharx,y,z;
6:
x=0x10;
C:
0x000F7F10MOVR7,#0x10
7:
y=0x20;
C:
0x00117D20MOVR5,#0x20
8:
z=add1(x,y);
C:
0x0013120019LCALLADD1(C:
0019)
C:
0x00168F08MOV0x08,R7
9:
}
C:
0x001822RET
5:
_ADD1:
MOVA,R7
C:
0x0019EFMOVA,R7
6:
ADDA,R5
C:
0x001A2DADDA,R5
7:
MOVR7,A
C:
0x001BFFMOVR7,A
8:
RET
注意数据传递过程。
4、用空函数编译查看汇编格式与数据传递
将add1函数编写一个空函数:
#defineucharunsignedchar
ucharadd1(uchara,ucharb)
{
}
voidmain(void)
{
ucharx,y,z;
x=0x10;
y=0x20;
z=add1(x,y);
}
编译时,文件选项要选择“GenerateAssemblerSRCFile”和“AssemblerSRCFile”,产生一个后缀为.SRC的文件。
方法:
在源程序名上右击鼠标,出现浮动菜单,将鼠标移到“optionsforFile文件名.c”,左击鼠标,在弹出窗口中选择即可,注意原来是灰色的,选择使其变为黑色。
打开hunhebc3.SRC文件如下:
;.\hunhebc3.SRCgeneratedfrom:
hunhebc3.c
;COMPILERINVOKEDBY:
;E:
\keil\C51\BIN\C51.EXEhunhebc3.cBROWSEDEBUGOBJECTEXTENDSRC(.\hunhebc3.SRC)
NAMEHUNHEBC3
?
PR?
_add1?
HUNHEBC3SEGMENTCODE
?
DT?
_add1?
HUNHEBC3SEGMENTDATAOVERLAYABLE
?
PR?
main?
HUNHEBC3SEGMENTCODE
?
DT?
main?
HUNHEBC3SEGMENTDATAOVERLAYABLE
EXTRNCODE(?
C_STARTUP)
P