嵌入式C 语言ROM 压缩方法

    在嵌入式的开发中,由于 ROM 有限,常常造成 ROM 不够的情况。那么怎么在不影响机能实现的情况下,对 ROM 进行压缩成为首要的问题。本篇文章结合了项目实际应用的 ROM 压缩方法,对常用的 ROM 压缩方法进行总结。 在嵌入式开发体系中,ROM 区主要分为中断向量表, const(sconst)段,TEXT 段,以及rompsec section。压缩的主要对象是 const 段,TEXT 段。 
TEXT 段:存储程序生成的机器码。因为,机器码是由我们编写的源代码生成,因此,要对 TEXT 段进行压缩,就要修正我们编写的源代码。下面我们对通用的 TEXT 段压缩进行描述。 

const 段:在源程序用 const 声明的变量放在 const 段。在开发中主要压缩的对象是const table。 

1. 通用的压缩方法


1.1. 无效代码删除

 无效函数删除
编译器在编译源代码->汇编语言->机器语言的过程中,是不会判断当前的函数或者Code是否真正被其他函数调用,都会编译成机器语言。可是在项目开发中由于有很多的Base留用code,这些code实现的是暂时不需要对应或者无需对应的机能,那么需要为这些code加上编译开关,例如:
#if XXXX == YYYY
函数名()
{
函数体
}
#else
#endif

无效table删除
对于不需要的table,要加上编译开关删除。
#if XXXX == YYYY
const table[]
#else
#endif
1.2. 相似处理的共通化

对于函数内部有相似处理的部分,要用函数来代替。

只用到一个函数就取代了其他的重复处理,节省ROM size。但是请注意,因为函数要占用堆栈,共通化会造成RAM堆栈的增加,不过用少量的堆栈换取ROM的大量节省,还是非常值得的。通常的TEXT段的压缩方法就是上面两种方法,这两种方法是压缩ROM很有效的方法。

1.3. const table元素合并
假如目前表格是一个结构体数组,结构体包含3项或者3项以上,其中每项都占用一个或者一个以上的字节,请确认是否有的项目可以合并,公用一个成员类型。
假设结构体成员(D1,D2,D3,D4)那么考虑是否可以合并为(D1|D2,D3,D4),(D1|D2|D3,D4)等等可能的情况。
例如:
typedef struct {
Type_uByte ubTest1;
Type_uByte ubTest2;
Type_uByte ubTest3;
}Test_ts;
static const Test_ts TestTable[] = {
{ VALUE1 , VALUE2 ,VALUE4 } ,
{ VALUE1 , VALUE2 ,VALUE5 } ,
{ VALUE1 , VALUE2 ,VALUE6 } ,
}
结构体的第一项最大值是2,第二项的最大值是8,那么此处表格就可以进行压缩了,可以让结构体的第一项和第二项公用一个字节,第一项占用4个bit,第二项占用4个bit(划分原则要根据不同需求来设定)。压缩结果如下:
typedef struct {
Type_uByte ubTest1;
Type_uByte ubTest2;
}Test_ts;
static const Test_ts TestTable[] = {
{ VALUE18 ,VALUE3 } ,
{ VALUE18 ,VALUE4 } ,
{ VALUE18 ,VALUE5 } ,
}
TestTable优化前后的size如下所示:
优化前                                        优化后                             code减少量
9bytes                                         6bytes                                   3bytes
Note:对const table元素的合并,可以减少const段的size,但是在实际的应用中如果对const table结构体成员进行访问和引用时,不能向未修改前一样直接引用和访问,要进行一些位操作,这样必定增加了code size。所以在使用该方法时还要考虑到对Text size的影响。如果const table的元素合并后size的减少量大于code size的减少量,这个方法就可以应用。
1.4. 结构体成员字节对齐优化
对于结构体类型,定义的时候要考虑到结构体成员字节对齐,按照成员类型从大到小的顺序进行定义,这样可以节省很多空间。
例如:
typedef struct
{
unsigned char ucDmode;
unsigned int ucEcode;
unsigned char ucPcode;
} DcheckElement3_ts;
考虑到字节对齐,该结构体的字节数是4+4+4=12bytes
如果按照从大到小的顺序进行定义:
typedef struct
{
unsigned int ucEcode;
unsigned char ucPcode;
unsigned char ucDmode;
} DcheckElement3_ts;
修正后的结构体的大小为4+4=8bytes。
如果定义的结构体数组的大小为n,那么节省的size为(12-8)*n=4nbytes。

1.5. 全局或静态变量不能有初始值
注意全局变量或静态变量定义时不要赋初始值。定义有初始值的全局或静态变量,其初始值会被分配到ROM中,这样会占用ROM size。所以不用定义有初始值的全局或静态变量,全局或静态变量应该在reset,ACC OFF等场合对其初始化。
Note:编码规范中已经明确规定全局或静态变量在定义的时候不能有初始值,上述的方案只是从Rom压缩的角度阐述了有初始值变量对Rom size的影响。

1.6. 有效利用库函数memset进行初始化
memset这些函数库函数对数组或结构体赋值或初始化,都已经被系统封装了,所以,我们直接调用就可以了,不需要再进行定义,从而节省了很多的ROM。
例如:
#define GERDALDRV_BYTE_CLR 0x00U
#define GERDA_LOOP_MAX 255U
for (aubLpCnt = GERDALDRV_BYTE_CLR; aubLpCnt < GERDA_LOOP_MAX; aubLpCnt++)
{
wubGerdaDrvRecbuf_a[aubLpCnt] = GERDALDRV_BYTE_CLR;
}
上面的循环语句的意图是对数组wubGerdaDrvRecbuf_a清零,那么上面的循环语句完全可以用库函数memset来代替。
修正后:
memset(wubGerdaDrvRecbuf_a,GERDALDRV_BYTE_CLR,sizeof(wubGerdaDrvRecbuf_a))
优化前                                          优化后                                      code减少量
438848bytes                                 438840bytes                                 8bytes

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值