概述
但是在代码优化和实时性方面,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系列有片内、片外程序存储器,分别对应code、data、xdata、idata以及根据51系列特点而设定的pdata类型,使用不同的存储器,将使程序执行效率不同,在编写C51程序时,指定变量的存储类型,这样将有利于提高程序执行效率。
A。data:固定指前面0x00-0x7f的128个RAM,可以用acc直接读写的,速度最快,生成的代码也最小。一般来讲,我们需要提高效率定义变量的时候尽量用data,不然,keil c 有时候会吧你的变量compile成idata,或者xdata,需要用mov @R0、movx去寻址,这样效率就低了。
B。idata:固定指前面0x00-0xff的256个RAM,其中前128和data的128完全相同,只是因为访问的方式不同。idata是用类似C中的指针方式访问的。汇编中的语句为:mox ACC,@Rx.(不重要的补充:c中idata做指针式的访问效果很好)
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。 赋值的时候,可用“=”“|=”“&=”等,反汇编就是mov,orl,anl得语句,但是如果赋值像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会用一大堆汇编去转换,包括用到xor,cpl逻辑语句等语句,用无符号数(unsigned)就是简单的8位,0---255,C compiler 编译的是时候,加减也只是用add、subb和不需要对符号标志(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
最后
以上就是受伤红酒为你收集整理的Keil C 与 汇编 联用 一些记录与注意事项的全部内容,希望文章能够帮你解决Keil C 与 汇编 联用 一些记录与注意事项所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复