汇编语言混合编程对Keil的设置.docx
《汇编语言混合编程对Keil的设置.docx》由会员分享,可在线阅读,更多相关《汇编语言混合编程对Keil的设置.docx(8页珍藏版)》请在冰豆网上搜索。
汇编语言混合编程对Keil的设置
在keilC语言与汇编语言的混合编程中曾经遇到过的一些问题,写下来留作以后参考。
(本文写于11.04.10,在8.18日加入了第4点的内容)
1、C语言中加入汇编语言模块的方法:
例子:
voidfunc()
{
C语言代码……
#pragmaasm
MOVR6,#23
DELAY2:
MOVR7,#191
DELAY1:
DJNZR7,DELAY1
DJNZR6,DELAY2
RET
#pragmaendasm
C语言代码……
}
其中红色为C语言部分,绿色为嵌入的汇编语言部分。
汇编部分需要用#pragmaasm和#pragmaendasm包起来
2、Keil提示“asm/endasm”出错的解决方法
如果只是像1中那样直接加入汇编代码的话,编译将会报错,错误如下:
compilingsendata.c...
sendata.c(81):
errorC272:
'asm/endasm'requiressrc-controltobeactive
sendata.c(87):
errorC272:
'asm/endasm'requiressrc-controltobeactive
Targetnotcreated
解决方法如下:
首先右键单击包含有汇编部分的c语言文件名,然后在如上图所示的菜单中选择带有红色方框的选项
在弹出的对话框中,将上图中红色方框选中的两项打上勾(默认的情况下,前面的勾是灰色的,要让这两项前的勾变为黑色的),点击确定。
3、?
C_START等相关警告的处理
按照2中的方法处理完之后,再编译不会出现错误信息了,但是会出现如下的警告信息:
linking...
***WARNINGL1:
UNRESOLVEDEXTERNALSYMBOL
SYMBOL:
?
C_START
MODULE:
STARTUP.obj(?
C_STARTUP)
***WARNINGL2:
REFERENCEMADETOUNRESOLVEDEXTERNAL
SYMBOL:
?
C_START
MODULE:
STARTUP.obj(?
C_STARTUP)
ADDRESS:
000DH
处理方法如下:
在如上图所示的“SourceGroup1”上点右键,在菜单中选择“AddFilestoGroup'SourceGroup1'”
找到你的KEIL安装目录,选择其中的“C51”目录下的“LIB”目录下的“C51S.LIB”文件,点击Add,然后Close即可。
注意,上图所示的文件选择框进入LIB目录下后,默认只显示.c文件,需要在“文件类型”中选择“Libraryfile(*.lib)”,即可显示LIB文件了。
添加C51S.LIB到工程后,再次编译,警告信息消失。
linking...
ProgramSize:
data=9.0xdata=0code=28
creatinghexfilefrom"sendata"...
"sendata"-0Error(s),0Warning(s).
4、寄存器冲突问题的解决
汇编程序块中常常会使用到51的通用寄存器,比如R0-R7。
这种情况下可能会和C语言程序中已经使用到的R0-R7产生冲突,以至于产生一些非常隐蔽和古怪的错误。
虽然有人说KEIL可以自动分配寄存器组使之不产生冲突。
但是在我这里具体测试时还是有冲突的(或者是我的KEIL设置有问题?
),下面是测试小程序:
/*************************************************************
*测试程序
*************************************************************/
#include
#include
typedefunsignedcharuint8;
uint8buf[16]=
{
0x55
};
uint8i=0;
/************************************************************
*初始化单片机相关寄存器
***********************************************************/
voidUartInit()
{
SCON=0x50;
TMOD|=0x21;
PCON|=0x80;
TH1=0xE8;
TL1=0xE8;
IE|=0x90;
TR1=1;
}
/**************************************************
*延时
***************************************************/
voiddelay()
{
#pragmaasm
MOVR6,#19
DELAY2:
MOVR7,#18
DELAY1:
DJNZR7,DELAY1
DJNZR6,DELAY2
RET
#pragmaendasm
}
/**********************************************
*向COM1发送一个字符
**********************************************/
voidSendChar(uint8byteToSend)
{
SBUF=byteToSend;
while(!
TI);
TI=0;
}
/************************************************************
*读取一个字节
***********************************************************/
uint8read_byte()
{
uint8recvdata=8;
delay();//延时
returnrecvdata;
}
/**************************************************
*主程序
***************************************************/
intmain()
{
UartInit();//串口初始化
while
(1)
{
buf[i++]=read_byte();
}
}
/**************************************************
*串口中断处理
***************************************************/
voidchuankou()interrupt4
{
if(RI)
for(i=0;i{
SendChar(buf[i]);
}
RI=0;
}
程序本身非常简单,一目了然。
main函数的作用就是不断把read_byte()的返回值读入buf中,再待有串口中断时,将buf中的内容输出到串口。
read_byte()函数也已经做了简化,返回值固定为8。
将这个程序在KEIL中编译,下载运行。
奇怪的情况出现了,buf中的内容输出到串口总是0(输出理所当然应该是8)。
把delay();这一句注释掉后,程序就输出正常了。
delay();不过起到了一个延时作用而已,怎么可能改变到函数返回值呢?
看看read_byte()函数生成的汇编程序
USING0
MOVR7,#08H
ACALLdelay
RET
才晓得,原来read_byte()函数将返回值放到R7中,然后调用delay函数,再返回。
后面的程序调用read_byte()返回值时,直接从R7中取数。
但是“ACALLdelay”时,已经存放了#08H的R7,在延时中被递减到了0,这也是为什么buf中存放的内容都是0的缘故。
知道了原因就好办一些了,由于本人是菜鸟,单片机水平很有限,目前只想到了如下4种解决方法:
(1)避开C语言部分已经使用了的Rn
编汇编模块时,看看C语言部分生成的汇编程序,把那些C语言已经使用到的,且可能对汇编部分构成冲突的Rn避开就好了。
比如上面的延时程序中,把R6、R7换成R3、R4,程序就正常了。
(2)用USINGX+ARX的方式调用其他组Rn寄存器
51单片机有4组R0-R7的寄存器,据说main中一般使用的都是第0组。
那么在汇编部分中使用其他的组就可以了,delay函数的内容可以改成如下这样:
voiddelay()
{
#pragmaasm
USING2
MOVAR6,#19
DELAY2:
MOVAR7,#18
DELAY1:
DJNZAR7,DELAY1
DJNZAR6,DELAY2
RET
#pragmaendasm
}
但是注意,如果这样用的话,delay延时的长度可能需要重新计算。
因为AR6和R6有所不同,以下是在网上找到的两段英文描述:
R0–R7Theeight8bitgeneralpurpose8051registersinthecurrentlyactiveregisterbank.
AMaximumoffourregisterbanksareavailable.
AR0–AR7RepresenttheabsolutedataaddressesofR0throughR7inthecurrentregisterbank.
Theabsoluteaddressfortheseregisterschangesdependingontheregisterbankthatiscurrentlyselected.
ThesesymbolsareonlyavailablewhentheUSINGassemblerstatementisgiven.
RefertotheUSINGassemblerstatementformoreinformationonselectingtheregisterbank.
TheserepresentationsaresuppressedbytheNOAREGSdirectiveofftheCx51compiler.
看这意思,似乎R0–R7是寄存器,而AR0–AR7是地址,所以“MOVAR6,#19”和“MOVR6,#19”所花的时钟周期数是不同的。
我调一个采集数据的程序时,刚开始没有注意到这个问题,因此很奇怪为什么用AR6和R6,采集到的数据是不一样的,后来才反应过来它们延时不同。
(关于51单片机的各条指令的时钟周期数可以在XX上搜到很多,这里就不列出了)
(3)对汇编部分中使用到的寄存器采用入栈保护
简单的说就是在汇编部分使用Rn之前,将它们的内容塞进栈中存起来,延时循环结束之后再从栈中取出来