keil c与汇编联用 及注意事项

Keil C 与 汇编 联用 一些记录与注意事项
我们习惯于用在keil C去做51单片机编程或者用keil MDK去做一些简单 arm的编程。
但是在代码优化和实时性方面,C语言有些是没有办法可以实现的,只能用汇编,汇编比较枯燥指令限制比较多,特别是号称RISC的arm,那个arm指令让人感觉繁琐复杂。

我最近在番禺忙于工作,有做51,我写系统和高阶自然离不开c,但是我写单片机不能单单用C,C的效率不如汇编,毕竟,C最后还是翻译成汇编的。对于资源不多的单片机,这个是非常不好的。两者能否结合一起,发挥各自优势??! 答案是可以的。

在此分享一下51的keil C 嵌入 汇编的一些使用心得,当然,有部分是在网上搜索所得。有些是个人原创,如果用到此文章内容,请注明出处。

1、C语言中加入汇编语言模块的方法:

网上一些例子说的:"方法很简单,只需要“#Pragma asm”和"Pragma endasm" "
如下:
例子:
void func()
{

    C语言代码……
#pragma asm
         MOV R6,#23
DELAY2:    MOV R7,#191
DELAY1:    DJNZ R7,DELAY1
DJNZ R6,DELAY2
RET

#pragma endasm
    C语言代码……

其中红色为C语言部分,绿色为嵌入的汇编语言部分。汇编部分需要用#pragma asm#pragma endasm包起来
但是,你编译的时候,发现不对,eror!!接下来要这样做:


2、Keil提示“
asm/endasm”出错的解决方法

如果只是像1中那样直接加入汇编代码的话,编译将会报错,错误如下:
compiling sendata.c...
sendata.c(81): error C272: 'asm/endasm' requires src-control to be active
sendata.c(87): error C272: 'asm/endasm' requires src-control to be active
Target not created

解决方法如下:


首先右键单击包含有汇编部分的c语言文件名,然后在如上图所示的菜单中选择带有红色方框的选项

 

 




在弹出的对话框中,将上图中红色方框选中的两项打上勾(默认的情况下,前面的勾是灰色的,要让这两项前的勾变为黑色的),点击确定。
OK了,你在次编译,发现,没错误,通过了,很高兴,只有一个“?C_START”警告。不管,先下载去试试,结果,你下载,发现板子竟然没能运行。原因就在于此警告。


3、?C_START等相关警告的处理

按照2 中的方法处理完之后,再编译不会出现错误信息了,但是会出现如下的警告信息:
linking...
*** WARNING L1: UNRESOLVED EXTERNAL SYMBOL
SYMBOL:  ?C_START
MODULE:  STARTUP.obj (?C_STARTUP)
*** WARNING L2: REFERENCE MADE TO UNRESOLVED EXTERNAL
SYMBOL:  ?C_START
MODULE:  STARTUP.obj (?C_STARTUP)
ADDRESS: 000DH

处理方法如下:



 

在如上图所示的“Source Group 1”上点右键,在菜单中选择 “Add Files to Group 'Source Group 1' ”


找到你的KEIL安装目录,选择其中的“C51”目录下的“LIB”目录下的“C51S.LIB”文件,点击Add,然后Close即可。

注意,上图所示的文件选择框进入LIB目录下后,默认只显示.c文件,需要在“文件类型”中选择“Library file (*.lib)”,即可显示LIB文件了。

添加
C51S.LIB到工程后,再次编译,警告信息消失。
linking...
Program Size: data=9.0 xdata=0 code=28
creating hex file from "sendata"...
"sendata" - 0 Error(s), 0 Warning(s).
好的,编译大功告成了,下载到目标板子,运行ok,这样就好了。实际上,这个C51S.lib是c与汇编的连用的一些初始化设定所需要的。

Keil C嵌入汇编的一些注意事项:
上面所弄的都可以了,其实keil C的编译器优化是做得蛮聪明的了,例如:
;  while((PCON&0x20)==0x20);
; SOURCE LINE # 812

?C0119: 
MOV   A,PCON
JB    ACC.5,?C0119
其中绿色的是C代码,红色的是Keil编译之后的汇编代码。




现在可以用c去做一些主要演算主要处理,用汇编去做一些驱动,中断处理,但是尽管Keil的优化有改进,但是还是有要注意的,有些需要注意的是:

从数据存储类型来说,8051系列有片内、片外程序存储器,分别对应codedataxdataidata以及根据51系列特点而设定的pdata类型,使用不同的存储器,将使程序执行效率不同,在编写C51程序时,指定变量的存储类型,这样将有利于提高程序执行效率。

A。data:固定指前面0x00-0x7f128RAM,可以用acc直接读写的,速度最快,生成的代码也最小。
一般来讲,我们需要提高效率定义变量的时候尽量用data不然,keil c 有时候会吧你的变量compileidata,或者xdata,需要用mov @R0、movx去寻址,这样效率就低了。

B。idata:固定指前面0x00-0xff256RAM,其中前128data128完全相同,只是因为访问的方式不同。idata是用类似C中的指针方式访问的。汇编中的语句为:mox ACC,@Rx.(不重要的补充:cidata做指针式的访问效果很好)


C。xdata:外部扩展RAM,用DPTR访问,
movx a,@dptr效率不高。

D。pdata:外部扩展RAM的低256个字节,地址出现在A0-A7的上时读写,用movx a,@Rx读写

E。code的作用是告诉单片机,我定义的数据要放在ROM(程序存储区)。相当与汇编里面的寻址MOVC,不用多说,但是你需要指定code的位置的时候,就要用到汇编了,
ORG 0060H
DB 00h 05h 3dh 33h.


F。还需要注意的是,单片机的ram有限 ram统一管理,直接赋值,建议少使用C内部内部临时变量。

c函数定义所谓动态的变量做形参,主要是为了移植程序方便,但在keil c compiler,它的动态变量是会从堆叠(stack)里或者用工作寄存器组 R0---R7,对ram面操作,实际上,使用太多的动态变量(形参)会让程序变得冗长,占用stack,降低效率。如果指定data变量,而且是全局变量,那么我们可以减少很多的冗余代码。操作时,只需把全局变量(data ram)作为函数形参,传递时更新全局变量,在程序用调用全局的data ram,那么效率非常高,汇编用到的只是mov A, xxh


G。 
赋值的时候,可用“=”“|=”“&=”等,反汇编就是movorlanl得语句,但是如果赋值像a= 0x25<<2;这样赋值,keil  C compiler,并不一定会把你的0x25<<1优化成一个结果,而是会吧<<2移位语句一起生成。


H。还要你需要移位操作的时候,可以直接把要操作的数据放到ACC,或者需要操作CY等,也可以直接在C语言里面对ACC已经CY操作,这样可以简化程序。如:
Write_74595:
; {

;  PORT_OF_595_DATA_IN_SER=0;
CLR   PORT_OF_595_DATA_IN_SER
;  ACC>>=1;
CLR   C
RRC   A
;  PORR_OF_595_DATA_CLK_SCLK=0;
CLR   PORR_OF_595_DATA_CLK_SCLK
;  PORT_OF_595_DATA_IN_SER=CY;
MOV   PORT_OF_595_DATA_IN_SER,C
;  PORR_OF_595_DATA_CLK_SCLK=1;
SETB  PORR_OF_595_DATA_CLK_SCLK
其中绿色是C语言,红色是汇编语言的对应翻译,如果用For语句和变量左右移去操作,那么效率。。。。  嘿嘿,保证吓到你,下面有说明。


I。少用有符号数定义变量。

用有符号数是一个麻烦事,如果碰到无符号数,c compiler会用一大堆汇编去转换,包括用到xorcpl逻辑语句等语句,用无符号数(unsigned)就是简单的8位,0---255C compiler 编译的是时候,加减也只是用addsubb和不需要对符号标志(8位最高位是符号标志)处理。

J. 涉及到时序,最好用汇编,算准指令时间。

我买了一个tft液晶,买家给了一个keil c驱动程序,买家强调,要驱动他需要33Hmz的晶振+stc的1t单片机,然后前辈看了驱动资料。很快,用了一个12Mhz晶振的4T华邦单片机驱动成功。原因就是他的汇编非常熟练,准确算出驱动需要的指令周期并编写出简洁高效的驱动程序。当然,这个是C无法做到的,也是Kiel C compiler无法做到的。


K。下面看一个C与汇编的对比(上面是汇编,下面是C):



是不是觉得很纳闷,用了C的两个For语句而已,汇编竟然如此庞大哦??
令人人吃惊的竟然用到SUBB指令!!
这就是C特别要注意的一些地方了:
··1,count这个形参,在汇编中,用R7,我们可以如上面说说的,直接用ACC做形参,省去mov R7,A的操作
··2,不要用少于或者大于的判断,如上图,少于竟然是用到subb指令去判断,subb这个不是个省时的指令。
··3,不用for语句,用goto取代,goto等于汇编中的sjmp或者jmp
··4,用C的时候,我们直接可以操作ACC,CY,B等做传入参数,不必用到形参。

L。关于乘除的问题,先看一个例子(其中绿色是C语言,红色是汇编语言的对应翻译):
Seg_Wei[1]=Out_Put_Time_Start[0]/10;
Seg_Wei[0]=Out_Put_Time_Start[0]%10;
你猜测着两句C语言Keil编译之后会变成怎样?? 

汇编成如下:
MOV   A,Out_Put_Time_Start
MOV   B,#0AH
DIV   AB
MOV   Seg_Wei+01H,A
MOV   A,Out_Put_Time_Start
MOV   B,#0AH
DIV   AB
MOV   Seg_Wei,B

竟然用了两个DIV指令,一个div就是4个周期了啊,
所以我做了以下优化:
Seg_Wei[1]=Out_Put_Time_Start[0]/10;
Seg_Wei[0]=B;

汇编是变成这样了:
MOV   A,Out_Put_Time_Start
MOV   B,#0AH
DIV   AB
MOV   Seg_Wei+01H,A
MOV   Seg_Wei,B 
这样自然高效简洁多了,看起来也舒服 

 

就此停笔,希望大家一起分享。

===========================end===========================

 

Done Lin
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值