摸索了几天,把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还是值得信赖和使用的。毕竟,它是开源的,能做到这些,已经非常可贵了。