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

VS Code + SDCC + stcflash + Makefile 搭建比Keil更优雅的51开发环境

单于亮
2023-12-01

概述

相信很多人对Keil并不陌生,这可能是目前很多初学者接触到的第一个IDE。你点开本文的原因,可能是再也无法忍受Keil一成不变的界面、复古的代码编辑体验;也可能是由于一些需要,必须要摆脱商业软件的环境。

本文将打通编辑、编译、烧录的完整工具链,搭建出一个优雅、开源的51开发环境。

这篇文章主要作用是对关键问题做一个备忘,方便以后自己翻阅,所以写的也比较跳跃。个人水平有限,也没有能力写系统的教程,文中如有错误,欢迎指正!

编辑器:VS Code

编辑器的选择有很多,这里我闭眼选VS Code,原因不做解释。

创建一个新的工作路径,配置如下:

setting.json文件

{
    "files.associations": {
        "*.h": "c",
    },
    "C_Cpp.errorSquiggles": "Disabled",
    "files.autoGuessEncoding": true,
}

tasks.json文件

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "clean",
            "type": "shell",
            "command": "mingw32-make",
            "args": [
                "clean"
            ],
            "problemMatcher": []
        },
        {
            "label": "compile",
            "type": "shell",
            "command": "mingw32-make",
            "args": [
                "target=${fileBasenameNoExtension}"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}

详细的配置过程可以参考其他教程。

编译器:SDCC

编辑器只是一个表层,使用VS Code还是记事本,没有本质区别。要想使我们的工程完全摆脱Keil,最重要的是摆脱Keil C51编译器。这里推荐开源编译器SDCC。

SDCC官方网站

需要在官网安装并添加到path中。过程可以参考:开源SDCC编译器(一)–基本介绍

SDCC直接为我们完成了C语言到单片机可执行文件(hex)的编译过程,支持的所有基于MCS51指令集的单片机,以及STM8、Freescale、HC等部分单片机。这里以STC8为例。

STC8在外设、性能上有了较大的提升,但其本质还是基于MCS51指令集的。所以,我们只需要一个.h文件对寄存器地址进行定义,告诉SDCC我们单片机的寄存器在哪儿即可,其他的事情由SDCC自己完成。

  • 对于基础的89C51单片机,直接#include <at89x52.h>,这是SDCC已经提供给我们的头文件(请注意不要使用Keil中的reg52.h)
  • 对于STC89C52、STC12、STC15、STC8等增(魔)强(改)型51单片机,厂商通常提供有配套的.h文件(STC系列芯片可以直接从stc-isp软件中获得),但是配套的.h文件通常是用于Keil的,需遵循下表规范对.h文件中的关键字进行替换,以使其适用于SDCC编译器

SDCC关键字替换表(参考自SDCC编译器简明使用教程_weixin_50288476的博客

KeilSDCC
寄存器声明sfr P0 = 0x80__sfr __at 0x80 P0
位寄存器声明sbit P00 = P0^0__sbit __at 0x80 P00
位寄存器声明sbit P01 = P0^1__sbit __at 0x81 P01
片外RAM声明xdata__xdata
………………
  • 在反转引脚时,不要使用取反(P00 = ~P00),而应该使用**非(P00 = !P00)**运算;因为 sdcc 会默认进行类型提升,然后进行取反,这样有时会导致一些问题。(参考自Embedded IDE Manual

  • #define LED P00
    LED = 0;

    这种宏定义的方式在SDCC中不支持。可以替换为以下方式:

    #define LED(x) P00=x
    LED(0);

我已经移好了STC8A和STC8H的.h文件,可以点击以下链接下载:
适用于SDCC编译器的STC8H系列单片机头文件

适用于SDCC编译器的STC8A系列单片机头文件

烧录:stcflash

STC官方提供有STC-ISP工具进行程序烧录,但是该软件并非开源的,STC没有公布ISP下载的具体细节。

网上有人通过抓取STC-ISP与单片机的通讯过程,并使用python复现这个过程,实现了向STC系列单片机的程序烧录。stcflash本身只是一个.py文件,我们可以将其放在工程目录下,在VS Code中直接调用python脚本,或者将脚本嵌入到任何我们需要的程序中。

stcflash项目链接

stcflash原生是不支持STC8的,后来有人在此基础上做了改动,使其支持了STC8A、STC8F、STC8H、STC8C等系列的芯片,我本次使用的也是这个版本。点此打开项目连接

在使用之前,如果你的电脑上没有python环境,请自行安装。

除了stcflash外,支持51的开源编译器还有stcgal,但是这个编译器不支持我手上的STC8A和STC8H芯片。

组织:Makefile

上文中,我们已经逐个击破了编辑、编译、烧录三个环节。接下来,我们需要用Makefile将以上三个环节组织起来。

#指定sdcc编译器路径(注:请改为自己的路径)
sdcc  := sdcc\bin\sdcc.exe
packihx := sdcc\bin\packihx.exe

#指定stcflash烧录器路径(注:请改为自己的路径)
stcflash := stcflash/stcflash.py

#指定.c文件(注:请将工程中所有的.c文件添加进来)
SRCS = \
User/main.c \

#指定.h文件(注:请将工程中所有.h文件所在的文件夹添加进来)
INCS = \
-IUser \
-ILibraries \
-IHardware \

#指定输出hex文件的路径与文件名
outdir = Build
outname = output

all: $(outdir)/$(outname).hex download

#将所有的.c->.rel,存入OBJECT
OBJECTS = $(addprefix $(outdir)/,$(notdir $(SRCS:.c=.rel)))
vpath %.c $(sort $(dir $(SRCS)))

#(注:请对照自己的芯片调整以下几条语句的--iram-size、--xram-size参数)
$(outdir)/%.rel: %.c Makefile | $(outdir)
	$(sdcc) --code-size 64000 --iram-size 256 --xram-size 8192 --stack-auto -c $(INCS) $< -o $@

$(outdir)/$(outname).ihx: $(OBJECTS)
	$(sdcc) --code-size 64000 --iram-size 256 --xram-size 8192 --stack-auto $^ -o $(outdir)/$(outname).ihx

$(outdir)/%.hex: $(outdir)/%.ihx | $(outdir)
	$(packihx) $< $@ > $(outdir)/$(outname).hex

#调用stcflash进行烧录(注:请对照自己的设备修改COM口)
download:
	python $(stcflash) Build/output.hex --port COM7 --lowbaud 2400 --highbaud 460800

#定义清除操作
.PHONY : clean
clean :
  del $(outdir)\*.* /q

以后,只需要优雅地敲入"make",Makefile就可以像Keil这种集成开发环境一样,帮你自动完成所有工作。

需要注意的:RAM管理

51单片机的RAM分为片内RAM和片外RAM,片内RAM包括低128字节(data)和高128字节(idata),片外RAM最大64K,其中低256字节单独定义为pdata,其余空间定义为xdata

对于STC12、STC15、STC8等较新的51单片机来说,其内部通常集成了1024~8192字节不等的片外RAM,这足以应对大部分使用场景。但片内RAM的资源依然非常紧张,在编写较大工程时,如果不能妥善分配RAM,将会在编译时看到以下报错:

?ASlink-Error-Could not get 15 consecutive bytes in internal RAM for area DSEG.

该报错意味着片内RAM资源耗尽,你应当仔细检查有无以下问题:

  • 没有利用起来片外RAM,所有变量都塞在了可用空间不到256字节的片内RAM中,当然会不够用啦。继续参考下文,排查是否存在这个问题。

  • 如果确定不存在上一条问题,真的就是片内片外RAM都写满了,检查代码中有无静态的表或变量,既然这些静态数据在运行过程中不需要修改,那就使用__code关键词把他放在ROM中。如果还是不行,考虑换个资源更宽裕些的芯片。

关于编译器的内存管理模式,以下摘取一段SDCC官方手册给出的解释。

3.3.6 MCS51 Options(节取)
–model-small Generate code for Small model programs, see section Memory Models for more details. This is thedefault model.
model-medium Generate code for Medium model programs, see section Memory Models for more details. If this option is used all source files in the project have to be compiled with this option. It must also be used when invoking the linker.
model-large Generate code for Large model programs, see section Memory Models for more details. If this option is used all source files in the project have to be compiled with this option. It must also be used when invoking the linker.
model-huge Generate code for Huge model programs, see section Memory Models for more details. If this option is used all source files in the project have to be compiled with this option. It must also be used when invoking the linker.

3.15.1 MCS51 Memory Models(节取)
3.15.1.1 Small, Medium, Large and Huge
SDCC allows four memory models for MCS51 code, small, medium, large and huge. Modules compiled with different memory models should never be combined together or the results would be unpredictable. The library routines supplied with the compiler are compiled for all models (however, the libraries for –stack-auto are compiled for the small and large models only). The compiled library modules are contained in separate directories as small, medium, large and huge so that you can link to the appropriate set.
When the medium, large or huge model is used all variables declared without specifying an intrinsic named address space will be allocated into the external ram, this includes all parameters and local variables (for non reentrant functions). Medium model uses pdata and large and huge models use xdata. When the small model is used variables without an explicitly specified intrinsic named address space are allocated in the internal ram.
The huge model compiles all functions as banked4.1.3 and is otherwise equal to large for now. All other models compile the functions without bankswitching by default.
Judicious usage of the processor specific intrinsic named address spaces and the ’reentrant’ function type will yield much more efficient code, than using the large model. Several optimizations are disabled when the program is compiled using the large model, it is therefore recommended that the small model be used unless absolutely required.

我个人的理解是:使用medium、large、huge模式会将未指明的数据全部放在片外RAM中(xdata区域),且无法使用编译器的部分优化功能。

SDCC建议使用–model-small(这也是默认的编译模式),然后手动管理和分配RAM空间(通过在代码中使用__xdata、__pdata关键字将体积较大的变量或表放在片外RAM中,不加关键字则默认放在片内RAM中)。

在本工程中,我们也将按照这样的做法。

除此之外,需要通过以下参数,告诉SDCC你手中的芯片的RAM和ROM具体有多大。

xstack Uses a pseudo stack in the __pdata area (usually the first 256 bytes in the external ram) for allocating variables and passing parameters. See section 3.15.1.2 External Stack for more details.
iram-size Causes the linker to check if the internal ram usage is within limits of the given value.
xram-size Causes the linker to check if the external ram usage is within limits of the given value.
code-size Causes the linker to check if the code memory usage is within limits of the given value.
stack-size Causes the linker to check if there is at minimum bytes for stack.

参考代码见makefile章节。

 类似资料: