前言

当前,在单片机开发领域,Keil是广泛被采用的开发工具。然而,其编辑器功能相较于现代的VScode显得相当陈旧和不足。过去,我也曾试验过使用platformIO,其核心理念是对SDCCstcgal进行封装,从而提供一个更加集成的开发环境。

现在,我们将基于SDCCstcgal来搭建一个全新的单片机开发环境。我们将现有代码从Keil迁移到SDCC,确保原有的Keil代码能够在新环境中顺畅运行。

本文使用的示例项目地址:uart_temp

正文

一、安装SDCC和stcgal

下载链接

SDCC的特点:

  • 开源免费、支持常见MCU

  • 一次只能编译一个文件,多个文件需要编写Makefile

  • 对c99标准支持的比Keil C51更好,更像一个标准的C语言编译器

  • SDCC代码生成的体积会比较大,原因如下:

    在GCC编译器中,-ffunction-sections-fdata-sections参数是用来将每个函数和数据段分别放在单独的section中。这样做的好处是在链接阶段,未被引用的函数和数据可以被识别并删除,从而减小最终的执行文件大小。而-Wl,--gc-sections参数则是告诉链接器去删除未使用的函数和数据段。

    然而,SDCC(Small Device C Compiler)并不支持这些GCC的特性。这可能导致SDCC编译的单片机程序的最终执行文件比使用GCC编译的更大,因为即使某些代码没有被调用,它也会被包含在执行文件中。为了解决这个问题,有些SDCC库的开发者选择在每个源文件中只放置一个函数,尽管这样做会让代码看起来更加分散和不易读。

stcgal的特点:

  • 开源免费,支持 STC MCU Ltd. 8051 系列单片机
  • 支持命令行操作,stc-isp可以用于烧录单片机,但是软件是闭源的。不支持命令行操作

二、从Keil迁移到SDCC

Keil C51 SDCC
头文件 reg51.h/reg52.h 8051.h/8052.h
端口 P2^0 P2_0
端口定义 sbit LED1=P2^0; #define LED1 P2_0 替换的时候注意删除后面的分号
中断声明 void uart() interrupt 4 using 0 void uart(void) __interrupt (4) __using (0)
特殊类型 sbit,sfr,bit,code __sbit,__sfr,__bit,__code Keil中的很多特殊类型前面加上__就是sdcc中对应的类型。
_nop()_ Keil C51专有 #define _nop_() __asm NOP __endasm
intrins.h Keil C51专有, SDCC需自己创建 自行创建

intrins.h文件内容图下,请自行创建

#ifndef _INTRINS_H_
#define _INTRINS_H_

/* warning: __push __pop 使用堆栈临时保存 sfr 数据,必需成对使用!  
     __push(x);
         ...  // 保护代码块
         __pop(x);  //缺少无该语句编译不会出错,但运行错误!
*/
        #define __push(x)      __asm push _##x  __endasm  /* void _push_ (unsigned char _sfr); */
        #define __pop(x)       __asm pop  _##x  __endasm  /* void _pop_  (unsigned char _sfr); */

        #define _push_        __push        /*兼容 keil c51*/
        #define _pop_        __pop         /*兼容 keil c51*/
        
/*   安全使用保护宏: 
     pushSfr(x);
         ...                 // 受保护代码块
         popSfr(x);         // 缺少无该语句编译出错,确保生成正确代码。
*/
    #define pushSfr(x)  do{\
                                  __push(x)

        #define  popSfr(x)     __pop(x);\
                              }while(0)

#endif //_INTRINS_H_
常见编译问题:
  • 待补充

三、编写Makefile

示例项目地址:uart_temp

项目结构如下:

D:\PROJECT\UART_TEMP
├─include
├─obj
└─src

对应的Makefile如下:

# set CC TOOL
CC := sdcc
PACKIHX := packihx
CFLAGS := -DUSE_FLOATS=1

# set DIR
INCDIR = include
SRCDIR = src
OBJDIR = obj
TARGET = obj/main.ihx

INC := ./include
SRC := $(wildcard $(SRCDIR)/*.c)
# OBJ := $(SRC:%.c=%.rel)
OBJ := $(patsubst src/%.c, obj/%.rel, $(SRC))  

.PHONY: all clean

all: uart_temp.hex

clean:
	-del /q obj\*

$(OBJ):obj/%.rel:src/%.c
	-$(CC) $(CFLAGS) -I $(INC) -c $^ -o $@

$(TARGET): $(OBJ)
	-$(CC) $^ -o $@
	
uart_temp.hex: $(TARGET)
	-$(PACKIHX) $(TARGET) > uart_temp.hex

flash: 
	-stcgal -P stc89 -p COM3 .\uart_temp.hex

#led.bin:led.hex
#       objcopy -I ihex -O binary led.hex led.bin

#led.hex:main.ihx
#       packihx main.ihx > led.hex

# led.bin:main.ihx
#         makebin main.ihx led.bin

需要注意的是:

  • sdcc编译生成.rel不生成.o
  • sdcc链接得到.ihx,需要使用packihx转换成.hex
  • sdcc一次只能编译一个文件

四、编译、烧录

编译命令,编译得到.\uart_temp.hex

make clean all

烧录前,确认单片机连接的串口号,并将Makefile中的COM3为单片机使用的串口号

烧录命令

make flash

参考文献

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐