您的位置 首页 电路

C51优化规划之循环句子

C51有三种循环语句即while,do-while和for,这三种循环都可以用来处理同一问题,基本上三者可以相互替换但由于C51是针对51汇编语言的编译器,

C51有三种循环句子即while,do-while和for,这三种循环都能够用来处理同一问题,底子上三者能够彼此替换.但由于C51是针对51汇编言语的编译器,假如不留意51汇编指令的特色,不同的编程办法或许得到不同的程序功用(履行速度和代码长度).以核算1+2+3+…+9+10为例,下面做一比照.

程序1:unsigned char i;unsigned char sum;for(i=1,sum=0;i<11;i++){sum+=i;}汇编代码为:C:0x0003 7F01 MOV R7,#0x01C:0x0005 E4 CLR AC:0x0006 FE MOV R6,AC:0x0007 EF MOV A,R7C:0x0008 2E ADD A,R6C:0x0009 FE MOV R6,AC:0x000A 0F INC R7C:0x000B BF0BF9 CJNE R7,#0x0B,C:0007代码长度(字节):11,履行周期(机器周期):63程序2:unsigned char i;unsigned char sum;for(i=10,sum=0;i;i--){sum+=i;}汇编代码为:C:0x000F 7F0A MOV R7,#0x0AC:0x0011 E4 CLR AC:0x0012 FE MOV R6,AC:0x0013 EF MOV A,R7C:0x0014 2E ADD A,R6C:0x0015 FE MOV R6,AC:0x0016 DFFB DJNZ R7,C:0013代码长度(字节):9,履行周期(机器周期):53程序3:unsigned char i=11;unsigned char sum=0;while(i--){sum+=i;}汇编代码为:C:0x0003 7F0A MOV R7,#0x0BC:0x0005 E4 CLR AC:0x0006 FE MOV R6,AC:0x0007 AD07 MOV R5,0x07C:0x0009 1F DEC R7C:0x000A ED MOV A,R5C:0x000B 6005 JZ C:0012C:0x000D EF MOV A,R7C:0x000E 2E ADD A,R6C:0x000F FE MOV R6,AC:0x0010 80F5 SJMP C:0007代码长度(字节):15,履行周期(机器周期):130

从以上三个不同程序能够看出,其运算成果都是0x37(55),但最短代码为9,最长代码为15,最快速度为53,最慢速度为130,可见三个程序的功用差异较大.

怎么编出占用空间小运转功率高的循环代码呢?在C51编译环境下要写出优异的循环代码有必要了解51汇编言语的指令系统.调查程序2,循环操控指令运用了DJNZ循环搬运指令,该指令一起完结计数和循环判别两种操作,并且只占用两个字节,是51指令系统中最为高效的循环指令,因而在规划循环程序时,应尽或许使C51将DJNZ用于循环程序中.当然DJNZ指令的循环次数是确认的,首要用在有确认循环次数的状况.

DJNZ指令的一个最大特色是递减计数,因而循环程序有必要选用递减办法才有或许编译出DJNZ指令,如以上程序2.DJNZ指令的另一个特色是先减后判别,因而规划循环程序也有必要坚持先减后判别的准则,不然得不到DJNZ指令,如以上程序3.假如将程序3改写为:
unsigned char i=10;
unsigned char sum=0;
while(i)
{
sum+=i;
i--;
}
就能够得到与程序2相同的汇编代码.若i--后还有其它操作,比方改为:
unsigned char i=10,j=0;
unsigned char sum=0;
while(i)
{
sum+=i;
i--;
j++;
}
也得不到DJNZ汇编指令,也就是说,循环句子在履行进程中,减1与判别有必要是接连的,且减1在前,判别在后.关于while循环,当将减1与判别组成一步时,应当选用while(--i).依照以上所述,do-while循环相同能够汇编出DJNZ指令,不再一一列举.

可是当循环变量不是经过常数赋值句子完结,而是来自于另一个变量时,for和while句子不管选用何种操控流程都不能发生DJNZ指令,由于这两种循环都是先判别后履行的操控逻辑,而DJNZ的履行进程是先履行循环体后进行循环判别.依照DJNZ的操控流程,只要do-while句子契合这个条件,因而当循环次数不是常量而是变量时,就有必要运用do-while循环句子了.

综上所述,若要运用DJNZ指令进步程序功率,在规划循环程序中应坚持以下三大准则:
① 选用递减计数;
② 先减后判别,减与判别接连进行;
③ 循环次数为变量时,选用do-while循环.
8051单片机有两条循环指令,即DJNZ Rn,rel和DJNZ direct,rel.关于底子型单片机而言,两者的履行时间都是2个机器周期,但两者的指令长度不同,前者占用2个字节,后者占用3个字节.循环程序还涉及到循环变量初始化操作,关于前者运用MOV Rn,#XX,2字节1周期,关于后者运用MOV direct,#XX,3字节2周期.以单层循环为例,运用作业寄存器比直接地址节约2字节1周期.除此之外,两者比较,更重要的功用差异在于后者需求再分配一个内存单元.由于一般程序模块都运用作业寄存器作局部变量,将作业寄存器用作循环变量不会增加内存占用量.总归,运用作业寄存器作循环计数器是规划循环程序应坚持的一项重要准则.

一般状况下,C51编译器将循环次数赋予作业寄存器,比方
unsigned char i;
for(i=100;i;i--)
{
dosomething();
}

可是存在下述状况之一时,C51编译的成果往往令人不满意:
① 函数dosomething是一个外部界说C言语函数;
② 函数dosomething是一个具有C言语接口,内部用汇编言语完成的,供C程序调用的外部函数.
以上两种状况循环变量i都存放在内存单元中,即选用直接寻址办法.关于局部变量i,C51编译器选用了直接地址存储,其原因在于依据这种假定,即在无任何特别处理的状况下,C51默许外部函数占用一切作业寄存器,因而在循环的外部,不能修正这些已被占用的寄存器,C51只能将循环操控变量分配在内存地址单元中.但假如循环体句子中仅运用少量几个或乃至底子不运用作业寄存器,编译器仍按这种假定处理,那么编译器就不能显现出它的高效性了.走运的是,C51供给了补偿这一缺点的伪指令REGUSE.REGUSE伪指令用于奉告编译器某函数或子程序占用了哪些寄存器或特别功用寄存器SFR,编译器依据函数供给的寄存器占用信息就或许将循环变量分配到循环体未占用的寄存器中,然后到达优化规划的意图.

别的,一项开关有必要翻开,即Global Register Coloring,办法是勾选Project - Options for Target - C51 - Global Register Coloring.

在状况①中,应在函数dosomething地点源程序文件中增加代码(假定函数占用A和B):
#pragma asm
$REGUSE dosomething(A,B)
#pragma endasm
从头编译项目后,在汇编窗口中能够看到,循环变量已运用了作业寄存器.
在状况②中,由所以汇编程序,只需增加一行代码(也假定子程序占用A和B):
$REGUSE dosomething(A,B)
相同能够调查到循环变量改成了作业寄存器完成.

需求留意的是,这儿所说的寄存器占用是指在函数或子程序履行进程中或许或必定对这些寄存器形成损坏,即履行写操作,关于只读寄存器不应按占用处理.别的,参数传递运用的作业寄存器不用指明.

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/fangan/dianlu/255291.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部