当前位置: 首页 > 工具软件 > SDCC > 使用案例 >

单片机开发,推荐开源跨平台的SDCC编译器

邢焱
2023-12-01

    摸索了几天,把STC8A单片机的程序转到了SDCC编译器。如果是刚接触单片机,没用过其他编译器,那么不会感觉有啥难度。如果从其他开发工具转过来,可能需要适应一下新环境。

    STC官网给出的头文件和例程基本是基于keil的,这也让许多人选择keil开发环境。keil确实有过人之处,商业软件,比较可信赖。但是keil也有不足:一、它是收费的,免费也可以用,但有代码长度限制。我记得以前keil是免费的,被收购之后就收费了。二、keil只有windows版本,限制了使用的平台。

    SDCC则是开源的,在多个平台可以使用,如linux, 苹果电脑, windows就更不在话下了,并且承诺一直开发更新。

    看了一下STC8的头文件,只是寄存器地址的说明方法在SDCC中略有不同,使用notepad++很容易转换过来。SDCC也有自己的头文件,针对8051的,或者其他单片机的,都有。所以,也可以不用修改STC8的头文件,直接用SDCC提供的就好。

    我使用的是SDCC 4.0 64位版本的。简单说说这个版本的使用方法。

   1,SDCC支持多种单片机。它使用命令行编译,可以使用命令行参数指示编译功能。使用SDCC --help查看各编译参数。

   2,SDCC一个命令行编译一个源文件。多文件源码要使用-c参数,先每个编译为目标文件rel,然后使用SDCC链接多个rel文件。其实这不算用法了,写个Makefile行了,用windows的,就下载cygwin或者mysys带的make,喜欢eclipse的也有个sdcc eclipse插件。还有个8051ide(windows版的我怎么没用对呢,编译不报告还总卡死)。

    一个Makefile的参考例子,使用SDCC编译器,编译链接当前目录的所有c文件,编译结果为main.ihx

下载:https://download.csdn.net/download/lang999888/12727351

CC = sdcc
EXEC = main.ihx
SRC = $(wildcard *.c)
OBJ = $(SRC:%c=%rel)
CCFLAG = -c --stack-auto --no-xinit-opt --model-large
LDFLAG = --stack-auto --no-xinit-opt --model-large
$(EXEC):$(OBJ)
    $(CC) $(LDFLAG) $(OBJ) -o $(EXEC)
%.rel:%.c
    $(CC) $(CCFLAG) $(<) -o $(@)

注:下面2行前面的空格要用TAB符替换
    $(CC) $(LDFLAG) $(OBJ) -o $(EXEC)
    $(CC) $(CCFLAG) $(<) -o $(@)

    3,--stack-auto编译参数。缺省的如果不使用--stack-auto编译参数,SDCC将函数参数和局部变量编译为数据变量,这是考虑到8051的堆栈空间有限,防止堆栈空间不足的处理办法。说实在的8051的8位堆栈指针确实能访问的空间不大。但SDCC这样处理的问题也来了:如果函数的局部变量比较多,它就可能报告“没有***个连续的数据存储器在DSEG”,造成编译失败。自己写的代码,尽量减少函数参数和局部变量。如果从计算机程序移植到8051的,这就不能怪SDCC,只能怪计算机的堆栈空间太大了,用起来太随意了。SDCC是比较保守的编译器,优化处理不大可能做。

    4,使用--stack-auto编译参数。使用--stack-auto编译参数,SDCC真正将函数和局部变量编译进堆栈,数据区也比较清爽了,程序也编译通过了。但是,要小心堆栈溢出了。对于参数比较多的函数,最好将它们从参数表移出来,用全局变量传递。函数局部变量使用static声明为数据变量。这样要占用一些数据空间,可是,堆栈空间不是小吗,也是一种解决办法了。其实,8051的上百个字节的堆栈空间也不算小了,因此推荐使用--stack-auto编译参数。

    5,使用__code声明查表数据。查表数据是不会改变的数据,如果数据空间紧张,将他们声明为__code,数据编译到代码空间,节省了数据空间。一般地,SDCC默认编译RAM数据空间256,XDATA扩展数据空间64KB,程序空间64KB。查看mem文件可以看到具体使用量。查看lst文件可以看到具体分配情况。

    6,使用 --no-xinit-opt。SDCC 4.0这个版本有点怪异,对需要初始化的__xdata缺省不能完成初始化(等同于使用--model-large编译)。SDCC手册说使用了MOV指令做了优化,实际查看编译的lst文件,初始化数据是编译了,但是没有MOV。知道原因了,就不管它为啥没MOV了。使用--no-xinit-opt行了。

    7,printf打印输出到串口。可以使用printf打印到串口,在程序中要提供一个putchar函数,这个函数完成串口输出。对于8051,SDCC给出了这个函数的参考源码:

int putchar (int c) {

while (!TI) /* assumes UART is initialized */ ;

TI = 0;

SBUF = c;

return c;

}

    8,打印浮点数。printf库函数默认不支持打印浮点数。要打印浮点数,需要重新编译printf源码。推荐编译src目录中的printf_large.c,对于8051,使用 -DUSE_FLOATS=1 和 --model-large参数,然后链接它。printf和sprintf函数是很能占用代码空间的库函数啊。

    9,浮点和长型定点运算。SDCC使用一套来自gcc的数学函数库来完成浮点和长型定点运算,运算效率一般,好处是可靠。

    10,烧写STC单片机。我烧写程序使用STC官网的isp软件。文件可以直接使用缺省生成的ihx文件,不必转换成hex。

    11,缺省代码效率。SDCC属于很保守的编译器了,对全局变量分配,代码优化做的不多。这样的好处是可靠性高。所以,不能指望它编译出的程序代码高效,只能寄希望使用的单片机高效了,呵呵。STC8A8K64S12执行128点的FFT运算速度约为25次每秒,相比于keil编译的代码40次每秒的速度,略逊一些。

  12,手工优化。通过简单的手工处理代码:1,常数直接写进代码中;2,使用移位代替常数乘除运算;3,查表数据使用__code编译到代码区。通过这3点简单处理,128点的FFT运算速度提高到了55次每秒,速度提高1倍多。可见,对于编译器,代码编写对了非常重要。

总之,SDCC还是值得信赖和使用的。毕竟,它是开源的,能做到这些,已经非常可贵了。

 类似资料: