Keil c51的应用及特性解析

发布时间:2023-04-13  

Keil c51号称作为51系列单片机最好的开发环境,大家一定都很熟悉。它的一些普通的特性大家也都了解,(书上也都说有)如:因为51内的RAM很小,C51的函数并不通过堆栈传递参数(重入函数除外),局部变量也不存储在堆栈中,而是存在于固定的RAM中及寄存器中。那么看一下下面的程序。


void fun1(unsigned char i)

{

}


正常情况参数i通过R7传入函数,那么它的实际地址在什么地方呢?就是R7吗?回答这个问题之前我们先来了解keil c51的几个有趣的特性(不考虑重入函数)。


一、函数在调用前定义与在调用后定义产生的代码是有很大差别的(特别是在优化级别大于3级时)。(本人也不太清楚为什么,大概因为在调用前定义则调用函数已经知道被调用函数对寄存器的使用情况,则可对函数本身进行优化;而在调用后进行定义则函数不知被调用函数对寄存器的使用情况,它默认被调用函数对寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)都已经改变,因此不在这些寄存器中存入有效的数据)


二、函数调用函数时除在堆栈中存入返回地址之外,不在堆栈中保存其它任何寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)的内容。(除非被调用函数使用了using特性)


三、中断函数是一个例外,它会计算自身及它所调用的函数对寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)的改变,并保存相应它认为被改变了的寄存器。


四、使用C写程序时,尽量少使用using n (n=0,1,2,3)特性。(这个特性在本人使用的过程中存在一些问题,不知算不算是一个小bug)


以下的试验都是在(环境 keil c51 v7.20)中,优化级为default下完成。


先看第一个特性问题。

例1:

void fun2(void)

{

}

void fun1(unsigned char i)

{

fun2();

while(i--);

}

它的汇编代码如下:

; void fun2(void)

RSEG ?PR?fun2?TEST

fun2:

; SOURCE LINE # 12

; {

; SOURCE LINE # 13

; }

; SOURCE LINE # 14

RET

; END OF fun2

;

; void fun1(unsigned char i)

RSEG ?PR?_fun1?TEST

_fun1:

USING 0

; SOURCE LINE # 16

;---- Variable ‘i?240’ assigned to Register ‘R7’ ----

; {

; SOURCE LINE # 17

; fun2();

; SOURCE LINE # 18

LCALL fun2

?C0003:

; while(i--);

; SOURCE LINE # 19

MOV R6,AR7

DEC R7

MOV A,R6

JNZ ?C0003

; }

; SOURCE LINE # 20

?C0005:

RET

; END OF _fun1

从中可以看到fun2()在fun1()前先定义,fun1()知道fun2()对寄存器的使用情况,知道R7没有改变,而参数i存于R7中,即i既是R7。(;---- Variable ‘i?140’ assigned to Register ‘R7’ ----)

看另一情况

void fun2(void);

void fun1(unsigned char i)

{

fun2();

while(i--);

}

void fun2(void)

{

}

汇编代码如下:

; void fun1(unsigned char i)

RSEG ?PR?_fun1?TEST

_fun1:

USING 0

; SOURCE LINE # 14

MOV i?140,R7

; {

; SOURCE LINE # 15

; fun2();

; SOURCE LINE # 16

LCALL fun2

?C0002:

; while(i--);

; SOURCE LINE # 17

MOV R7,i?140

DEC i?140

MOV A,R7

JNZ ?C0002

; }

; SOURCE LINE # 18

?C0004:

RET

; END OF _fun1

;

; void fun2(void)

RSEG ?PR?fun2?TEST

fun2:

; SOURCE LINE # 20

; {

; SOURCE LINE # 21

; }

; SOURCE LINE # 22

RET

; END OF fun2

fun2()在fun1()调用后定义,因fun1()调用fun2()时不知道fun2()对寄存器的使用情况,则认为fun2()改变了所有的寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)。因为fun1()认为fun2()改变了寄存器的值(包括R7),因此i虽然通过R7传递,但因已因调用fun2()而改变,所以不能再存在R7了,而上在RAM中额外的用一个Byte来存储。

这也就解释了在开始时的那个问题,参数i的存储是看问题而定的。

哈哈,是否很有趣呢。在节约RAM方面,这可是一个很有用的特性哦。(大家是否也为自己的节省了1Byte的RAM)

这个例子还解释了第二个特性,函数调用函数时除在堆栈中存入返回地址之外,不在堆栈中保存其它任何寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、R6、R7)的内容。函数在调用函数前,尽量不在这些寄存器中保存有效的数据,实在无法避免,则把有效数据存入固定的RAM中。

对于中断函数问题,当你看到下面的程序相差55 Byte时,不知你会怎么想的。

例2:

void OSTimeDly(void); //using 1

static void Timer0OVInt(void) interrupt 1 //using 1

{

TR0 = 0;

TH0 = 100;

TL0 = 100;

TR0 = 1;

OSTimeDly();

}

void OSTimeDly(void) //using 1

{

}

void OSTimeDly(void) //using 1

{

}

static void Timer0OVInt(void) interrupt 1 //using 1

{

TR0 = 0;

TH0 = 100;

TL0 = 100;

TR0 = 1;

OSTimeDly();

}

它们的汇编代码分别是,

; static void Timer0OVInt(void) interrupt 1 //using 1

RSEG ?PR?Timer0OVInt?TEST

USING 0

Timer0OVInt:

PUSH ACC

PUSH B

PUSH DPH

PUSH DPL

PUSH PSW

MOV PSW,#00H

PUSH AR0

PUSH AR1

PUSH AR2

PUSH AR3


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    容易、网上资源丰富、单片机稳定性及资源比较丰富、通过串口即可下载程序成本低廉。本文将介绍开发环境KeilC51的安装及工程的创建。 工具/原料   KeilC519.00安装包 方法/步骤......
    以充电桩测试负载为例介绍电池检测仪使用步骤;通过对充电桩测试负载等电池测试的了解,以及具备管理功能的动力电池系统测试的了解,接下来就是向用户们解答如何正确使用电池检测仪,为此将为大家讲解电池检测仪的使用步骤......
    步进电机的使用方法 使用注意事项;  步进电机是一种电动机,它通过逐步改变电磁场的方向来驱动转子,从而实现转动。步进电机的特点是可以精确定位,因为它们可以在每一个步骤中停下来,不需要反馈系统。另外......
    必须交换引脚 2 和 3 才能使电机正常工作。 // 使用步骤和引脚创建步进器类的实例 步进步进器(STEPS、D1、D3、D2、D4);   在下一步中,我们必须共享我们的 blynk 应用......
    玻璃板液位计投用步骤_玻璃板液位计的钢球作用;  玻璃板液位计投用步骤   如何投用玻璃板液位计:   1、检查安装正确无误,液位计完整无损;   2、如果设备到液位计有多zhuan道阀门,则先......
    STM32单片机RTC时钟的使用方法及步骤;STM32RTC使用步骤: 打开PWR时钟和Backup区数据访问 若使用外部低速时钟(LSE),打开LSE并等待起振 选择和打开RTC时钟,等待......
    触发)就比 None (没有触发源) 多一个步骤,首先都要写 DHR 寄存器写入数据,写完后 None 自动转换,但是 software trigger 需要多一步软件触发操作。 在标准库中,使用步骤......
    //内核相关的头文件,NVIC在这里 10 #include 'misc.h' 11  12 /*GPIO配置使用步骤 13 1.使能,调用函数: 14 RCC_APB2PeriphClockCmd......
    是从硬件开始,到操作系统,再到实用步骤。 64位更多体现在使用上,这就涉及第三方App的优化,一个32位的软件装在64位操作系统,并不等于它能获得2倍的性能。 苹果对iOS生态的把控力远胜于它的对手们,但这......
    普赛斯仪表PL系列设备的测试场景和使用步骤;普赛斯仪表PL系列窄脉冲电流源可应用于VCSEL及其它需要窄脉冲激励的测试场景。 PL系列设备有两个通道: ①、恒流脉冲激励源,并能......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>