浅谈C51内存优化(data idata xdata)

发布者:莫愁前路最新更新时间:2016-11-23 来源: eefocus关键字:C51  内存优化  data  idata  xdata 手机看文章 扫描二维码
随时随地手机看文章

对 51 单片机内存的认识,很多人有误解,最常见的是以下两种

① 超过变量128后必须使用compact模式编译
   实际的情况是只要内存占用量不超过 256.0 就可以用 small 模式编译
② 128以上的某些地址为特殊寄存器使用,不能给程序用
   与 PC 机不同,51 单片机不使用线性编址,特殊寄存器与 RAM 使用重复的重复的地址。但访问时采用不同的指令,所以并不会占用 RAM 空间。

    由于内存比较小,一般要进行内存优化,尽量提高内存的使用效率。

    以 Keil C 编译器为例,small 模式下未指存储类型的变量默认为data型,即直接寻址,只能访问低 128 个字节,但这 128 个字节也不是全为我们的程序所用,寄存器 R0-R7必须映射到低RAM,要占去 8 个字节,如果使用寄存组切换,占用的更多。

    所以可以使用 data 区最大为 120 字节,超出 120 个字节则必须用 idata 显式的指定为间接寻址,另外堆栈至少要占用一个字节,所以极限情况下可以定义的变量可占 247 个字节。当然,实际应用中堆栈为一个字节肯定是不够用的,但如果嵌套调用层数不深,有十几个字节也够有了。


为了验上面的观点,写了个例子

#define LEN 120
data UCHAR tt1[LEN];
idata UCHAR tt2[127];

void main()
{
    UCHAR i,j;

    for(i = 0;  i < LEN; ++i )
    {
        j = i;
        tt1[j] = 0x55;
    }
}

可以计算 R0-7(8) + tt1(120) + tt2(127) + SP(1) 总共 256 个字节

keil 编译的结果如下:
Program Size: data=256.0 xdata=0 code=30
creating hex file from ".\Debug\Test"...
".\Debug\Test" - 0 Error(s), 0 Warning(s).
(测试环境为 XP + Keil C 7.5)

    这段代码已经达到了内存分配的极限,再定义任何全局变量或将数组加大,编译都会报错 107

    这里要引出一个问题:为什么变量 i、j 不计算在内?
    这是因为 i、j 是局部变量,编译器会试着将其优化到寄存器 Rx 或栈。问题也就在这了,如果局部变量过多或定义了局部数组,编译器无法将其优化,就必须使用 RAM 空间,虽然全局变量的分配经过精心计算没有超出使用范围,仍会产生内存溢出的错误!

    而编译器是否能成功的优化变量是根据代码来的
    上面的代码中,循环是臃肿的,变量 j 完全不必要,那么将代码改成

UCHAR i;
UCHAR j;

for(i = 0;  i < LEN; ++i )
{
    tt1[i] = 0x55;
}

再编译看看,出错了吧!
因为编译器不知道该如何使用 j,所以没能优化,j 须占 RAM 空间,RAM 就溢出了。
(智能一点的编译器会自动将这个无用的变量去掉,但这个不在讨论之列了)

另外,对 idata 的定义的变量最好放在 data 变量之后

对于这一种定义


uchar c1;
idata uchar c2;
uchar c3;
变量 c2 肯定会以间接寻址,但它有可能落在 data 区域,就浪费了一个可直接寻址的空间


变量优化一般要注意几点:

    ①让尽可能多的变量使用直接寻址,提高速度
      假如有两个单字节的变量,一个长119的字符型数组
      因为总长超过 120 字节,不可能都定义在 data 区
      按这条原则,定义的方式如下:


      data UCHAR tab[119];
      data UCAHR c1;
      idata UCHaR c2;
      但也不是绝的,如果 c1, c2 需要以极高的频率访问,而 tab 访问不那么频繁
      则应该让访问量大的变量使用直接寻址:

      data UCAHR c1;
      data UCHaR c2;
      idata UCHAR tab[119];
      这个是要根据具体项目需求来确定的

    ②提高内存的重复利用率
      就是尽可能的利用局部变量,局部变量还有个好处是访问速度比较快
      由前面的例子可以看出,局部变量 i, j 是没有单独占用内存的
      子程序中使用内存数目不大的变量尽量定义为局部变量

    ③对于指针数组的定义,尽可能指明存储类型
       尽量使用无符号类型变量

      一般指针需要一个字节额外的字节指明存储类型
     8051 系列本身不支持符号数,需要外加库来处理符号数,一是大大降低程序运行效率,二是需要额外的内存


    ④避免出现内存空洞

      可以通过查看编译器输出符号表文件(.M51)查看
      对前面的代码,M51文件中关于内存一节如下:


* * * * * * *   D A T A   M E M O R Y   * * * * * * *
REG     0000H     0008H     ABSOLUTE     "REG BANK 0"
DATA    0008H     0078H     UNIT         ?DT?TEST
IDATA   0080H     007FH     UNIT         ?ID?TEST
IDATA   00FFH     0001H     UNIT         ?STACK


第一行显示寄存器组0从地址0000H开始,占用0008H个字节
第二行显示DATA区变量从0008H开始,占用0078H个字节
第三行显示IDATA区变量从0080H开始,占用007F个字节
第四行显示堆栈从00FFH开始,占0001H个字节

由于前面代码中变量定义比较简单,且连续用完了所有空间,所以这里显示比较简单
变量定义较多时,这里会有很多行

如果全局变量与局部变量分配不合理,就有可能出现类似下面的行

0010H     0012H                  *** GAP ***
      该行表示从0010H开始连续0012H个字节未充分利用或根本未用到
出现这种情况最常见的原因是局变量太多、多个子程序中的局部变量数目差异太大、使用了寄存器切换但未充分利用


关键字:C51  内存优化  data  idata  xdata 引用地址:浅谈C51内存优化(data idata xdata)

上一篇:C51与HT9170和HT9200接口程序(双音多频)
下一篇:Silabs IDE 编译器设置(Keil for C51)

推荐阅读最新更新时间:2024-03-16 15:21

C51程序设计中的数组和指针关系
一、指针是地址 各种类型的数据被分配合适的内存。比如整形数据通常被分配两个连续的存储单元(字节)存放。对数据的访问是通过分配给数据的内存首地址来实现的。我们称这些内存地址为指针。 二、指针变量是存放地址的变量 如果我们定义了一些变量来存放数据的地址(指针),这样的变量就是指针变量。也就是说,指针变量有两个特征:首先它作为变量会分配内存空间;其次,它存放的内容应该是内存地址。 比如,当我们声明了一个整形变量i并且赋予初始值10;同时我们声明了一个整形的指针变量p,将它指向变量i。当我们运行代码时,内存中可能是这样的: i - |0AH| FFF0H |00H|FFF1H |...| p - |0F0H | FFFAH |0FFH
[单片机]
HD44780读写C51程序
下面我给大家介绍一个HD44780读写单片机c51程序 #include reg51.h #include intrins.h sbit GND_LCD=P1^7; sbit rs=P1^0; sbit rw=P1^1; sbit e=P1^2; unsigned char busy(void); void ctrl(unsigned char); void wd_h(unsigned char); void wd_l(unsigned char); unsigned char rd(void); void write(unsigned char); void init(void); void NOP(void); voi
[单片机]
最简单的4*4矩阵键盘程序 c51
调试通过. key_scan_p2() //定时器,或者主程序扫描 { uchar x,y,z; P2=0x0f; x=P2&0x0f; P2=0xf0; y=P2&0xf0; z=x|y; if(z!=key_value1) key_value1=z; //如果两次结果不同 else { if(key_value1 == 0xff) key_release=0; else { if (key_release==0) //所有按键已经松开了吗? { key_release = (z ^ 0xFF); key_val = z; switch(key
[单片机]
实时时钟DS1302读写程序(C51)
//打开DS1302 void TimeSpiOpen(void) { TIMECLK = 0; TIMERST = 0;//禁止DS1302 TIMEIO = 1;//释放数据总线 TIMERST = 1;//使能DS1302 } //关闭DS1302 void TimeSpiClose(void) { TIMERST = 0;//禁止DS1302 TIMEIO = 1;//释放数据总线 TIMECLK = 1; } //读写DS1302 unsigned char TimeSpiReadWrite(unsigned char val) { unsigned char i; ACC = val;//取8位数据 for (i =
[单片机]
C51单片机延时程序源代码
C51单片机(STC11L32/48/60XE)的延时程序: void delay_us(U8 us) { do { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); us--; }
[单片机]
C51 延时程序
一.相关换算 1、1s=10^3ms(毫秒)=10^6μs(微秒)=10^9ns(纳秒)=10^12ps(皮秒)=10^15fs(飞秒)=10^18as(阿秒)=10^21zm(仄秒)=10^24ym(幺秒) 2、物质在1秒内完成周期性变化的次数叫做频率,常用f表示。   物理中频率的单位是赫兹(Hz),简称赫,也常用千赫(kHz)或兆赫(MHz)或GHz做单 位。1kHz=1000Hz,1MHz=1000000Hz 1GHz=1000MHz。频率f是周期T的倒数,即f =1/T,波速=波长*频率。 而像中国使用的电是一种正弦交流电,其频率是50Hz,也就是它速度惊人的地方,一秒钟内做了50次周期性变化 3、定时器定时原理采用的方
[单片机]
<font color='red'>C51</font> 延时程序
DATAmatic方案帮助汽车制造商应对互联汽车数据爆炸
据外媒报道,互联汽车服务供应商Airbiquity®宣布推出边缘数据管理解决方案套件DATAmatic®,旨在满足日益增长的汽车制造商对管理和使用互联汽车数据服务和工具的需求。该套件包括DATAmatic边缘数据平台(Edge Data Platform)和DATAmatic边缘数据记录器(Edge Data Logger)。 互联汽车产生的数据数量和多样性都在呈指数上涨,且为汽车制造商带来巨大经济价值。这些数据为推动新车功能、个性化消费者服务和驾驶体验提供全新开发机会,但也使得汽车制造商面临着重大的数据管理挑战。 DATAmatic套件可提供安全解决方案,旨在帮助汽车制造商应对数据挑战。Airbiquity强大的数据管
[嵌入式]
<font color='red'>DATA</font>matic方案帮助汽车制造商应对互联汽车数据爆炸
4×4键盘C51单片机程序源码分享
/*MCU:AT89S52*/ #include #include #define uchar unsigned char int key; int del; void Key_Scan(void); /************主程序*************/ void main(void) { void Key_Scan(void); void delay(int); while(1) { Key_Scan(); delay(2000); } } /********矩键查寻键值4*4程序******/ void Key_Scan(void) { uchar readkey; uchar x_temp,y_temp; P
[单片机]
4×4键盘<font color='red'>C51</font>单片机程序源码分享
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved