单片机程序MDK编译大小说明及小建议

---------------------------------------------------------------------------------------------------

字段说明:

Code:即代码域,它指的是编译器生成的机器指令,这些内容被存储到ROM区。 

RO-data:Read Only data,即只读数据域,它指程序中用到的只读数据,这些数据被存储在ROM区,因而程序不能修改其内容。例如C语言中const关键字定义的变量就是典型的RO-data。  

RW-data:Read Write data,即可读写数据域,它指初始化为“非0值”的可读写数据,程序刚运行时,这些数据具有非0的初始值,且运行的时候它们会常驻在RAM区,因而应用程序可以修改其内容。例如C语言中使用定义的全局变量,且定义时赋予“非0值”给该变量进行初始化。  

ZI-data:Zero Initialie data,即0初始化数据,它指初始化为“0值”的可读写数据域,它与RW-data的区别是程序刚运行时这些数据初始值全都为0,而后续运行过程与RW-data的性质一样,它们也常驻在RAM区,因而应用程序可以更改其内容。例如C语言中使用定义的全局变量,且定义时赋予“0值”给该变量进行初始化(若定义该变量时没有赋予初始值,编译器会把它当ZI-data来对待,初始化为0);  ZI-data的栈空间(Stack)及堆空间(Heap):在C语言中,函数内部定义的局部变量属于栈空间,进入函数的时候从向栈空间申请内存给局部变量,退出时释放局部变量,归还内存空间。而使用malloc动态分配的变量属于堆空间。在程序中的栈空间和堆空间都是属于ZI-data区域的,这些空间都会被初始值化为0值。编译器给出的ZI-data占用的空间值中包含了堆栈的大小(经实际测试,若程序中完全没有使用malloc动态申请堆空间,编译器会优化,不把堆空间计算在内)。

CODE+RO-data+ZI-data:是在单片机Flash中占用的大小

RW-data+ZI-data:是在单片机RAM中占用的大小

------------------------------------------------------------------------------------------------------

举例1:

如:

void test(int x,int y)

{

    printf("x+y=%d\r\n",x+y);

 }

这样的代码编译后会放置到CODE段。

很多时候使用宏,会增加CODE大小,需要注意。

举例二:

unsigned char sendbuf[10]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};

因为是已经初始化的buf,所以这样的代码编译会会放置到RW-data段。

举例三:

在嵌入式开发过程中,我们经常用用到没有字库的LCD或者数码管,这时就需要自己设计字符表。字符表通常不会被更改,所以会被设置成只读属性。如:

const unsigned char table[]={0xC0,0xF9,0xA4,0xB0,0x99, //0~4 

      0x92,0x82,0xF8,0x80,0x90, //5~9       

0x88,0x83,0xA7,0xA1,0x86,0x8E}; //A~F

这样的代码编译后会放置在RO-data段。

举例四:

在驱动开发过程中,有时读写Flash可能会开辟一块全局buf如:

unsigned char testbuf[1024]={0}; 

这样的代码编译后会放置在ZI-data段。

-----------------------------------------------------------------------------------------------------

小建议

建议一:数码管、LCD字库设置为const比较保险,查表时比较稳定。

            如果是C51单片机可以试试code关键字,很有意思。

建议二 :全局的buf不是越大越好,够用就可以了。如果嵌入式系统模块较多,你开辟了很多buf,随着开发进度的深入,你可能                会发现RAM不够用了,到时候再去挤bug,就很难找了。

建议三:调试的log,用完后最好注释掉,或者用帅气的宏开关屏蔽。因为这些log,一定占用了很多code段大小,所以如果                    flash小的话,要小心咯。

举例:观察以下两个代码段,请问哪种代码段关闭log的方式比较好?

代码段一:

------------------------------------------

#define LOG_PRINT    mylogprint

void mylogprint(char * string)

{

    printf(string);

}

int fputc(int ch, FILE *f)

{
    //sendchar(ch);
    //return ch;

}

int main(void)

{

LOG_PRINT("HelloWord1");    

LOG_PRINT("HelloWord2");    

LOG_PRINT("HelloWord3");    

LOG_PRINT("HelloWord1");        

LOG_PRINT("HelloWord2");        

LOG_PRINT("HelloWord3");

LOG_PRINT("HelloWord1");        

LOG_PRINT("HelloWord2");        

LOG_PRINT("HelloWord3");

while(1) {}

}



代码段二:

------------------------------------------

#define LOG_PRINT    //mylogprint

void mylogprint(char * string)

{

    printf(string);

}

int fputc(int ch, FILE *f)

{
    sendchar(ch);
    return ch;

}

int main(void)

{

LOG_PRINT("HelloWord1");    

LOG_PRINT("HelloWord2");    

LOG_PRINT("HelloWord3");    

LOG_PRINT("HelloWord1");      

LOG_PRINT("HelloWord2");      

LOG_PRINT("HelloWord3");

LOG_PRINT("HelloWord1");       

LOG_PRINT("HelloWord2");        

LOG_PRINT("HelloWord3");

while(1) {}

}

-------------------------------------------------------

我个人的理解,两种方式都能达到关闭log的效果。

但是在log很多的时候,用代码段二的方式,可以大大缩短实际编译的固件程序大小。我的一次亲身经历,是随着项目的深入,代码越来越多,后面发现flash快满了,这时我采用代码段二的方式关闭log,节省了将近40Kcode段大小。也就是flash上可以再写入40K的实际功能代码。对于Flash大小紧张的嵌入式芯片,40K的空间爽不爽?


你可能感兴趣的:(单片机程序MDK编译大小说明及小建议)